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

  • 相关阅读:
    HDU1029 Ignatius and the Princess IV
    UVA11039 Building designing【排序】
    UVA11039 Building designing【排序】
    POJ3278 HDU2717 Catch That Cow
    POJ3278 HDU2717 Catch That Cow
    POJ1338 Ugly Numbers(解法二)
    POJ1338 Ugly Numbers(解法二)
    UVA532 Dungeon Master
    UVA532 Dungeon Master
    POJ1915 Knight Moves
  • 原文地址:https://www.cnblogs.com/matoo/p/5530758.html
Copyright © 2011-2022 走看看