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. SAP R/3 IDES 4.71 编译前后硬盘空间大小比较

    使用SGEN编译前 使用SGEN编译后

  2. System Center Configuration Manager 2016 必要条件准备篇(Part3)

    步骤3.安装SQL Server 2017 注意:在Configuration Manager服务器(CM16)上以本地管理员身份执行以下操作 按照https://go.microsoft.com ...

  3. 修改Linux中发送邮件中附件大小的限制

    方法一: 在命令中设定postfix的message_size_limit值 (但系统重启后会失效) postconf -e "message_size_limit = 20480000&q ...

  4. frame、window和dialog区别

    属性 Window Frame Dialog 模式化 不是 不是 不是(可设置) 可调大小 不可 可 可 标题栏 无 有 有 边界 无 有 有 标题 无 有 有 菜单栏 无 有 无 焦点管理器 有 有 ...

  5. Docker和宿主机操作系统文件目录互相隔离的实现原理

    我们知道在Docker容器里是无法访问到宿主操作系统的文件目录的,但这种隔离是怎么实现的呢? 其实一点也不神奇--利用了Linux系统的内部命令chroot. chroot能将进程的根目录设置成任意指 ...

  6. 关于使用Encoding转码的问题,以及StreamWriter的小应用

    StreamWriter write = new StreamWriter("../../test2.txt"); write.WriteLine("中国123巴西red ...

  7. an exception occurred while initializing the database.

    对于手动删除本地的LocalDB数据库之后出现标题所示异常的,推荐下面的命令: sqllocaldb.exe stop v11.0 sqllocaldb.exe delete v11.0 在程序包管理 ...

  8. 创建 XXXXXXXX 的配置节处理程序时出错: 请求失败

    今天碰到这个错误,之前的程序在测试的时候都没有问题,同样的程序打包通过QQ传给其他人,在XP下测试也没有问题,我的Win7系统从QQ信箱下载压缩包,解压之后执行程序就会出问题,本来还是考虑自己程序是不 ...

  9. 移动端定制软键盘样式 配置input属性的type属性

    1.当你的input输入的数据为链接地址时type属性可改为 type=url 最下面一栏就会出现.com 提高用户体验 2.当你的input输入的数据为email时type属性可改为 type=em ...

  10. C# break语句

    一.C# break语句 break语句用于终止它后面的所有循环语句,使控制流程跳转到break语句所在层的外面,以便结束本层的所有循环.如果有多个循环语句进行嵌套,break语句则会跳到它所在层的外 ...