TouTiao开源项目 分析笔记20 问答详情


1.效果预览

1.1.效果预览,从问答列表开始

  

  前面实现了从列表到内容。

  这里主要讲解从内容到详情。

  点击每一个回答内容,进入回答详情页面。

1.2.触发的点击事件

  在WendaContentViewBinder中,设置item点击事件:

  WendaDetailActivity.lauch(bean);


2.问答详情的活动页面

2.1.源代码

public class WendaDetailActivity extends BaseActivity {

    private static final String TAG = "WendaDetailActivity";

    public static void launch(WendaContentBean.AnsListBean bean) {
        InitApp.AppContext.startActivity(new Intent(InitApp.AppContext, WendaDetailActivity.class)
                .putExtra(TAG, bean)
                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_wenda_content_view);
        getSupportFragmentManager().beginTransaction()
                .replace(R.id.container, WendaDetailFragment.newInstance(getIntent().getParcelableExtra(TAG)))
                .commit();
    }
}

2.2.需要的布局

<?xml version="1.0" encoding="utf-8"?>
<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"
    >

</FrameLayout>

2.3.清单中活动配置

<activity
            android:name=".module.wenda.detail.WendaDetailActivity"
            android:configChanges="orientation|screenSize|uiMode"
            android:label="@string/title_wenda_detail"
            android:theme="@style/AppTheme.NoActionBar.Slidable"/>


3.问答详情页面片段

3.1.需要实现的底层接口

public interface IWendaDetail {
    interface View extends IBaseListView<Presenter> {

        /**
         * 加载网页
         */
        void onSetWebView(String baseUrl, boolean flag);

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

    interface Presenter extends IBasePresenter {

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

        /**
         * 加载评论
         */
        void doLoadComment(String... ansId);

        /**
         * 加载更多评论
         */
        void doLoadMoreComment();

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

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

3.2.片段源代码 

public class WendaDetailFragment extends BaseFragment<IWendaDetail.Presenter> implements IWendaDetail.View{

    private static final String TAG = "WendaDetailFragment";
    private WendaContentBean.AnsListBean bean = null;
    private String url;
    private String title;
    private String shareTitle;

    private WebView webView;
    private NestedScrollView scrollView;
    private ContentLoadingProgressBar progressBar;
    private TextView tv_title;
    private CircleImageView iv_user_avatar;
    private TextView tv_user_name;
    private RecyclerView recyclerView;
    private LinearLayout header_layout;
    private SwipeRefreshLayout swipeRefreshLayout;

    private MultiTypeAdapter adapter;
    private boolean canLoadMore;
    private Items oldItems = new Items();

    public static WendaDetailFragment newInstance(Parcelable bean) {
        Bundle args = new Bundle();
        args.putParcelable(TAG, bean);
        WendaDetailFragment fragment = new WendaDetailFragment();
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    protected void initData() {
        bean = getArguments().getParcelable(TAG);
        if (null == this.bean) {
            onShowNetError();
            return;
        }
        url = bean.getShare_data().getShare_url();
        onLoadData();

        ImageLoader.loadCenterCrop(getActivity(), bean.getUser().getAvatar_url(), iv_user_avatar, R.color.viewBackground);
        tv_title.setText(bean.getTitle());
        tv_user_name.setText(bean.getUser().getUname());
        shareTitle = bean.getShare_data().getTitle();
        header_layout.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                WendaContentActivity.launch(bean.getQid());
            }
        });
    }

    @Override
    public void onLoadData() {
        presenter.doLoadData(url);
    }

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

