1.真实页面预览

1.1.成果预览

  TouTiao开源项目 分析笔记19 问答内容-LMLPHP

  首先是问答列表

  然后每个item设置点击事件,进入问答内容列表

  然后每一个问答内容也设置点击事件,进入问答详情

1.2.触发事件。

  在WendaArticleOneImgViewBinder中,设置item的点击事件,

  跳转到WendaContentActivity。

  在WendaArticleTextViewBinder中,设置item的点击事件,

  跳转到WendaContentActivity。

  在WendaArticleThreeImgViewBinder中,设置item的点击事件,

  跳转到WendaContentActivity。

2.问答内容的活动页面

2.1.源代码  

public class WendaContentActivity extends BaseActivity {

    private static final String TAG = "WendaContentActivity";

    public static void launch(String qid) {
InitApp.AppContext.startActivity(new Intent(InitApp.AppContext, WendaContentActivity.class)
.putExtra(TAG, qid)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
} @Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.container);
getSupportFragmentManager().beginTransaction()
.replace(R.id.container, WendaContentFragment.newInstance(getIntent().getStringExtra(TAG)))
.commit();
}
}

2.2.需要的布局==>container.xml

<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/viewBackground"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:layout="@layout/fragment_news_tab"/>

2.3.清单中活动配置

<activity
android:name=".module.wenda.content.WendaContentActivity"
android:configChanges="orientation|screenSize|uiMode"
android:theme="@style/AppTheme.NoActionBar.Slidable"/>

2.4.现在还需要问答内容的片段了。

3.问答内容片段

3.1.底层接口==>IWendaContent 

public interface IWendaContent {

    interface View extends IBaseListView<Presenter> {

        /**
* 请求数据
*/
void onLoadData(); /**
* 刷新
*/
void onRefresh(); /**
* 设置顶部信息
*/
void onSetHeader(WendaContentBean.QuestionBean questionBean);
} interface Presenter extends IBasePresenter { /**
* 设置顶部信息
*/
void doSetHeader(WendaContentBean.QuestionBean questionBean); /**
* 请求数据
*/
void doLoadData(String qid); /**
* 再起请求数据
*/
void doLoadMoreData(); /**
* 设置适配器
*/
void doSetAdapter(List<WendaContentBean.AnsListBean> list); /**
* 加载完毕
*/
void doShowNoMore();
}
}

3.2.源代码 

public class WendaContentFragment extends BaseListFragment<IWendaContent.Presenter> implements IWendaContent.View {

