• TouTiao开源项目 分析笔记13 最后一个订阅号的实现主页面


    1.实现订阅号的基础类

    1.1.本地订阅号的Bean类==>MediaChannelBean

    public class MediaChannelBean implements Parcelable {
    
        public static final Creator<MediaChannelBean> CREATOR = new Creator<MediaChannelBean>() {
            @Override
            public MediaChannelBean createFromParcel(Parcel in) {
                return new MediaChannelBean(in);
            }
    
            @Override
            public MediaChannelBean[] newArray(int size) {
                return new MediaChannelBean[size];
            }
        };
        private String id;
        private String name;
        private String avatar;
        private String type;
        private String followCount;
        private String descText;
        private String url;
    
        public MediaChannelBean() {
        }
    
        protected MediaChannelBean(Parcel in) {
            id = in.readString();
            name = in.readString();
            avatar = in.readString();
            type = in.readString();
            followCount = in.readString();
            descText = in.readString();
            url = in.readString();
        }
    
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeString(id);
            dest.writeString(name);
            dest.writeString(avatar);
            dest.writeString(type);
            dest.writeString(followCount);
            dest.writeString(descText);
            dest.writeString(url);
        }
    
        @Override
        public int describeContents() {
            return 0;
        }
    
        public String getId() {
            return id;
        }
    
        public void setId(String id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getAvatar() {
            return avatar;
        }
    
        public void setAvatar(String avatar) {
            this.avatar = avatar;
        }
    
        public String getType() {
            return type;
        }
    
        public void setType(String type) {
            this.type = type;
        }
    
        public String getFollowCount() {
            return followCount;
        }
    
        public void setFollowCount(String followCount) {
            this.followCount = followCount;
        }
    
        public String getDescText() {
            return descText;
        }
    
        public void setDescText(String descText) {
            this.descText = descText;
        }
    
        public String getUrl() {
            return url;
        }
    
        public void setUrl(String url) {
            this.url = url;
        }
    }

    1.2.数据库建立订阅号的基础表==>MediaChannelTable 

    public class MediaChannelTable {
        /**
         * 头条号信息表
         */
        public static final String TABLENAME = "MediaChannelTable";
    
        /**
         * 字段部分
         */
        public static final String ID = "id";
        public static final String NAME = "name";
        public static final String AVATAR = "avatar";
        public static final String TYPE = "type";
        public static final String FOLLOWCOUNT = "followCount";
        public static final String DESCTEXT = "descText";
        public static final String URL = "url";
    
        /**
         * 字段ID 数据库操作建立字段对应关系 从0开始
         */
        public static final int ID_ID = 0;
        public static final int ID_NAME = 1;
        public static final int ID_AVATAR = 2;
        public static final int ID_TYPE = 3;
        public static final int ID_FOLLOWCOUNT = 4;
        public static final int ID_DESCTEXT = 5;
        public static final int ID_URL = 6;
    
        /**
         * 创建表
         */
        public static final String CREATE_TABLE = "create table if not exists " + TABLENAME + "(" +
                ID + " text primary key, " +
                NAME + " text, " +
                AVATAR + " text, " +
                TYPE + " text, " +
                FOLLOWCOUNT + " text, " +
                DESCTEXT + " text, " +
                URL + " text) ";
    }

    1.3.实现最底层订阅号的数据库操作

      

    public class MediaChannelDao {
        private SQLiteDatabase db;
    
        public MediaChannelDao(){
            this.db= DatabaseHelper.getDatabase();
        }
    
        public void initData(){
            add("4377795668", "新华网", "http://p2.pstatp.com/large/3658/7378365093", "news",
                    "", "传播中国,报道世界;权威声音,亲切表达。", "http://toutiao.com/m4377795668/");
            add("52445544609", "互联网的这点事", "http://p3.pstatp.com/large/ef300164e786ff295da", "news",
                    "", "每天为你速递最新、最鲜、最有料的互联网科技资讯!", "http://toutiao.com/m52445544609/");
        }
    
        public boolean add(String id,
                           String name,
                           String avatar,
                           String type,
                           String followCount,
                           String descText,
                           String url) {
            ContentValues values = new ContentValues();
            values.put(MediaChannelTable.ID, id);
            values.put(MediaChannelTable.NAME, name);
            values.put(MediaChannelTable.AVATAR, avatar);
            values.put(MediaChannelTable.TYPE, type);
            values.put(MediaChannelTable.FOLLOWCOUNT, followCount);
            values.put(MediaChannelTable.DESCTEXT, descText);
            values.put(MediaChannelTable.URL, url);
            long result = db.insert(MediaChannelTable.TABLENAME, null, values);
            return result != -1;
        }
    
        public List<MediaChannelBean> queryAll() {
            Cursor cursor = db.query(MediaChannelTable.TABLENAME, null, null, null, null, null, null);
            List<MediaChannelBean> list = new ArrayList<>();
            while (cursor.moveToNext()) {
                MediaChannelBean bean = new MediaChannelBean();
                bean.setId(cursor.getString(MediaChannelTable.ID_ID));
                bean.setName(cursor.getString(MediaChannelTable.ID_NAME));
                bean.setAvatar(cursor.getString(MediaChannelTable.ID_AVATAR));
                bean.setType(cursor.getString(MediaChannelTable.ID_TYPE));
                bean.setFollowCount(cursor.getString(MediaChannelTable.ID_FOLLOWCOUNT));
                bean.setDescText(cursor.getString(MediaChannelTable.ID_DESCTEXT));
                bean.setUrl(cursor.getString(MediaChannelTable.ID_URL));
                list.add(bean);
            }
            cursor.close();
            return list;
        }
    
        public boolean queryIsExist(String id) {
            Cursor cursor = db.query(MediaChannelTable.TABLENAME, null, MediaChannelTable.ID + "=?", new String[]{id}, null, null, null);
            if (cursor.moveToNext()) {
                cursor.close();
                return true;
            }
            cursor.close();
            return false;
        }
    
        public boolean delete(String mediaId) {
            int id = db.delete(MediaChannelTable.TABLENAME, MediaChannelTable.ID + "=?", new String[]{mediaId});
            return id != -1;
        }
    }

      


    2.构建订阅号视图页面

    2.1.创建订阅号视图布局==>fragment_media.xml 

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/windowBackground"
        android:orientation="vertical"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">
    
        <android.support.v4.widget.SwipeRefreshLayout
            android:id="@+id/refresh_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
            <android.support.design.widget.CoordinatorLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent">
    
                <TextView
                    android:id="@+id/tv_desc"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center"
                    android:text="@string/media_hint_desc"/>
    
                <android.support.v7.widget.RecyclerView
                    android:id="@+id/recycler_view"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:fadeScrollbars="true"
                    android:scrollbarFadeDuration="1"
                    android:scrollbars="vertical"/>
    
            </android.support.design.widget.CoordinatorLayout>
    
        </android.support.v4.widget.SwipeRefreshLayout>
    
    </LinearLayout>

      预览图片:

      

    2.2.定义一个长按监听事件==>长按弹出提示框 

    public interface IOnItemLongClickListener {
    
        /**
         * RecyclerView Item长按事件
         */
        void onLongClick(View view, int position);
    }

    2.3.构建一个订阅号视图类 

    public class MediaChannelView extends RxFragment implements SwipeRefreshLayout.OnRefreshListener {
    
        private static final String TAG = "MediaChannelView";
        private static MediaChannelView instance = null;
        private RecyclerView recyclerView;
        private SwipeRefreshLayout swipeRefreshLayout;
        private MultiTypeAdapter adapter;
        private MediaChannelDao dao = new MediaChannelDao();
        private TextView tv_desc;
        private String isFirstTime = "isFirstTime";
        private List<MediaChannelBean> list;
    
        public static MediaChannelView getInstance() {
            if (instance == null) {
                instance = new MediaChannelView();
            }
            return instance;
        }
    
        @Nullable
        @Override
        public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.fragment_media, container, false);
            initView(view);
            initData();
            return view;
        }
    
        @Override
        public void onResume() {
            super.onResume();
            swipeRefreshLayout.setColorSchemeColors(SettingUtil.getInstance().getColor());
            setAdapter();
        }
    
        private void initData() {
            SharedPreferences editor = getActivity().getSharedPreferences(TAG, Context.MODE_PRIVATE);
            boolean result = editor.getBoolean(isFirstTime, true);
            if (result) {
                dao.initData();
                editor.edit().putBoolean(isFirstTime, false).apply();
            }
            setAdapter();
        }
    
        private void setAdapter() {
            Observable
                    .create(new ObservableOnSubscribe<List<MediaChannelBean>>() {
                        @Override
                        public void subscribe(@NonNull ObservableEmitter<List<MediaChannelBean>> e) throws Exception {
                            list = dao.queryAll();
                            e.onNext(list);
                        }
                    })
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .compose(this.<List<MediaChannelBean>>bindUntilEvent(FragmentEvent.DESTROY))
                    .subscribe(new Consumer<List<MediaChannelBean>>() {
                        @Override
                        public void accept(@NonNull List<MediaChannelBean> list) throws Exception {
                            adapter.setItems(list);
                            adapter.notifyDataSetChanged();
                            if (list.size() == 0) {
                                tv_desc.setVisibility(View.VISIBLE);
                            } else {
                                tv_desc.setVisibility(View.GONE);
                            }
                        }
                    });
        }
    
        private void initView(View view) {
            recyclerView = view.findViewById(recycler_view);
            recyclerView.setHasFixedSize(true);
            recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
    
            swipeRefreshLayout = view.findViewById(R.id.refresh_layout);
            swipeRefreshLayout.setColorSchemeColors(SettingUtil.getInstance().getColor());
            swipeRefreshLayout.setOnRefreshListener(this);
            tv_desc = view.findViewById(R.id.tv_desc);
    
            IOnItemLongClickListener listener = new IOnItemLongClickListener() {
                @Override
                public void onLongClick(View view, int position) {
                    final MediaChannelBean item = list.get(position);
                    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
                    builder.setMessage("取消订阅" " + item.getName() + " "?");
                    builder.setPositiveButton(R.string.button_enter, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            new Thread(new Runnable() {
                                @Override
                                public void run() {
                                    dao.delete(item.getId());
                                    setAdapter();
                                }
                            }).start();
                            dialog.dismiss();
                        }
                    });
                    builder.setNegativeButton(R.string.button_cancel, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            dialog.dismiss();
                        }
                    });
                    builder.show();
                }
            };
            adapter = new MultiTypeAdapter();
            Register.registerMediaChannelItem(adapter, listener);
            recyclerView.setAdapter(adapter);
        }
    
        @Override
        public void onRefresh() {
            swipeRefreshLayout.setRefreshing(true);
            setAdapter();
            swipeRefreshLayout.setRefreshing(false);
        }
    
        @Override
        public void onDestroyView() {
            super.onDestroyView();
            if (instance != null) {
                instance = null;
            }
        }
    }

       注意:如果setAdapter简化成下面这个函数,直观效果一直。 

    private void setAdapter(){
            list=dao.queryAll();
            adapter.setItems(list);
            adapter.notifyDataSetChanged();
            if (list.size() == 0) {
                tv_desc.setVisibility(View.VISIBLE);
            } else {
                tv_desc.setVisibility(View.GONE);
            }
        }

      但是,如果订阅号的数量很多很多后,这种效果远远不如订阅的方法性能好。

      所以我们统一就用订阅的方式吧。

      而且更加重要的是,如果没有数据的时候,

      这里要进行界面操作,如果直接在io线程会发生异常的。

    2.4.发现Register中还没有注册类型以及传入监听求

     public static void registerMediaChannelItem(@NonNull MultiTypeAdapter adapter, @NonNull IOnItemLongClickListener listener) {
            adapter.register(MediaChannelBean.class, new MediaChannelViewBinder(listener));
        }

      这里发现了监听器传进去了,说明绑定类中要给每一行都要设置这个listener

      然后这里又看到MediaChannelViewBinder绑定类还没有实现呢!

      第三点主要讲解这个简单的绑定类。


    3.订阅号视图绑定类

    3.1.首先看一下视图布局吧。==>item_media_channel.xml 

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/viewBackground">
    
        <LinearLayout
            android:id="@+id/content"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="?attr/selectableItemBackground"
            android:foreground="?attr/selectableItemBackground"
            android:padding="8dp">
    
            <com.meiji.toutiao.widget.CircleImageView
                android:id="@+id/cv_avatar"
                android:layout_width="52dp"
                android:layout_height="52dp"
                android:layout_gravity="center"
                android:scaleType="centerCrop"
                android:src="@color/textColorPrimary"/>
    
            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:layout_marginLeft="8dp"
                android:layout_marginStart="8dp">
    
                <TextView
                    android:id="@+id/tv_mediaName"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_alignParentLeft="true"
                    android:layout_alignParentStart="true"
                    android:layout_toLeftOf="@+id/tv_followCount"
                    android:layout_toStartOf="@+id/tv_followCount"
                    android:maxLines="1"
                    android:textSize="16sp"
                    android:textStyle="bold"
                    tools:text="新华国际"/>
    
                <TextView
                    android:id="@+id/tv_followCount"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_alignParentEnd="true"
                    android:layout_alignParentRight="true"
                    android:text=""
                    tools:text="111人关注"/>
    
                <TextView
                    android:id="@+id/tv_descText"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_below="@+id/tv_mediaName"
                    android:layout_marginTop="4dp"
                    android:ellipsize="end"
                    android:maxLines="1"
                    android:textSize="14sp"
                    tools:text="中国军力超越日本 日本为什么不怕中国?普京一句话让国人顿悟"/>
            </RelativeLayout>
    
        </LinearLayout>
    
        <View
            android:id="@+id/divider"
            android:layout_width="match_parent"
            android:layout_height="1px"
            android:layout_below="@+id/content"
            android:background="@color/line_divider"/>
    
    </RelativeLayout>

      视图效果预览:

      

    3.2.然后就是这个绑定类了==>MediaChannelViewBinder 

    package com.jasonjan.headnews.binder.media;
    
    import android.content.Context;
    import android.support.annotation.NonNull;
    import android.support.v7.widget.RecyclerView;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.TextView;
    
    import com.jakewharton.rxbinding2.view.RxView;
    import com.jasonjan.headnews.R;
    import com.jasonjan.headnews.bean.media.MediaChannelBean;
    import com.jasonjan.headnews.interfaces.IOnItemLongClickListener;
    import com.jasonjan.headnews.main.ErrorAction;
    import com.jasonjan.headnews.util.ImageLoader;
    import com.jasonjan.headnews.widget.CircleImageView;
    
    import java.util.concurrent.TimeUnit;
    
    import io.reactivex.functions.Consumer;
    import me.drakeet.multitype.ItemViewBinder;
    
    /**
     * Created by JasonJan on 2017/12/14.
     */
    
    public class MediaChannelViewBinder extends ItemViewBinder<MediaChannelBean,MediaChannelViewBinder.ViewHolder> {
    
        private IOnItemLongClickListener listener;
    
        public MediaChannelViewBinder(IOnItemLongClickListener listener) {
            this.listener = listener;
        }
    
        @NonNull
        @Override
        protected MediaChannelViewBinder.ViewHolder onCreateViewHolder(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent) {
            View view = inflater.inflate(R.layout.item_media_channel, parent, false);
            return new ViewHolder(view, listener);
        }
    
        @Override
        protected void onBindViewHolder(@NonNull final ViewHolder holder, @NonNull final MediaChannelBean item){
            try {
                final Context context = holder.itemView.getContext();
                String url = item.getAvatar();
                ImageLoader.loadCenterCrop(context, url, holder.cv_avatar, R.color.viewBackground);
                holder.tv_mediaName.setText(item.getName());
                holder.tv_descText.setText(item.getDescText());
    
                RxView.clicks(holder.itemView)
                        .throttleFirst(1, TimeUnit.SECONDS)
                        .subscribe(new Consumer<Object>() {
                            @Override
                            public void accept(@io.reactivex.annotations.NonNull Object o) throws Exception {
                               // MediaHomeActivity.launch(item.getId());
                            }
                        });
            } catch (Exception e) {
                ErrorAction.print(e);
            }
        }
    
        public class ViewHolder extends RecyclerView.ViewHolder implements View.OnLongClickListener {
    
            private CircleImageView cv_avatar;
            private TextView tv_mediaName;
            private TextView tv_followCount;
            private TextView tv_descText;
            private IOnItemLongClickListener listener;
    
            public ViewHolder(View itemView, IOnItemLongClickListener listener) {
                super(itemView);
                this.cv_avatar = itemView.findViewById(R.id.cv_avatar);
                this.tv_mediaName = itemView.findViewById(R.id.tv_mediaName);
                this.tv_followCount = itemView.findViewById(R.id.tv_followCount);
                this.tv_descText = itemView.findViewById(R.id.tv_descText);
                this.listener = listener;
                itemView.setOnLongClickListener(this);
            }
    
            @Override
            public boolean onLongClick(View v) {
                if (listener != null) {
                    listener.onLongClick(v, getLayoutPosition());
                    return true;
                }
                return false;
            }
        }
    }

      这里先理一理RxView.clicks思路。

      这个ViewHolder继承于RecyclerView.ViewHolder

      绑定类由于继承ItemViewBinder,不得不去实现onBindViewHolder<T,这里面的ViewHolder>

      所以在这里面处理ViewHolder的holdr.itemView的时候

      要用到RxView.clicks(view)

      如下方的代码:

    RxView.clicks(holder.itemView)
                        .throttleFirst(1, TimeUnit.SECONDS)
                        .subscribe(new Consumer<Object>() {
                            @Override
                            public void accept(@io.reactivex.annotations.NonNull Object o) throws Exception {
                                MediaHomeActivity.launch(item.getId());
                            }
                        });

      RxView代表着用了第三方库,结合了RxJava。

      采用订阅的方式处理点击事件。

      当然也可以不用这种方式,不过我还没发现这种方式的好处。

      这里的ThrottleFirst操作符会定期发射这个时间段里源Observable发射的第一个数据。

      参考博客:RxJava操作符(三)


    4.效果预览

    4.1.目前完成的工作

      新闻的主页面三种大类型

      图片的一种大类型(也只用了一种)

      视频的一种大类型(采用了新闻主页面的其中一种)

      订阅号的主页面的一种大类型(也只采用了一种)

      然后还有一些点击事件,调转到相应的活动页面还未实现。

    4.2.目前手机真实数据效果

      



    既然选择了,便不顾风雨兼程。Just follow yourself.
  • 相关阅读:
    卡特兰数
    hdu 1023 Train Problem II
    hdu 1022 Train Problem
    hdu 1021 Fibonacci Again 找规律
    java大数模板
    gcd
    object dection资源
    Rich feature hierarchies for accurate object detection and semantic segmentation(RCNN)
    softmax sigmoid
    凸优化
  • 原文地址:https://www.cnblogs.com/Jason-Jan/p/8039075.html
Copyright © 2020-2023  润新知