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发射的第一个数据。
4.效果预览
4.1.目前完成的工作
新闻的主页面三种大类型
图片的一种大类型(也只用了一种)
视频的一种大类型(采用了新闻主页面的其中一种)
订阅号的主页面的一种大类型(也只采用了一种)
然后还有一些点击事件,调转到相应的活动页面还未实现。
4.2.目前手机真实数据效果
TouTiao开源项目 分析笔记13 最后一个订阅号的实现主页面的更多相关文章
- TouTiao开源项目 分析笔记12 从总体到局部 构建视频主页面
1.构建视频主列表的整体碎片VideoTabLayout 1.1.首先创建一个VideoTabLayout package com.jasonjan.headnews.module.video; im ...
- TouTiao开源项目 分析笔记9 实现一个问答主页面
1.根据API返回创建几个基础的Bean 1.1.WendaArticleDataBean类 API返回的数据如下: /** * cell_type : 36 * extra : {"wen ...
- TouTiao开源项目 分析笔记4==>一个简单APP 整体常用框架
1.效果预览 1.1.如下图所以,到目前为止所有的功能. 2.从InitApp开始->SplashActivity->MainActivity 2.1.InitApp源代码.这是整个项目的 ...
- TouTiao开源项目 分析笔记2
1.Constant常量定义类 1.1.源代码 public class Constant { public static final String USER_AGENT_MOBILE = " ...
- TouTiao开源项目 分析笔记6
1.NewsChannelBean简单类笔记 1.1.Comparable接口的实现和使用 参考文章:Comparable接口的实现和使用. 因为NewsChannelBean实现了Comparabl ...
- TouTiao开源项目 分析笔记10 实现通用普通文章片段页面
1.RxJava的Observable数据操作符总结 1.1.Map操作符 Map操作符对原始Observable发射的没一项数据应用一个你选择的函数, 然后返回一个发射这些结果的Observable ...
- TouTiao开源项目 分析笔记17 新闻媒体专栏
1.效果预览 1.1.要实现的效果 1.2.如何调转到新闻媒体专栏 点击右上角的用户图标. 在新闻详情页面的Fragment的菜单点击事件中触发. case R.id.action_open_medi ...
- TouTiao开源项目 分析笔记15 新闻详情之两种类型的实现
1.预览效果 1.1.首先看一下需要实现的效果. 第一种,文字类型新闻. 第二种,图片类型新闻. 1.2.在NewsArticleTextViewBinder中设置了点击事件 RxView.click ...
- TouTiao开源项目 分析笔记11 以总体到局部的思路 构建图片主列表
1.构建图片主列表的整体片段PhotoTabLayout 1.1.首先创建一个PhotoTabLayout片段 public class PhotoTabLayout extends Fragment ...
随机推荐
- android ContentProvider共享数据
ContentProvider共享数据 ContentProvider对外共享数据需要: 1.定义一个ContentProvider类,需要继承android的ContentProvider基类 2. ...
- Mat转化为IplImage类型的方法
Mat image_mat; IplImage imgTmp = image_mat; IplImage *img = cvCloneImage(&imgTmp);
- MOSS2010中如何用代码给托管元数据类型的栏目赋值
最近项目中遇到如何用代码给托管元数据类型的栏目赋值问题,经过折腾,现把我的思路和实现方法共享出来,让大家一起来学习学习.相互探讨下. /// <summary> /// 托管元数据 /// ...
- 磁盘空间满了之后MySQL会怎样
大多数用户在对于磁盘进行分区的时候都是习惯性的不给系统盘预留很大空间,其实这并不是一个好习惯.因为系统分区并不像我们想象的那样会仅仅安装一个操作系统,系统分区多数还是会承载操作系统主要应用软件安装任务 ...
- 复制windows CMD命令行中的内容
标记文本后,按"回车",或鼠标"右键"为从CMD中复制文本. 在CMD中,按鼠标"右键",为在CMD中粘贴文本.
- java集合框架——List
一.List接口概述 List有个很大的特点就是可以操作角标. 下面开始介绍List接口中相对于Collection接口比较特别的方法.在Collection接口中已经介绍的方法此处就不再赘述. 1. ...
- C++学习之虚析构函数
什么样的情况下才需要虚析构函数? 类需要控制自己的对象执行一系列操作时发生什么样的行为,这些操作包括:创建(对象).拷贝.移动.赋值和销毁.在继承体系中,如果一个类(基类或其派生的类)没有定义拷贝控制 ...
- 创建一个gradle项目
1.创建项目 一定要选这个安装的路径 项目创建成功,修改build.gradle文件,主要是为了下载依赖的jar包,原始模板, 而我修改之后,如下 apply plugin: 'idea' apply ...
- 机器学习实战之Logistic回归
Logistic回归一.概述 1. Logistic Regression 1.1 线性回归 1.2 Sigmoid函数 1.3 逻辑回归 1.4 LR 与线性回归的区别 2. LR的损失函数 3. ...
- Spring Quartz *.QRTZ_LOCKS' doesn't exist
ERROR [org.springframework.web.context.ContextLoader] - Context initialization failedorg.springframe ...