    private static final String TAG = "WendaContentFragment";
private String qid;
private String shareTitle;
private String shareUrl;
private WendaContentBean.QuestionBean WendaContentHeaderBean; public static WendaContentFragment newInstance(String qid) {
Bundle args = new Bundle();
args.putString(TAG, qid);
WendaContentFragment fragment = new WendaContentFragment();
fragment.setArguments(args);
return fragment;
} @Override
public void setPresenter(IWendaContent.Presenter presenter) {
if (null == presenter) {
this.presenter = new WendaContentPresenter(this);
}
} @Override
public void onSetAdapter(List<?> list) {
Items newItems = new Items();
newItems.add(WendaContentHeaderBean);
newItems.addAll(list);
newItems.add(new LoadingBean());
DiffCallback.notifyDataSetChanged(oldItems, newItems, DiffCallback.WENDA_CONTENT, adapter);
oldItems.clear();
oldItems.addAll(newItems);
canLoadMore = true;
} @Override
protected int attachLayoutId() {
return R.layout.fragment_list_toolbar;
} @Override
protected void initView(View view) {
super.initView(view);
Toolbar toolbar = view.findViewById(R.id.toolbar);
initToolBar(toolbar, true, getString(R.string.title_wenda));
toolbar.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
recyclerView.smoothScrollToPosition(0);
}
});
toolbar.setBackgroundColor(SettingUtil.getInstance().getColor()); adapter = new MultiTypeAdapter(oldItems);
Register.registerWendaContentItem(adapter);
recyclerView.setAdapter(adapter);
recyclerView.addOnScrollListener(new OnLoadMoreListener() {
@Override
public void onLoadMore() {
if (canLoadMore) {
canLoadMore = false;
presenter.doLoadMoreData();
}
}
});
setHasOptionsMenu(true);
} @Override
protected void initData() {
this.qid = getArguments().getString(TAG);
onLoadData();
} @Override
public void onLoadData() {
onShowLoading();
presenter.doLoadData(qid);
} @Override
public void onRefresh() {
recyclerView.smoothScrollToPosition(0);
presenter.doRefresh();
} @Override
public void onSetHeader(WendaContentBean.QuestionBean questionBean) {
this.shareTitle = questionBean.getShare_data().getTitle();
this.shareUrl = questionBean.getShare_data().getShare_url();
this.WendaContentHeaderBean = questionBean;
} @Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.menu_wenda_content, menu);
} @Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.action_wenda_share) {
IntentAction.send(getActivity(), shareTitle + "\n" + shareUrl);
}
return super.onOptionsItemSelected(item);
} @Override
public void fetchData() { }
}

  ①新建一个实例,传入一个问答id,返回一个片段。

  ②重写设置处理器,新建了一个。

  ③重写视图层的设置适配器,传入一个List,处理新老数据。

  ④重写初始化视图。

    获取toolbar,并设置点击事件。

    设置recyclerView的适配器,并设置滑动监听事件。

    设置菜单。

  ⑤重写初始化数据。

    从arguments获取问答id。

    然后调用加载数据的函数。

  ⑥重写加载数据的函数。

    先显示Loading。

    然后调用处理器的加载函数。

  ⑦重写刷新函数。

    先将recyclerView滑到最上面。

    然后调用处理器的刷新函数。

  ⑧重写设置头部。传入一个Bean。获取分享的标题和url。

  ⑨重写菜单以及菜单的点击事件。

3.3.需要的菜单布局  

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> <item
android:id="@+id/action_wenda_share"
android:icon="@drawable/ic_share_white_24dp"
android:title="@string/action_share"
app:showAsAction="ifRoom"/> </menu>

  预览页面:

  TouTiao开源项目 分析笔记19 问答内容-LMLPHP

4.设置处理器

4.1.处理器源代码

public class WendaContentPresenter implements IWendaContent.Presenter {
private static final String TAG = "WendaContentPresenter";
private IWendaContent.View view;
private String qid;
private int niceOffset = 0;
private int normalOffset = 0;
private int niceAnsCount = 0;
private int normalAnsCount = 0;
private List<WendaContentBean.AnsListBean> ansList = new ArrayList<>();
private String title; WendaContentPresenter(IWendaContent.View view) {
this.view = view;
} public void doRefresh() {
if (ansList.size() != 0) {
ansList.clear();
niceOffset = 0;
normalOffset = 0;
}
doLoadData(this.qid);
} @Override
public void doShowNetError() {
view.onHideLoading();
view.onShowNetError();
} @Override
public void doLoadData(String qid) {
this.qid = qid;
Log.d(TAG, "doLoadArticle: " + qid); RetrofitFactory.getRetrofit().create(IMobileWendaApi.class).getWendaNiceContent(qid)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<WendaContentBean>() {
@Override
public void accept(@NonNull WendaContentBean wendaContentBean) throws Exception {
doSetHeader(wendaContentBean.getQuestion());
doSetAdapter(wendaContentBean.getAns_list());
niceOffset += 10;
}
}, new Consumer<Throwable>() {
@Override
public void accept(@NonNull Throwable throwable) throws Exception {
doShowNetError();
ErrorAction.print(throwable);
}
});
} @Override
public void doLoadMoreData() { if (niceOffset < niceAnsCount) {
RetrofitFactory.getRetrofit().create(IMobileWendaApi.class)
.getWendaNiceContentLoadMore(qid, niceOffset)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<WendaContentBean>() {
@Override
public void accept(@NonNull WendaContentBean wendaContentBean) throws Exception {
doSetAdapter(wendaContentBean.getAns_list());
niceOffset += 10;
}
}, new Consumer<Throwable>() {
@Override
public void accept(@NonNull Throwable throwable) throws Exception {
doShowNetError();
ErrorAction.print(throwable);
}
});
} else if (normalOffset < normalAnsCount) {
RetrofitFactory.getRetrofit().create(IMobileWendaApi.class)
.getWendaNormalContentLoadMore(qid, normalOffset)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<WendaContentBean>() {
@Override
public void accept(@NonNull WendaContentBean wendaContentBean) throws Exception {
doSetAdapter(wendaContentBean.getAns_list());
normalOffset += 10;
}
}, new Consumer<Throwable>() {
@Override
public void accept(@NonNull Throwable throwable) throws Exception {
doShowNetError();
ErrorAction.print(throwable);
}
});
} else {
doShowNoMore();
}
} @Override
public void doSetAdapter(List<WendaContentBean.AnsListBean> list) {
for (WendaContentBean.AnsListBean bean : list) {
bean.setTitle(this.title);
bean.setQid(this.qid);
}
ansList.addAll(list);
view.onSetAdapter(ansList);
view.onHideLoading();
} @Override
public void doSetHeader(WendaContentBean.QuestionBean questionBean) {
this.niceAnsCount = questionBean.getNice_ans_count();
this.normalAnsCount = questionBean.getNormal_ans_count();
this.title = questionBean.getTitle();
view.onSetHeader(questionBean);
} @Override
public void doShowNoMore() {
view.onHideLoading();
if (ansList.size() > 0) {
view.onShowNoMore();
}
}
}

