TouTiao开源项目 分析笔记16 新闻评论


1.要达到的效果

1.1.主要效果图

  

  点击了标题栏的消息图标后,然后会跳转到评论详情的页面。

1.2.触发的点击事件

  在新闻详情的片段中的菜单点击事件中

  设置上方标题栏的消息标的监听事件

 case R.id.action_open_comment:
                NewsCommentActivity.launch(bean.getGroup_id() + "", bean.getItem_id() + "");
                break;

  bean就是某一个新闻的一些属性,从最前面item中传递过来的。


2.新闻评论详情活动

2.1.源代码

class NewsCommentActivity : BaseActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.container)
        val intent = intent
        supportFragmentManager.beginTransaction()
                .replace(R.id.container,
                        NewsCommentFragment.newInstance(intent.getStringExtra(ARG_GROUPID), intent.getStringExtra(ARG_ITEMID)))
                .commit()
    }

    companion object {

        private val TAG = "NewsCommentActivity"
        private val ARG_GROUPID = "groupId"
        private val ARG_ITEMID = "itemId"

        fun launch(groupId: String, itemId: String) {
            InitApp.AppContext.startActivity(Intent(InitApp.AppContext, NewsCommentActivity::class.java)
                    .putExtra(ARG_GROUPID, groupId)
                    .putExtra(ARG_ITEMID, itemId)
                    .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
        }
    }
}

2.2.外部启动这个评论活动的一个静态launch函数。

  这里需要将两个关键的参数保存起来,之后在评论中会用到。  

2.3.然后是一个onCreate函数,这个活动优先执行。

  将container替换成片段的布局。

2.4.在清单中配置这个活动 

 <activity
            android:name=".module.news.comment.NewsCommentActivity"
            android:configChanges="orientation|screenSize|uiMode"
            android:label="@string/title_comment"
            android:theme="@style/AppTheme.NoActionBar.Slidable"/>


3.新闻评论的片段

3.1.底层接口==>INewsComment

public interface INewsComment {

    interface View extends IBaseListView<Presenter> {

        /**
         * 请求数据
         */
        void onLoadData();
    }

    interface Presenter extends IBasePresenter {

        /**
         * 请求数据
         */
        void doLoadData(String... groupId_ItemId);

        /**
         * 再起请求数据
         */
        void doLoadMoreData();

        /**
         * 设置适配器
         */
        void doSetAdapter(List<NewsCommentBean.DataBean.CommentBean> list);

        /**
         * 加载完毕
         */
        void doShowNoMore();
    }
}

3.2.新闻评论片段源代码  

public class NewsCommentFragment extends BaseListFragment<INewsComment.Presenter> implements INewsComment.View{
    private static final String GROUP_ID = "groupId";
    private static final String ITEM_ID = "itemId";
    private static final String TAG = "NewsCommentFragment";
    private String groupId;
    private String itemId;

    public static NewsCommentFragment newInstance(String groupId, String itemId) {
        NewsCommentFragment instance = new NewsCommentFragment();
        Bundle bundle = new Bundle();
        bundle.putString(GROUP_ID, groupId);
        bundle.putString(ITEM_ID, itemId);
        instance.setArguments(bundle);
        return instance;
    }

    @Override
    protected int attachLayoutId() {
        return R.layout.fragment_list_toolbar;
    }

    @Override
    protected void initData() {
        Bundle arguments = getArguments();
        groupId = arguments.getString(GROUP_ID);
        itemId = arguments.getString(ITEM_ID);
        onLoadData();
    }

    @Override
    public void onLoadData() {
        onShowLoading();
        presenter.doLoadData(groupId, itemId);
    }

    @Override
    public void onRefresh() {
        presenter.doRefresh();
    }

    @Override
    protected void initView(View view) {
        super.initView(view);
        Toolbar toolbar = view.findViewById(R.id.toolbar);
        initToolBar(toolbar, true, getString(R.string.title_comment));
        toolbar.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                recyclerView.smoothScrollToPosition(0);
            }
        });
        toolbar.setBackgroundColor(SettingUtil.getInstance().getColor());

        adapter = new MultiTypeAdapter(oldItems);
        Register.registerNewsCommentItem(adapter);
        recyclerView.setAdapter(adapter);
        recyclerView.addOnScrollListener(new OnLoadMoreListener() {
            @Override
            public void onLoadMore() {
                if (canLoadMore) {
                    canLoadMore = false;
                    presenter.doLoadMoreData();
                }
            }
        });
        setHasOptionsMenu(true);
    }

    @Override
    public void onSetAdapter(final List<?> list) {
        Items newItems = new Items(list);
        newItems.add(new LoadingBean());
        DiffCallback.notifyDataSetChanged(oldItems, newItems, DiffCallback.NEWS_COMMENT, adapter);
        oldItems.clear();
        oldItems.addAll(newItems);
        canLoadMore = true;
    }

    @Override
    public void setPresenter(INewsComment.Presenter presenter) {
        if (null == presenter) {
            this.presenter = new NewsCommentPresenter(this);
        }
    }

    @Override
    public void fetchData() {

    }
}

