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. c语言函数指针的几种使用方式

    1.直接定义函数指针赋值并使用. #include <stdio.h> int max(int x, int y) { if (x > y) return x; else retur ...

  2. Trouble Shooting的一些感想(实时补充)

    最近一直做两个产品功能的Merge工作,代码Merge过来之后,发现了很多问题.经过Trouble Shooting,最终发现归根结底都是我们Merge的问题,例如有的Code没有Merge过来,Me ...

  3. Struts2_使用token拦截器控制重复提交(很少用)

    控制重复提交的方式:1.表单提交后页面重定向:2.Struts2.x token拦截器 大致流程: 例子: index.jsp <%@ page language="java" ...

  4. Struts2_访问Web元素

    取得Map 类型的 request,session,application, HttpServletRequest,HttpSession,ServletContext的引用. 分访问 Map 类型和 ...

  5. MyEclipse导入JAVA工程显示红色叉叉的解决方法

    当我们有时候导入一个新的工程的时候可能会出现以下这种情况,基本上是因为jar包路径的问题. 解决方法如下: 1.右击工程,选择properties 2.选择 Java Build Path -> ...

  6. Python基础学习之文件(2)

    文件内建方法 1.输入 read()方法用来直接读取字节到字符串中,最多读取给定数目个字节.如果没有给定size参数(默认值为-1)或size值为负,文件将被读取直至末尾. readline()方法读 ...

  7. 【css基础修炼之路】— 谈谈元素的垂直水平居中

    作为一个初级的前端工程师,在开发的过程中遇到了许多问题,其中使元素垂直居中这个问题难住了我,可能在大家看来这是一个非常小的问题,但是却困扰了我很长时间,于是决定做一个总结!!! 废话不多说,直接上代码 ...

  8. python :编写装饰器

    简单装饰器 def log_time(func): # 此函数的作用时接受被修饰的函数的引用test,然后被内部函数使用 def make_decorater(): print('现在开始装饰') f ...

  9. python解析ini文件

    python解析ini文件 使用configparser - Configuration file parser sections() add_section(section) has_section ...

  10. java 基础词汇 必须 第九天

    Collection 集合 List 列表集合 Set 不重复集合 Linked 链表 Vector 线程安全集合 Hash 哈希值 tree 树型结构 Map 键值对集合 add 增加 remove ...