4.2.处理器的构造函数。

  传入了一个视图层,方便处理视图。

4.3.刷新函数。

  将之前的列表清空,然后调用处理器的加载函数即可。

4.4.处理网络异常。

4.5.处理器的加载函数。

  传入了一个问答Id。

  调用API去请求问答内容。

  然后在subscribe进行下一步处理。

4.6.处理器的加载更多函数。

  没有传递任何参数。

  调用API去请求问答更多内容。

  然后在subscribe中进行下一步处理。

4.7.处理器的设置适配器函数。

  传递了一个List。

  然后调用视图层的设置适配器,并将这个List传给它。

  

4.8.处理器的设置头部。

  传入了一个问题Bean类型。

  调用视图层的设置头部信息。

4.9.处理器的没有更多了。

  调用视图层的隐藏加载+没有更多了。

5.注册数据类型

5.1.统一管理

/**
* 注册问答内容
* @param adapter
*/
public static void registerWendaContentItem(@NonNull MultiTypeAdapter adapter) {
adapter.register(WendaContentBean.QuestionBean.class, new WendaContentHeaderViewBinder());
adapter.register(WendaContentBean.AnsListBean.class, new WendaContentViewBinder());
adapter.register(LoadingBean.class, new LoadingViewBinder());
adapter.register(LoadingEndBean.class, new LoadingEndViewBinder());
}

5.2.问答头部绑定器==>WendaContentHeaderViewBinder

  源代码:

public class WendaContentHeaderViewBinder extends ItemViewBinder<WendaContentBean.QuestionBean, WendaContentHeaderViewBinder.ViewHolder> {

    @NonNull
@Override
protected WendaContentHeaderViewBinder.ViewHolder onCreateViewHolder(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent) {
View view = inflater.inflate(R.layout.item_wenda_content_header, parent, false);
return new ViewHolder(view);
} @Override
protected void onBindViewHolder(@NonNull ViewHolder holder, @NonNull final WendaContentBean.QuestionBean item) {
try {
String tv_title = item.getTitle();
String tv_abstract = item.getContent().getText(); String tv_answer_count = item.getNormal_ans_count() + item.getNice_ans_count() + " 回答";
String tv_follow_count = item.getFollow_count() + " 关注";
holder.tv_title.setText(tv_title);
if (!TextUtils.isEmpty(tv_abstract)) {
holder.tv_abstract.setText(tv_abstract);
} else {
holder.tv_abstract.setVisibility(View.GONE);
}
holder.tv_answer_count.setText(tv_answer_count);
holder.tv_follow_count.setText(tv_follow_count);
holder.title_view.setBackgroundColor(SettingUtil.getInstance().getColor());
} catch (Exception e) {
ErrorAction.print(e);
}
} public class ViewHolder extends RecyclerView.ViewHolder { private TextView tv_title;
private TextView tv_abstract;
private TextView tv_answer_count;
private TextView tv_follow_count;
private LinearLayout title_view; public ViewHolder(View itemView) {
super(itemView);
this.tv_title = itemView.findViewById(R.id.tv_title);
this.tv_abstract = itemView.findViewById(R.id.tv_abstract);
this.tv_answer_count = itemView.findViewById(R.id.tv_answer_count);
this.tv_follow_count = itemView.findViewById(R.id.tv_follow_count);
this.title_view = itemView.findViewById(R.id.title_view);
}
}
}

  需要的布局==>item_wenda_content.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"> <LinearLayout
