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 + "\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

<?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的支持

  请参考这篇文章:使用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>\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);
}

TouTiao开源项目 分析笔记20 问答详情的更多相关文章

  1. TouTiao开源项目 分析笔记19 问答内容

    1.真实页面预览 1.1.成果预览 首先是问答列表 然后每个item设置点击事件,进入问答内容列表 然后每一个问答内容也设置点击事件,进入问答详情 1.2.触发事件. 在WendaArticleOne ...

  2. TouTiao开源项目 分析笔记15 新闻详情之两种类型的实现

    1.预览效果 1.1.首先看一下需要实现的效果. 第一种,文字类型新闻. 第二种,图片类型新闻. 1.2.在NewsArticleTextViewBinder中设置了点击事件 RxView.click ...

  3. TouTiao开源项目 分析笔记18 视频详情页面

    1.效果预览 1.1.需要做到的真实效果 1.2.触发的点击事件 在MediaArticleVideoViewBinder的每一个item点击事件中: VideoContentActivity.lau ...

  4. TouTiao开源项目 分析笔记2

    1.Constant常量定义类 1.1.源代码 public class Constant { public static final String USER_AGENT_MOBILE = " ...

  5. TouTiao开源项目 分析笔记9 实现一个问答主页面

    1.根据API返回创建几个基础的Bean 1.1.WendaArticleDataBean类 API返回的数据如下: /** * cell_type : 36 * extra : {"wen ...

  6. TouTiao开源项目 分析笔记12 从总体到局部 构建视频主页面

    1.构建视频主列表的整体碎片VideoTabLayout 1.1.首先创建一个VideoTabLayout package com.jasonjan.headnews.module.video; im ...

  7. TouTiao开源项目 分析笔记17 新闻媒体专栏

    1.效果预览 1.1.要实现的效果 1.2.如何调转到新闻媒体专栏 点击右上角的用户图标. 在新闻详情页面的Fragment的菜单点击事件中触发. case R.id.action_open_medi ...

  8. TouTiao开源项目 分析笔记10 实现通用普通文章片段页面

    1.RxJava的Observable数据操作符总结 1.1.Map操作符 Map操作符对原始Observable发射的没一项数据应用一个你选择的函数, 然后返回一个发射这些结果的Observable ...

  9. TouTiao开源项目 分析笔记6

    1.NewsChannelBean简单类笔记 1.1.Comparable接口的实现和使用 参考文章:Comparable接口的实现和使用. 因为NewsChannelBean实现了Comparabl ...

随机推荐

  1. 安装Android模拟器Genymotion【Android学习入门】

    安装Android模拟器Genymotion 推荐教程:一个强大的Android模拟器Genymotion具体内容如下: 相信很多Android开发者一定受够了速度慢.体验差效率及其地下的官方模拟器了 ...

  2. 【Node.js】Stream(流)的学习笔记

    最近学习使用Node.js创建http proxy server,少不了要跟Stream打交道.昨天开始查阅一些资料,多少有了一些粗浅了解.整理在这里,供学习之用. 从Node.js API文档中可知 ...

  3. 【Node.js】初识Node.js

    因组里项目需要,我和另外一名同事要学习Node.js.之前接触过Javascript,都是前台处理html时用到,现在要用Javascript做后端,学习Node.js,用一段时间专心学习一门新技术, ...

  4. appium (五)desired_caps参数

     转自:http://blog.csdn.net/Yejianyun1/article/details/56279051   一.介绍 在appium server 与手机端建立会话关系时,手机端需要 ...

  5. js有限状态机

    http://www.ruanyifeng.com/blog/2013/09/finite-state_machine_for_javascript.html 有限状态机(Finite-state m ...

  6. 变更hostname

    具有dns解析的主机名 # vim /etc/sysconfig/network ... HOSTNAME=webserver.mydomain.com ... # hostname webserve ...

  7. CRM, C4C和Hybris的工作流简介

    CRM的例子 Step by Step to debug IC inbox workflow WS14000164 C4C Custom recipient determination in work ...

  8. querystring处理参数小利器

    相信上一章的讲解,相信大家对url地址有一个更直观的认识,在url解析的时候可以用querystring这样一个module替换,然后对这个query集成一个对象,这里不管是前端开发还是后端开发,都常 ...

  9. 问题 A: xiaoping学构造函数

    题目描述 xiaoping刚接触类的构造和析构函数,对于构造函数的编写比较困惑.zhuangzhuang给小平布置了一道题目,xiaoping苦思两天也无法解答,请你帮帮xiaoping吧. #inc ...

  10. RandomUserAgentMiddleware练习

    # 请求头添加随机user-agent class RandomUserAgentMiddleware(object): def __init__(self, agents): self.agent ...