一直以來都想實現相似新聞客戶端、鮮城等文章型app的正文顯示,即在web editor下編輯後存爲json,在app中解析json並顯示正文。java
網上搜過,沒找到輪子。都是給的思路,而後告知是公司項目很差分享代碼,因此乾脆就本身作。node
例子給的ui很粗,以實現功能爲目的,你要有興趣能夠star等我更新。react
輸出的效果看起來是如上圖所示。包括web的編輯器、ios、android。沒作ui美化。android
只是爲了驗證功能,因此信息包括標題、內容、其餘數據都是模擬的,輸出的json格式看起來是這樣的:ios
[
{
"id": 2,
"title": "fdfd",
"text": "[{\"object\":\"text1\",\"type\":2},{\"object\":\"http:\\/\\/gitwiduu.u.qiniudn.com\\/lanwen_14637283563254.jpg\",\"type\":3}]",
"author": "做者名稱",
"created_at": "2016-05-20 15:12:38",
"updated_at": "2016-05-20 15:12:38"
},
{
"id": 3,
"title": "adfadf",
"text": "[{\"object\":\"text1adsfdasf\",\"type\":2}]",
"author": "做者名稱",
"created_at": "2016-05-20 15:22:49",
"updated_at": "2016-05-20 15:22:49"
},
{
"id": 4,
"title": "adfadf",
"text": "[{\"object\":\"text1\",\"type\":2}]",
"author": "做者名稱",
"created_at": "2016-05-20 15:23:07",
"updated_at": "2016-05-20 15:23:07"
}
]
web端基於laravel4.2開發,採用的umeditor和七牛雲服務器上傳圖片(這部分已剝離單獨開源),。我仔細研究了一下,可能須要更進一步自定義umeditor,把圖片parse規則作好。laravel
規則一句話:umeditor每一個天然段以<p>標籤包圍,遍歷<p>標籤,找出文字和圖片存爲json便可。git
public function getDom($text) { $dom = new DOMDocument(); $dom->loadHTML($text); $node = $dom->documentElement; $tempArray = false; //遍歷全部節點 $childs = $node->getElementsByTagName('p'); foreach ($childs as $child) { //文字 if ($child->nodeValue) { $tempArray[] = ['object' => $child->nodeValue, 'type' => 2]; } //圖片 $imgs = $child->getElementsByTagName('img'); if ($imgs) { foreach ($imgs as $img) { $tempArray[] = ['object' => $img->attributes->getNamedItem('src')->nodeValue, 'type' => 3]; } } } return json_encode($tempArray); }
android端基於RecyclerView,主要考慮的地方是在adapter中須要根據type來輸出不一樣的view,我用以前獨白故事項目的思路(是基於之前看過一篇文章大神的思路),把不一樣type類型封裝爲「render」,而後根據數據的type來肯定顯示什麼view。github
package com.huijimuhe.lanwen.adapter.base; import android.annotation.TargetApi; import android.support.v7.widget.RecyclerView; import android.view.View; import android.view.ViewGroup; import java.util.ArrayList; import java.util.List; public abstract class AbstractRenderAdapter<T> extends RecyclerView.Adapter<AbstractViewHolder> { public static final int BTN_CLICK_ITEM = 0; public ArrayList<T> mDataset; public onItemClickListener mOnItemClickListener; protected View mHeaderView; @TargetApi(4) public AbstractViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { return null; } @Override public int getItemCount() { return mHeaderView == null ? mDataset.size() : mDataset.size() + 1; } public List<T> getList() { return this.mDataset; } public int getRealPosition(int position) { return mHeaderView == null ? position : position - 1; } public T getItem(int position) { return mDataset.get(getRealPosition(position)); } public void setOnItemClickListener(onItemClickListener l) { mOnItemClickListener = l; } public interface onItemClickListener { void onItemClick(View view, int postion, int type); } public void setHeaderView(View view) { mHeaderView = view; } public View getHeaderView() { return mHeaderView; } }
上面的代碼是基礎baseadapter,加了通用的點擊事件web
package com.huijimuhe.lanwen.adapter; import android.annotation.TargetApi; import android.view.ViewGroup; import com.huijimuhe.lanwen.adapter.base.AbstractRender; import com.huijimuhe.lanwen.adapter.base.AbstractRenderAdapter; import com.huijimuhe.lanwen.adapter.base.AbstractViewHolder; import com.huijimuhe.lanwen.adapter.render.ImageSectionRender; import com.huijimuhe.lanwen.adapter.render.TextSectionRender; import com.huijimuhe.lanwen.model.SectionBean; import java.util.ArrayList; public class SectionAdapter extends AbstractRenderAdapter<SectionBean> { public static final int RENDER_TYPE_TEXT = 0; public static final int RENDER_TYPE_IMAGE = 1; public static final int BTN_CLICK_ITEM = 101; public static final int BTN_CLICK_IMAGE = 102; public SectionAdapter(ArrayList<SectionBean> statues) { this.mDataset = statues; } @TargetApi(4) public AbstractViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { //header view 的判斷 AbstractViewHolder holder = super.onCreateViewHolder(viewGroup, viewType); if (holder != null) { return holder; } switch (viewType) { case RENDER_TYPE_TEXT: { TextSectionRender head = new TextSectionRender(viewGroup, this); AbstractViewHolder headHolder=head.getReusableComponent(); headHolder.itemView.setTag(android.support.design.R.id.list_item,head); return headHolder; } case RENDER_TYPE_IMAGE: { ImageSectionRender head = new ImageSectionRender(viewGroup, this); AbstractViewHolder headHolder=head.getReusableComponent(); headHolder.itemView.setTag(android.support.design.R.id.list_item,head); return headHolder; } default: return null; } } @TargetApi(4) public void onBindViewHolder(AbstractViewHolder holder, int position) { AbstractRender render = (AbstractRender) holder.itemView.getTag(android.support.design.R.id.list_item); render.bindData(position); } @Override public int getItemViewType(int position) { int type = getItem(position).getType(); switch (type) { case 2: return RENDER_TYPE_TEXT; case 3: return RENDER_TYPE_IMAGE; default: return 0; } } }
這是文章的adapter,能夠看到getItemViewType重寫了。json
package com.huijimuhe.lanwen.adapter.render; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import com.huijimuhe.lanwen.R; import com.huijimuhe.lanwen.adapter.SectionAdapter; import com.huijimuhe.lanwen.adapter.base.AbstractRender; import com.huijimuhe.lanwen.adapter.base.AbstractRenderAdapter; import com.huijimuhe.lanwen.adapter.base.AbstractViewHolder; import com.huijimuhe.lanwen.model.SectionBean; public class TextSectionRender extends AbstractRender { private ViewHolder mHolder; private AbstractRenderAdapter mAdapter; public TextSectionRender(ViewGroup parent, AbstractRenderAdapter adapter) { this.mAdapter =adapter; View v = LayoutInflater.from(parent.getContext()) .inflate(R.layout.listitem_text,parent,false); this.mHolder=new ViewHolder(v,adapter); } @Override public void bindData(int position) { SectionBean model=(SectionBean) mAdapter.getItem(position); mHolder.mTvContent.setText(model.getObject()); } @Override public AbstractViewHolder getReusableComponent() { return this.mHolder; } public class ViewHolder extends AbstractViewHolder{ public TextView mTvContent; public ViewHolder(View v,final AbstractRenderAdapter adapter) { super(v); mTvContent = (TextView) v.findViewById(R.id.item_text); v.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { adapter.mOnItemClickListener.onItemClick(v, getLayoutPosition(), SectionAdapter.BTN_CLICK_ITEM); } }); } } }
這是文字段落的render。
其餘功能如網絡訪問、json轉換再也不討論,都是很常見的。在ios下實現其實簡單,只須要根據type返回不一樣的cell便可,但須要注意的是高度,我圖省事用的固定高度,這一塊會在後續的更新中修正。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { HJSectionModel* section=(HJSectionModel*)self.data[indexPath.row]; switch (section.type) { case 2:{ HJTextTableViewCell* cell=[tableView dequeueReusableCellWithIdentifier:textIdentifier forIndexPath:indexPath]; cell.text.text=section.object; return cell; } case 3:{ HJImageTableViewCell* cell=[tableView dequeueReusableCellWithIdentifier:imageIdentifier forIndexPath:indexPath]; [cell.image sd_setImageWithURL:section.object]; return cell; } default: return nil; } }
【WEB】https://github.com/huijimuhe/dom2json-web
【ANDROID】https://github.com/huijimuhe/dom2json-android
【IOS】https://github.com/huijimuhe/dom2json-ios
這樣作和webview相比有幾個好處:
1.讀得快
2.你能夠加點自定義控件進去,並且排版不彆扭
3.排版能夠更好的定製
若是你用react.js之類的h5,請無視這篇文章。其實我也想用,哈哈哈...
作完以後總結起來,下一步要改進的地方:
1.不能像鮮城同樣幾個圖片存入一個數組
2.能夠作個像投票和圖片輪播的控件,這樣才能顯示出優點
3.android的adapter是還能夠繼續改進的
4.ios的架構能夠寫的更易讀
P.S
來App獨立開發羣533838427
github:https://github.com/huijimuhe