android:id="@+id/title_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="8dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="0dp"> <TextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.AppCompat.Title"
android:textColor="@color/White"
tools:text="都说床头不能朝西,有什么说法吗?"/>
</LinearLayout> <LinearLayout
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/title_view"
android:background="@color/viewBackground"
android:orientation="vertical"
android:paddingBottom="8dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="8dp"> <TextView
android:id="@+id/tv_abstract"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="16sp"
tools:text="都说床头不能朝西,可是我家设计的两个卧室都是不得不朝西的啊!有什么不好吗?"/> <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:orientation="horizontal"> <TextView
android:id="@+id/tv_answer_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
tools:text="602个回答"/> <TextView
android:id="@+id/tv_follow_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:textSize="14sp"
tools:text="68关注"/> </LinearLayout>
</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>

  预览图片:

  TouTiao开源项目 分析笔记19 问答内容-LMLPHP

  

5.3.问答列表绑定视图

  源代码: 

public class WendaContentViewBinder extends ItemViewBinder<WendaContentBean.AnsListBean, WendaContentViewBinder.ViewHolder> {

    @NonNull
@Override
protected WendaContentViewBinder.ViewHolder onCreateViewHolder(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent) {
View view = inflater.inflate(R.layout.item_wenda_content, parent, false);
return new ViewHolder(view);
} @Override
protected void onBindViewHolder(@NonNull ViewHolder holder, @NonNull final WendaContentBean.AnsListBean item) {
try {
String iv_user_avatar = item.getUser().getAvatar_url();
ImageLoader.loadCenterCrop(holder.itemView.getContext(), iv_user_avatar, holder.iv_user_avatar, R.color.viewBackground);
String tv_user_name = item.getUser().getUname();
String tv_like_count = item.getDigg_count() + "";
String tv_abstract = item.getContent_abstract().getText();
holder.tv_user_name.setText(tv_user_name);
holder.tv_like_count.setText(tv_like_count);
holder.tv_abstract.setText(tv_abstract); RxView.clicks(holder.itemView)
.throttleFirst(1, TimeUnit.SECONDS)
.subscribe(new Consumer<Object>() {
@Override
public void accept(@io.reactivex.annotations.NonNull Object o) throws Exception {
//WendaDetailActivity.launch(item);
}
});
} catch (Exception e) {
ErrorAction.print(e);
}
} public class ViewHolder extends RecyclerView.ViewHolder { private CircleImageView iv_user_avatar;
private TextView tv_user_name;
private TextView tv_like_count;
private TextView tv_abstract; public ViewHolder(View itemView) {
super(itemView);
this.iv_user_avatar = itemView.findViewById(R.id.iv_user_avatar);
this.tv_user_name = itemView.findViewById(R.id.tv_user_name);
this.tv_like_count = itemView.findViewById(R.id.tv_like_count);
this.tv_abstract = itemView.findViewById(R.id.tv_abstract);
}
}
}

  需要的item布局:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
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:orientation="vertical"
android:padding="16dp"> <LinearLayout
android:id="@+id/header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"> <com.jasonjan.headnews.widget.CircleImageView
android:id="@+id/iv_user_avatar"
android:layout_width="22dp"
android:layout_height="22dp"
android:scaleType="centerCrop"/> <TextView
android:id="@+id/tv_user_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:ellipsize="end"
android:maxLines="1"
android:textAppearance="@style/TextAppearance.AppCompat.Caption"
tools:text="用户名"/> <RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"> <ImageView
android:id="@+id/iv_like"
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_centerVertical="true"
android:layout_marginEnd="4dp"
android:layout_marginRight="4dp"
android:layout_toLeftOf="@+id/tv_like_count"
android:layout_toStartOf="@+id/tv_like_count"
app:srcCompat="@drawable/ic_like_gray_24dp"
tools:ignore="ContentDescription"/> <TextView
android:id="@+id/tv_like_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
tools:text="22"/> </RelativeLayout>
</LinearLayout> <TextView
android:id="@+id/tv_abstract"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:ellipsize="end"
android:maxLines="6"
tools:text="我去过印度,觉得印度人有时也太可爱了,在他们眼里,印度几乎就是唯一的,他们接受新事物的能力似乎非常的有限。但真心是想不到,印度居然还是IT大国。去过印度的人通常都会从导游那里知道:从新德里出发到阿格拉的泰姬陵之间的一趟列车,时速最高的时候达到了160公里/每小时,被印度人称为当地最快的火车。因为印度人非常的热情,看到中国游客就会用蹩脚的汉语跟中国人搭讪,甚至会问:“中国有没有这样快的火车呀?”,这让人尴尬不已,不知道如何回答是好。我在想如下回答,如何?----对不起,中国没有时速160的火车,只有时速360的动车。----我们中国的火车坐的人少,拉轻,印度的火车超载了,跑不快,所以中国的火车要快一点。----你们印度人是坐在车外面的,所以感觉很快,我们的高铁是坐里面的,所以感觉不到快。"/> </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>

  预览效果:

  TouTiao开源项目 分析笔记19 问答内容-LMLPHP

