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.目前手机真实数据效果

  

TouTiao开源项目 分析笔记13 最后一个订阅号的实现主页面的更多相关文章

  1. TouTiao开源项目 分析笔记12 从总体到局部 构建视频主页面

    1.构建视频主列表的整体碎片VideoTabLayout 1.1.首先创建一个VideoTabLayout package com.jasonjan.headnews.module.video; im ...

  2. TouTiao开源项目 分析笔记9 实现一个问答主页面

    1.根据API返回创建几个基础的Bean 1.1.WendaArticleDataBean类 API返回的数据如下: /** * cell_type : 36 * extra : {"wen ...

  3. TouTiao开源项目 分析笔记4==>一个简单APP 整体常用框架

    1.效果预览 1.1.如下图所以,到目前为止所有的功能. 2.从InitApp开始->SplashActivity->MainActivity 2.1.InitApp源代码.这是整个项目的 ...

  4. TouTiao开源项目 分析笔记2

    1.Constant常量定义类 1.1.源代码 public class Constant { public static final String USER_AGENT_MOBILE = " ...

  5. TouTiao开源项目 分析笔记6

    1.NewsChannelBean简单类笔记 1.1.Comparable接口的实现和使用 参考文章:Comparable接口的实现和使用. 因为NewsChannelBean实现了Comparabl ...

  6. TouTiao开源项目 分析笔记10 实现通用普通文章片段页面

    1.RxJava的Observable数据操作符总结 1.1.Map操作符 Map操作符对原始Observable发射的没一项数据应用一个你选择的函数, 然后返回一个发射这些结果的Observable ...

  7. TouTiao开源项目 分析笔记17 新闻媒体专栏

    1.效果预览 1.1.要实现的效果 1.2.如何调转到新闻媒体专栏 点击右上角的用户图标. 在新闻详情页面的Fragment的菜单点击事件中触发. case R.id.action_open_medi ...

  8. TouTiao开源项目 分析笔记15 新闻详情之两种类型的实现

    1.预览效果 1.1.首先看一下需要实现的效果. 第一种,文字类型新闻. 第二种,图片类型新闻. 1.2.在NewsArticleTextViewBinder中设置了点击事件 RxView.click ...

  9. TouTiao开源项目 分析笔记11 以总体到局部的思路 构建图片主列表

    1.构建图片主列表的整体片段PhotoTabLayout 1.1.首先创建一个PhotoTabLayout片段 public class PhotoTabLayout extends Fragment ...

随机推荐

  1. JavaScript 事件兼容性写法

    1.以下是JavaScript事件兼容性写法,使用者可以随意使用,兼容所有浏览器.包括IE6(亲测) <!DOCTYPE html> <html> <head> & ...

  2. 关于HTML5,最牛逼的10本书!

    关于HTML5,最牛逼的10本书! 关于HTML5,最牛逼的10本书.rar HTML5+CSS3从入门到精通 李东博 著 推荐指数:★★★☆ 简介:本书通过基础知识+中小实例+综合案例的方式,讲述了 ...

  3. 安卓手机下载YouTube视频的3种方法

    作为全球最大的在线视频网站,YouTube上面的内容可真是应有尽有啊,从教学视频到个人手工艺品制作流程,从各种搞笑视频到电视连续集等等,包罗万象.如果你想下载YouTube视频到电脑上面的话,网上有很 ...

  4. My First Blog in Cnblogs

    终于打算从csdn搬到博客园了 虽然在csdn只写过三篇文章,不过打算写第四篇的时候发现原先的三篇都消失了.联系客服最终还是找回了,不过对于csdn神奇的管理方式还是不放心,也没在csdn上再写过文章 ...

  5. leetcode: 哈希——two-sum,3sum,4sum

    1). two-sum Given an array of integers, find two numbers such that they add up to a specific target ...

  6. 如何查找BAPI SD_SALESDOCUMENT_CHANGE里的字段对应的数据库存储表

    BAPI函数SD_SALESDOCUMENT_CHANGE可以让我们很方便地通过ABAP代码来修改Sales Order. 其输入参数ORDER_HEADER_IN的类型是BAPISDHD1, 里面包 ...

  7. centos6 yum 安装 install c++4.8 gcc4.8

    cd /etc/yum.repos.d wget http://people.centos.org/tru/devtools-1.1/devtools-1.1.repo yum --enablerep ...

  8. python3中使用HTMLTestRunner.py报ImportError: No module named 'StringIO'的解决办法

    .原因是官网的是python2语法写的,看官手动把官网的HTMLTestRunner.py改成python3的语法: 参考:http://bbs.chinaunix.net/thread-415474 ...

  9. 20145238-荆玉茗 《Java程序设计》第2次实验

    20145238 <Java程序设计>第2次实验报告 实验二 Java面向对象程序设计 一.实验内容 初步掌握单元测试和TDD 理解并掌握面向对象三要素:封装.继承.多态 初步掌握UML建 ...

  10. java、javac -version不一致(java编译及运行环境不一致)的环境变量设置问题解决

    问题描述: 电脑上同时安装了JDK1.6与1.7  设置了环境变量JAVA_HOME为jdk1.6.0_21的安装目录,并且在PATH变量中加入了%JAVA_HOME%\bin,但在Windows命令 ...