    @Override
    public void onSetAdapter(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;
        recyclerView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT));
    }

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

    @Override
    protected void initView(View view) {
        Toolbar toolbar = view.findViewById(R.id.toolbar);
        initToolBar(toolbar, true, getString(R.string.title_wenda_detail));
        toolbar.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                scrollView.smoothScrollTo(0, 0);
            }
        });

        webView = view.findViewById(R.id.webview);
        initWebClient();

        header_layout = view.findViewById(R.id.header_layout);
        header_layout.setBackgroundColor(SettingUtil.getInstance().getColor());

        tv_title = view.findViewById(R.id.tv_title);
        iv_user_avatar = view.findViewById(R.id.iv_user_avatar);
        tv_user_name = view.findViewById(R.id.tv_user_name);

        scrollView = view.findViewById(R.id.scrollView);
        scrollView.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() {
            @Override
            public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
                onHideLoading();
            }
        });
        scrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
            @Override
            public void onScrollChanged() {
                View view = scrollView.getChildAt(scrollView.getChildCount() - 1);
                int diff = (view.getBottom() - (scrollView.getHeight() + scrollView.getScrollY()));
                if (diff == 0) {
                    canLoadMore = false;
                    presenter.doLoadMoreComment();
                }
            }
        });

        swipeRefreshLayout = view.findViewById(R.id.refresh_layout);
        swipeRefreshLayout.setColorSchemeColors(SettingUtil.getInstance().getColor());
        swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                swipeRefreshLayout.post(new Runnable() {
                    @Override
                    public void run() {
                        swipeRefreshLayout.setRefreshing(true);
                    }
                });
                presenter.doLoadData(url);
            }
        });

        progressBar = view.findViewById(R.id.pb_progress);
        int color = SettingUtil.getInstance().getColor();
        progressBar.getIndeterminateDrawable().setColorFilter(color, PorterDuff.Mode.MULTIPLY);
        progressBar.show();

        recyclerView = view.findViewById(R.id.recycler_view);
        recyclerView.setHasFixedSize(true);
        // 禁止嵌套滚动
        recyclerView.setNestedScrollingEnabled(false);
        adapter = new MultiTypeAdapter(oldItems);
        Register.registerNewsCommentItem(adapter);
        recyclerView.setAdapter(adapter);

        setHasOptionsMenu(true);
    }

    @Override
    public void onSetWebView(String url, boolean flag) {
        // 是否解析网页成功
        if (flag) {
            webView.loadDataWithBaseURL(null, url, "text/html", "utf-8", null);
            presenter.doLoadComment(bean.getAnsid());
        } else {
            webView.loadUrl(url);
        }
    }

    @SuppressLint("SetJavaScriptEnabled")
    private void initWebClient() {
        WebSettings settings = webView.getSettings();
        settings.setJavaScriptEnabled(true);
        // 缩放,设置为不能缩放可以防止页面上出现放大和缩小的图标
        settings.setBuiltInZoomControls(false);
        // 缓存
        settings.setCacheMode(WebSettings.LOAD_DEFAULT);
        // 开启DOM storage API功能
        settings.setDomStorageEnabled(true);
        // 开启application Cache功能
        settings.setAppCacheEnabled(true);
        // 判断是否为无图模式
        settings.setBlockNetworkImage(SettingUtil.getInstance().getIsNoPhotoMode());
        // 不调用第三方浏览器即可进行页面反应
        webView.setWebViewClient(new WebViewClient() {
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                if (!TextUtils.isEmpty(url)) {
                    view.loadUrl(url);
                }
                return true;
            }

            @Override
            public void onPageFinished(WebView view, String url) {
                onHideLoading();
                super.onPageFinished(view, url);
            }
        });

        webView.setOnKeyListener(new View.OnKeyListener() {
            @Override
            public boolean onKey(View view, int i, KeyEvent keyEvent) {
                if ((keyEvent.getKeyCode() == KeyEvent.KEYCODE_BACK) && webView.canGoBack()) {
                    webView.goBack();
                    return true;
                }
                return false;
            }
        });

        webView.setWebChromeClient(new WebChromeClient() {
            @Override
            public void onProgressChanged(WebView view, int newProgress) {
                super.onProgressChanged(view, newProgress);
                if (newProgress >= 90) {
                    onHideLoading();
                } else {
                    onShowLoading();
                }
            }
        });
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        super.onCreateOptionsMenu(menu, inflater);
        inflater.inflate(R.menu.menu_wenda_detail, menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == R.id.action_wenda_share) {
            IntentAction.send(getActivity(), shareTitle + "
" + url);
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onShowNoMore() {
        getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                if (oldItems.size() > 0) {
                    Items newItems = new Items(oldItems);
                    newItems.remove(newItems.size() - 1);
                    newItems.add(new LoadingEndBean());
                    adapter.setItems(newItems);
                    adapter.notifyDataSetChanged();
                } else if (oldItems.size() == 0) {
                    oldItems.add(new LoadingEndBean());
                    adapter.setItems(oldItems);
                    adapter.notifyDataSetChanged();
                }
                canLoadMore = false;
            }
        });
    }

    @Override
    public void onShowLoading() {
        progressBar.show();
    }

    @Override
    public void onHideLoading() {
        progressBar.hide();
        swipeRefreshLayout.post(new Runnable() {
            @Override
            public void run() {
                swipeRefreshLayout.setRefreshing(false);
            }
        });
    }

    @Override
    public void onShowNetError() {
        Snackbar.make(scrollView, R.string.network_error, Snackbar.LENGTH_SHORT).show();
        getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                adapter.setItems(new Items());
                adapter.notifyDataSetChanged();
                canLoadMore = false;
            }
        });
    }
}
View Code

3.3.新建实例。

  传进去一个bean类。

  传出去一个片段。