6.API请求IMobileWendaApi

6.1.获取头条问答优质回答 

/**
* 获取头条问答优质回答
* http://is.snssdk.com/wenda/v1/question/brow/?iid=10344168417&device_id=36394312781
*
* @param qid 问答ID
*/
@POST("http://is.snssdk.com/wenda/v1/question/brow/?iid=10344168417&device_id=36394312781")
@FormUrlEncoded
Observable<WendaContentBean> getWendaNiceContent(@Field("qid") String qid);

  传进来一个问答Id。

  传出去Observable<WendaContentBean>。

6.2.获取头条问答优质回答的加载更多

/**
* 获取头条问答优质回答(加载更多)
* http://is.snssdk.com/wenda/v1/question/loadmore/?iid=10344168417&device_id=36394312781
*
* @param qid 问答ID
* @param offset 偏移量
*/
@POST("http://is.snssdk.com/wenda/v1/question/loadmore/?iid=10344168417&device_id=36394312781")
@FormUrlEncoded
Observable<WendaContentBean> getWendaNiceContentLoadMore(
@Field("qid") String qid,
@Field("offset") int offset);

  传进来一个问答Id,一个偏移量。

  传出去一个Observable<WendaContentBean>。

6.3.获取头条问答普通回答

/**
* 获取头条问答普通回答
* http://is.snssdk.com/wenda/v1/questionother/brow/?iid=10344168417&device_id=36394312781
*
* @param qid 问答ID
*/
@POST("http://is.snssdk.com/wenda/v1/questionother/brow/?iid=10344168417&device_id=36394312781")
@FormUrlEncoded
Observable<WendaContentBean> getWendaNormalContent(@Field("qid") String qid);

  传进来一个问答Id。

  传出去一个Observable<WendaContentBean>。

6.4.获取头条问答普通回答的加载更多

 /**
* 获取头条问答普通回答(加载更多)
* http://is.snssdk.com/wenda/v1/questionother/loadmore/?iid=10344168417&device_id=36394312781
*
* @param qid 问答ID
* @param offset 偏移量
*/
@POST("http://is.snssdk.com/wenda/v1/questionother/loadmore/?iid=10344168417&device_id=36394312781")
@FormUrlEncoded
Observable<WendaContentBean> getWendaNormalContentLoadMore(
@Field("qid") String qid,
@Field("offset") int offset);

  传进来一个问答Id,一个偏移量。

  传出去一个Observable<WendaContentBean>。

05-26 17:27