zoukankan      html  css  js  c++  java
  • 仿各种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 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

  • 相关阅读:
    (转)java反射机制及简单工厂模式
    (转)JAVA反射机制理解
    (转)前缀、中缀、后缀表达式
    (转)java提高篇(四)-----理解java的三大特性之多态
    (转)java for循环的执行顺序和几种常用写法
    (转)JAVA堆栈操作
    POI 实现合并单元格以及列自适应宽度
    前端缓存支持的文件格式及请求方式
    freemarker在xml文件中遍历list数据
    freemarker在线编辑
  • 原文地址:https://www.cnblogs.com/matoo/p/5530758.html
Copyright © 2011-2022 走看看