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. 转:清除arcsde空间垃圾数据以及解决sde图层名称被占用的问题

    因为对空间数据管理的不善(非法的删除.重命名等),导致sde中存在一些垃圾数据.和图层名称被占用,这种问题已经有好几个同事问我怎么解决了?现把这个问题已经解决了,下面将整个详细过程写出来,共享给碰到同 ...

  2. django 定制admin

    https://www.cnblogs.com/liwenzhou/p/9519321.html

  3. Azure 1 月新公布

    Azure 1 月新发布:Microsoft Power BI Embedded 公共预览和计算机视觉 API 标准版的更新以及 Azure IoT 网关 SDK 和中心设备管理新功能正式发布以及关于 ...

  4. 在C++Builder中定义事件的实现方法

    ++Builder是由Borland公司推出的一款可视化集成开发工具.C++Builder的集成开发环境(IDE)提供了一系列可视化快速应用程序开发(RAD)工具,让程序员可以很轻松地建立和管理自己的 ...

  5. webpack整体了解

    一.下载 新建一个文件夹,在cmd中npm init->npm install->npm install webpack --save-dev 下载完成之后,新建一个webpack.con ...

  6. P1540 机器翻译

    题目背景 小晨的电脑上安装了一个机器翻译软件,他经常用这个软件来翻译英语文章. 题目描述 这个翻译软件的原理很简单,它只是从头到尾,依次将每个英文单词用对应的中文含义来替换.对于每个英文单词,软件会先 ...

  7. theano提示:g++ not detected的解决办法

    导入theano包后出现如下警告: WARNING (theano.configdefaults): g++ not detected ! Theano will be unable to execu ...

  8. 第15章 RCC—使用HSE/HSI配置时钟—零死角玩转STM32-F429系列

    第15章     RCC—使用HSE/HSI配置时钟 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku. ...

  9. jeDate日期控件

    http://www.jayui.com/jedate/     这是日期控件官网,可以去里面下载使用 前台 <%@ Page Language="C#" AutoEvent ...

  10. (转)ActionContext和ServletActionContext

    前面已经了解到ActionContext是Action执行时的上下文,里面存放着Action在执行时需要用到的对象,我们也称之为广义值栈. Struts2在每次执行Action之前都会创建新的Acti ...