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. IntelliJ、ReSharper 4折 加入慧都“惊喜惠”

    慧都2013岁末回馈惊喜不断!著名的软件开发公司JetBrains旗下所有产品加入"惊喜惠"活动环节, JAVA IDE——IntelliJ IDEA,.NET效率工具集——ReS ...

  2. 《ArcGIS Runtime SDK for Android开发笔记》——问题集:.geodatabase创建,创建时内容缺失问题总结

    1.前言 利用ArcGIS桌面提供的share as -> ArcGIS Runtiem Content工具在导出.geodatabase文件时经常会发生数据缺失问题,比如数据表中数据有4w多条 ...

  3. ui.datepicker的回显问题

    应用场景: 页面上有一些现有的输入框,需要调用日期插件,同时还要clone一份,动态添加到页面中,动态生成的输入框在调用datepicker的时候,click事件有效,但是选择的时间无法回显到对应的输 ...

  4. 笨办法学Python(十五)

    习题 15: 读取文件 你已经学过了 raw_input 和 argv,这些是你开始学习读取文件的必备基础.你可能需要多多实验才能明白它的工作原理,所以你要细心做练习,并且仔细检查结果.处理文件需要非 ...

  5. cs231n 17-18 assignment2 出现 No module named 'past' 解决方法

    解决方法: pip install future

  6. IOS 线程的总结(及cell的图片下载)

    零.线程的注意点(掌握) 1.不要同时开太多的线程(1~3条线程即可,不要超过5条)2.线程概念1> 主线程 : UI线程,显示.刷新UI界面,处理UI控件的事件2> 子线程 : 后台线程 ...

  7. iis 7 操作 .net

    下面说一下.NET对IIS7操作.IIS7的操作和IIS5/6(using system.DirectoryServices;使用类DirectoryEntry )有很大的不同,在IIS7里增加了 M ...

  8. 2018.8.6 学习 log4j.properties 配置文件

    配置文件的话第一步当然是解决乱码问题 Eclipse中properties文件中文乱码解决方式 打开eclipse的properties文件时你会发现,其中部分中文注释乱码了,下面将写出如何设置pro ...

  9. 2017.11.21 基于JSP+Servlet+JavaBean实现复数运算(二)

    代码的实现 最基本的MVC模式 //input.jsp 输入界面 <%@ page language="java" import="java.util.*" ...

  10. Ubuntu 10.04上安装MongoDB

    MongoDB是一个可扩展.高性能的下一代数据库.MongoDB中的数据以文档形式存储,这样就能在单个数据对象中表示复杂的关系.文档可能由 以下几 部分组成:独立的基本类型属性.“内嵌文档”或文档数组 ...