3.3.新建一个实例,供外部调用。

  传进来两个参数,一个groupId,一个itemId。

  传出去一个片段Fragment。

3.4.重写返回片段的布局==>fragment_list_toolbar.xml。

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    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:fitsSystemWindows="true"
    android:orientation="vertical">

    <include layout="@layout/toolbar"/>

    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/refresh_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <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"
            app:layoutManager="LinearLayoutManager">
        </android.support.v7.widget.RecyclerView>

    </android.support.v4.widget.SwipeRefreshLayout>

</android.support.design.widget.CoordinatorLayout>

  预览页面:

  

3.5.初始化视图initView。

  传进去一个view。

  获取toolbar+点击事件。

  新建一个adapter,给recycleView设置适配器+滑动监听事件。

  设置菜单。

3.6.初始化数据initData。

  获取存放在bundle中的两个信息。

  然后调用处理器来加载数据。

3.7.重写onRefresh函数。

  调用处理器的刷新。

3.8.重写加载函数onLoadData。

  显示视图的加载圈。

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

3.9.重写设置适配器。

  传入一个List。

  比较新老数据,动态变化数据。

3.10.重写设置处理器。

  传入一个底层接口中定义的一个处理器。

  将这个处理器保存起来以后用。

  

3.11.重写填充数据的fetchData。

  里面是空的。这里不做任何事情。   


4.新闻评论的处理器

4.1.源代码 

package com.jasonjan.headnews.module.news.comment;

import com.jasonjan.headnews.api.IMobileNewsApi;
import com.jasonjan.headnews.bean.news.NewsCommentBean;
import com.jasonjan.headnews.main.ErrorAction;
import com.jasonjan.headnews.main.RetrofitFactory;

import java.util.ArrayList;
import java.util.List;

import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.annotations.NonNull;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;

/**
 * Created by JasonJan on 2018/1/9.
 */

public class NewsCommentPresenter implements INewsComment.Presenter{
    private static final String TAG = "NewsCommentPresenter";
    private INewsComment.View view;
    private String groupId;
    private String itemId;
    private int offset = 0;
    private List<NewsCommentBean.DataBean.CommentBean> commentsBeanList = new ArrayList<>();

    public NewsCommentPresenter(INewsComment.View view) {
        this.view = view;
    }

    @Override
    public void doLoadData(String... groupId_ItemId){
        try {
            if (null == this.groupId) {
                this.groupId = groupId_ItemId[0];
            }
            if (null == this.itemId) {
                this.itemId = groupId_ItemId[1];
            }
        } catch (Exception e) {
            ErrorAction.print(e);
        }

        RetrofitFactory.getRetrofit().create(IMobileNewsApi.class)
                .getNewsComment(groupId, offset)
                .subscribeOn(Schedulers.io())
                .map(new Function<NewsCommentBean, List<NewsCommentBean.DataBean.CommentBean>>() {
                    @Override
                    public List<NewsCommentBean.DataBean.CommentBean> apply(@NonNull NewsCommentBean newsCommentBean) throws Exception {
                        List<NewsCommentBean.DataBean.CommentBean> data = new ArrayList<>();
                        for (NewsCommentBean.DataBean bean : newsCommentBean.getData()) {
                            data.add(bean.getComment());
                        }
                        return data;
                    }
                })
                .compose(view.<List<NewsCommentBean.DataBean.CommentBean>>bindToLife())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<List<NewsCommentBean.DataBean.CommentBean>>() {
                    @Override
                    public void accept(@NonNull List<NewsCommentBean.DataBean.CommentBean> list) throws Exception {
                        if (null != list && list.size() > 0) {
                            doSetAdapter(list);
                        } else {
                            doShowNoMore();
                        }
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(@NonNull Throwable throwable) throws Exception {
                        doShowNetError();
                        ErrorAction.print(throwable);
                    }
                });
    }

    @Override
    public void doLoadMoreData() {
        offset += 20;
        doLoadData();
    }

