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.片段源代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 + "\n" + 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; } }); } }
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
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<?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>
预览图片:
4.问答详情处理器
4.1.需要第三方库Jsoup的支持
在build.gradle中这样配置:
// 解析HTML implementation 'org.jsoup:jsoup:1.10.2'
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>\n" + "<html lang=\"en\">\n" + "<head>\n" + " <meta charset=\"UTF-8\">" + css + "<body>\n" + "<article class=\"article-container\">\n" + " <div class=\"article__content article-content\">" + content + " </div>\n" + "</article>\n" + "</body>\n" + "</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>\n" + "<html lang=\"en\">\n" + "<head>\n" + " <meta charset=\"UTF-8\">" + css + "<body>\n" + "<article class=\"article-container\">\n" + " <div class=\"article__content article-content\">" + content + " </div>\n" + "</article>\n" + "</body>\n" + "</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); }