3.4.初始化视图。

  获取toolbar,并进行相关设置。

  获取webview,设置一些交互属性。

  获取头部布局,设置颜色。

  获取scrollView,设置滑动监听,以及设置观察树。

  获取刷新圈,设置刷新事件。

  获取进度条,设置颜色。

  获取recyclerView,设置适配器。

  设置菜单。

3.5.重写设置WebView。

  判断是否解析网页成功。

3.6.初始化webClient,设置webView的配置。

3.7.设置菜单。

3.8.重写没有更多了,重写显示加载,重写隐藏加载,重写网络错误。

3.9.重写设置适配器和设置处理器。

3.10.重写布局。

  需要的布局==>fragment_wenda_detail.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"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/news_content_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">

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

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fadeScrollbars="true"
        android:scrollbarFadeDuration="1"
        android:scrollbars="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.v4.widget.NestedScrollView
                android:id="@+id/scrollView"
                android:layout_width="match_parent"
                android:layout_height="match_parent">

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:orientation="vertical">

                    <LinearLayout
                        android:id="@+id/header_layout"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:background="?attr/selectableItemBackground"
                        android:foreground="?attr/selectableItemBackground"
                        android:orientation="vertical">

                        <TextView
                            android:id="@+id/tv_title"
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:ellipsize="end"
                            android:maxLines="2"
                            android:padding="16dp"
                            android:textAppearance="@style/TextAppearance.AppCompat.Title"
                            android:textColor="@color/White"
                            tools:text="都说床头不能朝西,有什么说法吗?"/>

                    </LinearLayout>

                    <LinearLayout
                        android:layout_width="match_parent"
                        android:layout_height="match_parent"
                        android:background="@color/viewBackground"
                        android:gravity="center_vertical"
                        android:padding="8dp">

                        <com.jasonjan.headnews.widget.CircleImageView
                            android:id="@+id/iv_user_avatar"
                            android:layout_width="36dp"
                            android:layout_height="36dp"
                            android:scaleType="centerCrop"
                            android:src="@color/Black" />

                        <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"
                            tools:text="用户名"/>
                    </LinearLayout>

                    <View
                        android:id="@+id/divider"
                        android:layout_width="match_parent"
                        android:layout_height="1px"
                        android:background="@color/line_divider"/>

                    <WebView
                        android:id="@+id/webview"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"/>

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

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

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

        <android.support.v4.widget.ContentLoadingProgressBar
            android:id="@+id/pb_progress"
            style="?android:attr/progressBarStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"/>

    </RelativeLayout>

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

  预览图片:

  


4.问答详情处理器

4.1.需要第三方库Jsoup的支持

  请参考这篇文章:使用Jsoup解析HTML

  在build.gradle中这样配置:

// 解析HTML
    implementation 'org.jsoup:jsoup:1.10.2'

  参考的官方网站:http://jsoup.org/

  

4.2.源代码 

public class WendaDetailPresenter implements IWendaDetail.Presenter {
    private IWendaDetail.View view;
    private String groupId;
    private int offset = 0;
    private List<NewsCommentBean.DataBean.CommentBean> commentsBeanList = new ArrayList<>();

    WendaDetailPresenter(IWendaDetail.View view) {
        this.view = view;
    }

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

    @Override
    public void doLoadData(String url) {
        RetrofitFactory.getRetrofit().create(IMobileWendaApi.class)
                .getWendaAnsDetail(url)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<ResponseBody>() {
                    @Override
                    public void accept(@NonNull ResponseBody responseBody) throws Exception {
                        String result = getHTML(responseBody.string());
                        if (result != null) {
                            view.onSetWebView(result, true);
                        } else {
                            view.onSetWebView(null, false);
                        }
                        view.onHideLoading();
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(@NonNull Throwable throwable) throws Exception {
                        view.onSetWebView(null, false);
                        view.onHideLoading();
                        ErrorAction.print(throwable);
                    }
                });
    }

    private String getHTML(String response) {
        Document doc = Jsoup.parse(response, "UTF-8");
        Elements elements = doc.getElementsByClass("con-words");
        String content = null;
        for (Element element : elements) {
            content = element.toString();
            break;
        }
        if (content != null) {

            String css = "<link rel="stylesheet" href="file:///android_asset/toutiao_light.css" type="text/css">";
            if (SettingUtil.getInstance().getIsNightMode()) {
                css = css.replace("toutiao_light", "toutiao_dark");
            }

            String html = "<!DOCTYPE html>
" +
                    "<html lang="en">
" +
                    "<head>
" +
                    "    <meta charset="UTF-8">" +
                    css +
                    "<body>
" +
                    "<article class="article-container">
" +
                    "    <div class="article__content article-content">" +
                    content +
                    "    </div>
" +
                    "</article>
" +
                    "</body>
" +
                    "</html>";

            return html;
        } else {
            return null;
        }
    }