    @Override
    public void doSetAdapter(List<NewsCommentBean.DataBean.CommentBean> list) {
        commentsBeanList.addAll(list);
        view.onSetAdapter(commentsBeanList);
        view.onHideLoading();
    }

    @Override
    public void doRefresh() {
        if (commentsBeanList.size() != 0) {
            commentsBeanList.clear();
            offset = 0;
        }
        doLoadData();
    }

    @Override
    public void doShowNetError() {
        view.onHideLoading();
        view.onShowNetError();
    }

    @Override
    public void doShowNoMore() {
        view.onHideLoading();
        view.onShowNoMore();
    }
}

4.2.一个构造函数。

  传进去一个底层接口中定义的一个View。

  保存这个View,以后再用。

4.3.重写doLoadData函数。

  传进去一个String...类型,类似于数组。

  然后调用API请求。

4.4.重写加载更多doLoadMoreData函数。

  偏移量增加20即可。

  然后再调用doLoadData函数。

4.5.重写设置适配器doSetAdapter函数。

  传进去一个List。

  然后调用视图层的onSetAdapter函数。

  然后调用试图层的onHideLoading函数。

4.6.重写刷新。

  将List清空,设置偏移量为0。

  然后调用doLoadData。

4.7.重写网络错误。  

  调用视图层的隐藏加载函数。

  调用视图层的显示网络错误。

4.8.重写没有更多。

  调用视图层的隐藏加载函数。

  调用视图层的显示没有更多函数。


5.注册数据类型

5.1.首先在新闻评论片段中给适配器添加数据类型 

adapter = new MultiTypeAdapter(oldItems);
        Register.registerNewsCommentItem(adapter);
        recyclerView.setAdapter(adapter);

 

5.2.然后在自定义类Register统一注册这个页面需要的类型 

public static void registerNewsCommentItem(@NonNull MultiTypeAdapter adapter) {
        adapter.register(NewsCommentBean.DataBean.CommentBean.class, new NewsCommentViewBinder());
        adapter.register(LoadingBean.class, new LoadingViewBinder());
        adapter.register(LoadingEndBean.class, new LoadingEndViewBinder());
    }

  

5.3.然后看一下这个新闻评论的视图绑定类==>NewsCommentViewBinder

package com.jasonjan.headnews.binder.news;

import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.design.widget.Snackbar;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import com.jasonjan.headnews.R;
import com.jasonjan.headnews.bean.news.NewsCommentBean;
import com.jasonjan.headnews.main.ErrorAction;
import com.jasonjan.headnews.main.IntentAction;
import com.jasonjan.headnews.module.base.BaseActivity;
import com.jasonjan.headnews.util.ImageLoader;
import com.jasonjan.headnews.widget.BottomSheetDialogFixed;

import me.drakeet.multitype.ItemViewBinder;

/**
 * Created by JasonJan on 2018/1/9.
 */

public class NewsCommentViewBinder extends ItemViewBinder<NewsCommentBean.DataBean.CommentBean,NewsCommentViewBinder.ViewHolder> {

    @NonNull
    @Override
    protected NewsCommentViewBinder.ViewHolder onCreateViewHolder(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent) {
        View view = inflater.inflate(R.layout.item_news_comment, parent, false);
        return new ViewHolder(view);
    }

    @Override
    protected void onBindViewHolder(@NonNull final ViewHolder holder, @NonNull final NewsCommentBean.DataBean.CommentBean item) {

        final Context context = holder.itemView.getContext();

        try {
            String iv_avatar = item.getUser_profile_image_url();
            String tv_username = item.getUser_name();
            String tv_text = item.getText();
            int tv_likes = item.getDigg_count();

            ImageLoader.loadCenterCrop(context, iv_avatar, holder.iv_avatar, R.color.viewBackground);
            holder.tv_username.setText(tv_username);
            holder.tv_text.setText(tv_text);
            holder.tv_likes.setText(tv_likes + "赞");
            holder.itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    final String content = item.getText();
                    final BottomSheetDialogFixed dialog = new BottomSheetDialogFixed(context);
                    dialog.setOwnerActivity((BaseActivity) context);
                    View view = ((BaseActivity) context).getLayoutInflater().inflate(R.layout.item_comment_action_sheet, null);
                    view.findViewById(R.id.layout_copy_text).setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View view) {
                            ClipboardManager copy = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
                            ClipData clipData = ClipData.newPlainText("text", content);
                            copy.setPrimaryClip(clipData);
                            Snackbar.make(holder.itemView, R.string.copied_to_clipboard, Snackbar.LENGTH_SHORT).show();
                            dialog.dismiss();
                        }
                    });
                    view.findViewById(R.id.layout_share_text).setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View view) {
                            IntentAction.send(context, content);
                            dialog.dismiss();
                        }
                    });
                    dialog.setContentView(view);
                    dialog.show();
                }
            });
        } catch (Exception e) {
            ErrorAction.print(e);
        }
    }

    public class ViewHolder extends RecyclerView.ViewHolder {

        private ImageView iv_avatar;
        private TextView tv_username;
        private TextView tv_text;
        private TextView tv_likes;

        public ViewHolder(View itemView) {
            super(itemView);
            this.iv_avatar = itemView.findViewById(R.id.iv_avatar);
            this.tv_username = itemView.findViewById(R.id.tv_username);
            this.tv_text = itemView.findViewById(R.id.tv_text);
            this.tv_likes = itemView.findViewById(R.id.tv_likes);
        }
    }
}

 

