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. jquery_lazyload插件

    延迟加载图片的 jQuery 插件 http://www.neoease.com/lazy-load-jquery-plugin-delay-load-image/

  2. Azure杯年会Cosplay大赛,速来围观!

    又是一年年会时,正值各家公司红人纷纷登场之际,Azure 自然也不会缺席.我们今年的主题是:Azure 杯年会 Cosplay,秀出你最爱的角色! 在过去的一年中,Azure 智能云取得了长足的发展, ...

  3. java学习笔记之基础知识

    1.class不加修饰符默认default,只在当前包里能用. 2.构造函数前面的修饰符的作用类似class的,限制引用的权限. 3.java对象的创建其实就是类的实例化,类的实例化就是在堆上copy ...

  4. windows10家庭中文版升级专业版或企业版简单方便的操作方法

    以管理员的身份运行cmd 1 ,升级到专业版输入:slmgr /ipk W269N-WFGWX-YVC9B-4J6C9-T83GX 输入slmgr.vbs -skms zh.us.to 激活 2, 升 ...

  5. Laravel Scheduling Package

    Laravel 是在 App\Console\Kernel 类中的 schedule 方法去定义所有的调度任务. iBrand 产品作为一个电商类产品,本身业务需求非常庞大和复杂,全部定义在 sche ...

  6. 为什么要使用TLSv1.2和System SSL?

    FTP 和 Telnet 正是核心联网应用程序的两个示例.为 System SSL 编程接口编码的供应商应用程序可以通过更改代码来利用这些新支持. 这是安全套接层 (SSL) 协议的最新版本,也是最为 ...

  7. MySQL入门很简单: 1 数据库概述

    1. 数据库概述 1.1 数据存储方式: 1)人工管理阶段 2)文件系统阶段: 文件系统通过文件的存储路径和文件名称访问文件中的数据 3)数据库系统阶段:Oracle, SQL Server, MyS ...

  8. C++学习之显式类型转换与运行时类型识别RTTI

    static_cast const_cast reinterpret_cast 运行时类型识别(RTTI) dynamic_cast 哪种情况下dynamic_cast和static_cast使用的情 ...

  9. libtool: Version mismatch error. 解决方法

    在编译一个软件的时候,在 ./configure 和 make  之后可能会出现如下错误: libtool: Version mismatch error.  This is libtool 2.4. ...

  10. centos 7jenkin+git 安装

    jenkins+git配置 背景:用git管理源代码,所以需要jenkins安装Git Plugin插件配置 准备: 1.linux环境git客户端 2.jenkins环境 + git plugin插 ...