    @Override
    public void doLoadComment (String...ansId){

        try {
            if (null == groupId) {
                this.groupId = ansId[0];
            }
        } catch (ArrayIndexOutOfBoundsException e) {
            e.printStackTrace();
        }

        RetrofitFactory.getRetrofit().create(IMobileNewsApi.class)
                .getNewsComment(groupId, offset)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .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())
                .subscribe(new Consumer<List<NewsCommentBean.DataBean.CommentBean>>() {
                    @Override
                    public void accept(@NonNull List<NewsCommentBean.DataBean.CommentBean> list) throws Exception {
                        if (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 doLoadMoreComment () {
        offset += 10;
        doLoadComment();
    }

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

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

    @Override
    public void doShowNoMore () {
        view.onHideLoading();
        if (commentsBeanList.size() > 0) {
            view.onShowNoMore();
        }
    }
    
}

4.3.构造函数,传入一个视图层。

4.4.重写刷新函数,调用视图层的加载。

4.5.重写加载数据,调用API。

4.6.获取HTML的一个函数。

  传进去一个response的字符串。

  利用Jsoup解析这个字符串,然后得到一个html。

  方法源代码: 

private String getHTML(String response) {
        Document doc = Jsoup.parse(response, "UTF-8");
        Elements elements = doc.getElementsByClass("con-words");
        String content = null;
        for (Element element : elements) {
            content = element.toString();
            break;
        }
        if (content != null) {

            String css = "<link rel="stylesheet" href="file:///android_asset/toutiao_light.css" type="text/css">";
            if (SettingUtil.getInstance().getIsNightMode()) {
                css = css.replace("toutiao_light", "toutiao_dark");
            }

            String html = "<!DOCTYPE html>
" +
                    "<html lang="en">
" +
                    "<head>
" +
                    "    <meta charset="UTF-8">" +
                    css +
                    "<body>
" +
                    "<article class="article-container">
" +
                    "    <div class="article__content article-content">" +
                    content +
                    "    </div>
" +
                    "</article>
" +
                    "</body>
" +
                    "</html>";

            return html;
        } else {
            return null;
        }
    }

  

4.7.重写加载评论。

  调用API,请求获取问答详情的评论。

  

4.8.重写加载更多评论。

4.9.重写设置适配器。

  传进去一个List。

4.10.重写网络异常,没有更多了。


5.问答详情评论的视图绑定

5.1.这里用的依旧是新闻页面的视图绑定

  在WendaDetailFragment中的一个initView中引用。

 Register.registerNewsCommentItem(adapter);

  在Register中注册数据类型。

/**
     * 新闻评论
     * @param adapter
     */
    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.2.回顾一下新闻评论的视图绑定

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);
        }
    }
}

  需要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.jasonjan.headnews.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.获取头条回答正文

 /**
     * 获取头条问答回答正文
     */
    @GET
    @Headers("User-Agent:" + Constant.USER_AGENT_MOBILE)
    Observable<ResponseBody> getWendaAnsDetail(@Url String url);

  传进去一个url。

  传出来一个Observable<ResponseBody>。

6.2.整体问答API请求

package com.jasonjan.headnews.api;

import com.jasonjan.headnews.bean.wenda.WendaArticleBean;
import com.jasonjan.headnews.bean.wenda.WendaContentBean;
import com.jasonjan.headnews.global.Constant;

import io.reactivex.Observable;
import okhttp3.ResponseBody;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.GET;
import retrofit2.http.Headers;
import retrofit2.http.POST;
import retrofit2.http.Query;
import retrofit2.http.Url;

/**
 * Created by JasonJan on 2017/12/11.
 */

public interface IMobileWendaApi {
    /**
     * 获取头条问答标题等信息
     * http://is.snssdk.com/wenda/v1/native/feedbrow/?category=question_and_answer&wd_version=5&count=20&max_behot_time=1495245397?iid=10344168417&device_id=36394312781
     *
     * @param maxBehotTime 时间轴
     */
    @GET("http://is.snssdk.com/wenda/v1/native/feedbrow/?iid=10344168417&device_id=36394312781&category=question_and_answer&wd_version=5&count=20&aid=13")
    Observable<WendaArticleBean> getWendaArticle(
            @Query("max_behot_time") String maxBehotTime);


    /**
     * 获取头条问答优质回答
     * 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);


    /**
     * 获取头条问答优质回答(加载更多)
     * 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);

    /**
     * 获取头条问答普通回答
     * 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);

    /**
     * 获取头条问答普通回答(加载更多)
     * 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);

    /**
     * 获取头条问答回答正文
     */
    @GET
    @Headers("User-Agent:" + Constant.USER_AGENT_MOBILE)
    Observable<ResponseBody> getWendaAnsDetail(@Url String url);
}


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