5.4.需要一个新闻评论每一个item的布局==>item_news_comment.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:orientation="vertical"
        android:paddingBottom="8dp"
        android:paddingLeft="16dp"
        android:paddingRight="16dp"
        android:paddingTop="8dp">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <com.meiji.toutiao.widget.CircleImageView
                android:id="@+id/iv_avatar"
                android:layout_width="22dp"
                android:layout_height="22dp"
                android:layout_gravity="center"/>

            <TextView
                android:id="@+id/tv_username"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:layout_marginLeft="8dp"
                android:layout_marginStart="8dp"
                android:ellipsize="end"
                android:maxLines="1"
                tools:text="小恢恢的帽子"/>

        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="4dp"
            android:orientation="vertical">

            <TextView
                android:id="@+id/tv_text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                tools:text="光看个开头就笑的不行了,咱们中国有个传统,就是家里来客人了,要到门口迎一下,如果手里还带着礼物,那要先接过来,因为人家大老远一路带过来的,已经很累了,更何况老美不远万里带过来的呢,如果不接过来那也太不像话了,会让国际笑话中国,也有失大国风范!这也是一种礼貌,更是中华民族的传统美德!"/>

            <TextView
                android:id="@+id/tv_likes"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="2dp"
                android:gravity="end"
                android:maxLines="1"
                tools:text="4832赞"/>

        </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>

  预览页面:

  


6.API请求

6.1.在IMobieNewsApi中写这个函数

 /**
     * 获取新闻评论
     * 按热度排序
     * http://is.snssdk.com/article/v53/tab_comments/?group_id=6314103921648926977&offset=0&tab_index=0
     * 按时间排序
     * http://is.snssdk.com/article/v53/tab_comments/?group_id=6314103921648926977&offset=0&tab_index=1
     *
     * @param groupId 新闻ID
     * @param offset  偏移量
     */
    @GET("http://is.snssdk.com/article/v53/tab_comments/")
    Observable<NewsCommentBean> getNewsComment(
            @Query("group_id") String groupId,
            @Query("offset") int offset);

  传递进来两个参数,一个是新闻Id,一个是偏移量(就是获取那些评论)。

  传出去Observable<NewsCommentBean>

6.2.调用方式==>在处理器的加载数据中执行 

RetrofitFactory.getRetrofit().create(IMobileNewsApi.class)
                .getNewsComment(groupId, offset)
                .subscribeOn(Schedulers.io())
                .map(new Function<NewsCommentBean, List<NewsCommentBean.DataBean.CommentBean>>() {
                    @Override
                    public List<NewsCommentBean.DataBean.CommentBean> apply(@NonNull NewsCommentBean newsCommentBean) throws Exception {
                        List<NewsCommentBean.DataBean.CommentBean> data = new ArrayList<>();
                        for (NewsCommentBean.DataBean bean : newsCommentBean.getData()) {
                            data.add(bean.getComment());
                        }
                        return data;
                    }
                })
                .compose(view.<List<NewsCommentBean.DataBean.CommentBean>>bindToLife())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<List<NewsCommentBean.DataBean.CommentBean>>() {
                    @Override
                    public void accept(@NonNull List<NewsCommentBean.DataBean.CommentBean> list) throws Exception {
                        if (null != list && list.size() > 0) {
                            doSetAdapter(list);
                        } else {
                            doShowNoMore();
                        }
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(@NonNull Throwable throwable) throws Exception {
                        doShowNetError();
                        ErrorAction.print(throwable);
                    }
                });
    }


原文地址:https://www.cnblogs.com/Jason-Jan/p/8250612.html