仿各种APP将文章DOM转JSON并在APP中以列表显示(android、ios、php已开源)
背景
一直以来都想实现类似新闻客户端、鲜城等文章型app的正文显示,即在web editor下编辑后存为json,在app中解析json并显示正文。
网上搜过,没找到轮子。都是给的思路,然后告知是公司项目不好分享代码,所以干脆就自己做。
例子给的ui很粗,以实现功能为目的,你要有兴趣可以star等我更新。



输出的效果看起来是如上图所示。包括web的编辑器、ios、android。没做ui美化。
原理
web端
只是为了验证功能,所以信息包括标题、内容、其他数据都是模拟的,输出的json格式看起来是这样的:
[
{
"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规则做好。
规则一句话:umeditor每个自然段以<p>标签包围,遍历<p>标签,找出文字和图片存为json即可。
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端
android端基于RecyclerView,主要考虑的地方是在adapter中需要根据type来输出不同的view,我用之前独白故事项目的思路(是基于以前看过一篇文章大神的思路),把不同type类型封装为“render”,然后根据数据的type来确定显示什么view。
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,加了通用的点击事件
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重写了。
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。
ios端
其他功能如网络访问、json转换不再讨论,都是很常见的。在ios下实现其实简单,只需要根据type返回不同的cell即可,但需要注意的是高度,我图省事用的固定高度,这一块会在后续的更新中修正。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
HJSectionModel* section=(HJSectionModel*)self.data[indexPath.row];
switch (section.type) {
case :{
HJTextTableViewCell* cell=[tableView dequeueReusableCellWithIdentifier:textIdentifier forIndexPath:indexPath];
cell.text.text=section.object;
return cell;
}
case :{
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
仿各种APP将文章DOM转JSON并在APP中以列表显示(android、ios、php已开源)的更多相关文章
- 三、Uniapp+vue+腾讯IM+腾讯音视频开发仿微信的IM聊天APP,支持各类消息收发,音视频通话,附vue实现源码(已开源)-配置项目并实现IM登录
项目文章索引 1.项目引言 2.腾讯云后台配置TXIM 3.配置项目并实现IM登录 4.会话好友列表的实现 5.聊天输入框的实现 6.聊天界面容器的实现 7.聊天消息项的实现 8.聊天输入框扩展面板的 ...
- 七、Uniapp+vue+腾讯IM+腾讯音视频开发仿微信的IM聊天APP,支持各类消息收发,音视频通话,附vue实现源码(已开源)-聊天消息项的实现
会话好友列表的实现 1.项目引言 2.腾讯云后台配置TXIM 3.配置项目并实现IM登录 4.会话好友列表的实现 5.聊天输入框的实现 6.聊天界面容器的实现 7.聊天消息项的实现 8.聊天输入框扩展 ...
- 八、Uniapp+vue+腾讯IM+腾讯音视频开发仿微信的IM聊天APP,支持各类消息收发,音视频通话,附vue实现源码(已开源)-聊天输入框扩展面板的实现
聊天输入框扩展面板的实现 1.项目引言 2.腾讯云后台配置TXIM 3.配置项目并实现IM登录 4.会话好友列表的实现 5.聊天输入框的实现 6.聊天界面容器的实现 7.聊天消息项的实现 8.聊天输入 ...
- 五、Uniapp+vue+腾讯IM+腾讯音视频开发仿微信的IM聊天APP,支持各类消息收发,音视频通话,附vue实现源码(已开源)-聊天输入框的实现
会话好友列表的实现 1.项目引言 2.腾讯云后台配置TXIM 3.配置项目并实现IM登录 4.会话好友列表的实现 5.聊天输入框的实现 6.聊天界面容器的实现 7.聊天消息项的实现 8.聊天输入框扩展 ...
- 一、Uniapp+vue+腾讯IM+腾讯音视频开发仿微信的IM聊天APP,支持各类消息收发,音视频通话,附vue实现源码(已开源)-项目引言
项目文章索引 1.项目引言 2.腾讯云后台配置TXIM 3.配置项目并实现IM登录 4.会话好友列表的实现 5.聊天输入框的实现 6.聊天界面容器的实现 7.聊天消息项的实现 8.聊天输入框扩展面板的 ...
- 二、Uniapp+vue+腾讯IM+腾讯音视频开发仿微信的IM聊天APP,支持各类消息收发,音视频通话,附vue实现源码(已开源)-腾讯云后台配置TXIM
项目文章索引 1.项目引言 2.腾讯云后台配置TXIM 3.配置项目并实现IM登录 4.会话好友列表的实现 5.聊天输入框的实现 6.聊天界面容器的实现 7.聊天消息项的实现 8.聊天输入框扩展面板的 ...
- 四、Uniapp+vue+腾讯IM+腾讯音视频开发仿微信的IM聊天APP,支持各类消息收发,音视频通话,附vue实现源码(已开源)-会话好友列表的实现
会话好友列表的实现 1.项目引言 2.腾讯云后台配置TXIM 3.配置项目并实现IM登录 4.会话好友列表的实现 5.聊天输入框的实现 6.聊天界面容器的实现 7.聊天消息项的实现 8.聊天输入框扩展 ...
- 使用 jQuery Mobile 与 HTML5 开发 Web App 系列文章目录
使用 jQuery Mobile 与 HTML5 开发 Web App 系列文章目录 时间:2012年9月20日 分类:JavaScript 标签:HTML5‚ jQuery Mobile‚ Web ...
- iOS App 架构文章推荐
iOS应用开发架构 iOS应用架构谈系列 阿里技术沙龙 2.2.1. Hybrid App 2.2.2. taobao 客户端架构 2.2.3. alipay 客户端架构 iOS APP 架构漫谈 ...
随机推荐
- JQuery插件:遮罩+数据加载中。。。(特点:遮你想遮,罩你想罩)
在很多项目中都会涉及到数据加载.数据加载有时可能会是2-3秒,为了给一个友好的提示,一般都会给一个[数据加载中...]的提示.今天就做了一个这样的提示框. 先去jQuery官网看看怎么写jQuery插 ...
- javascript-适配器模式
适配器模式笔记 将一个类(对象)的接口(方法或属性)转化成另一个接口,以满足用户需求,使类(对象)之间接口的不兼容性问题通过适配器方法得以解决 demo实例:1.适配参数对象,使传入的参数完整 2.适 ...
- PowerBI 引入时间智能
简介 Power BI Desktop -是一款由微软发布的自助式商业智能工具,功能强大.易于使用.其中还可以通过微软云连多个数据源并且使用数据源来创建可视化表盘. 但是几乎所有的BI都需要展示如何随 ...
- jenkins邮件通知功能
第部分:全局设置 第一步:进入jenkins的系统设置 第二步:设置管理员邮件地址: 第三步:下载email-ext插件并填写对应的内容: 第四部:填写邮件通知 第五步:以上就是系统管理里需要填写的全 ...
- MicroCube 风力发电装置
这个叫做 MicroCube 的发电装置其实是一套「小型风扇+发电机」的组合,能够输出三相交流电,之后转换成直流电给电池组充电. 一个 MicroCube 长宽高均为 23 厘米左右,重约 1.4 公 ...
- Can't load AMD 64-bit .dll on a IA 32-bit platform
主要谈谈在win8.1(64bit)下搭建环境的经历. 安装win8.1(64bit)后,配置java环境是费了我一番心思的,所以想记录下来,成为经验.64位系统下比较理想的配置应该是 64位jdk ...
- .Net程序员之Python基础教程学习----字典的使用 [Third Day]
今天学习了字典的使用, 所谓的字典其实就是键值对数据, 一个字典里面有唯一的Key对应一个value,Key是唯一的,Value不唯一. 在.net添加相同的Key会报错,在Python,若出现相 ...
- Java Web之请求和响应
Servlet最主要作用就是处理客户端请求并作出回应,为此,针对每次请求,Web容器在调用service()之前都会创建两个对象,分别是HttpServletRequest和HttpServletRe ...
- hdu 5894 hannnnah_j’s Biological Test 组合数学
传送门:hdu 5894 hannnnah_j’s Biological Test 题目大意:n个座位,m个学生,使每个学生的间隔至少为k个座位 组合中的插空法 思路:每个学生先去掉k个空位间隔,剩下 ...
- (五)适配器模式-C++实现
将一个类的接口转换成客户希望的另外一个接口.Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作. 该模式中有三种角色: 1.目标:是一个抽象类,它是客户想使用的接口 2.被适配 ...