Android实现文章+评论(MVP,RxJava,Dagger2,ButterKnife)简介这个项目主要有两个功能,一个加载网页/文章,另一个用来显示评论。
并应用了MVP模式,Dagger2、RxJava、ButterKnife等开源框架。
demo结构首先来看一下布局文件:<android.support.design.widget.CoordinatorLayoutxmlns:android="/apk/res/android"xmlns:app="/apk/res-auto"xmlns:tools="/tools"android:background="#ffffff"android:layout_width="match_parent"android:layout_height="match_parent"android:fitsSystemWindows="true"tools:context="com.dean.articlecomment.article.ArticleActivity"><com.dean.articlecomment.ui.XAppBarLayoutandroid:id="@+id/app_bar"android:layout_width="match_parent"android:layout_height="wrap_content"android:fitsSystemWindows="true"android:theme="@style/AppTheme.AppBarOverlay"><android.support.v7.widget.Toolbarandroid:id="@+id/toolbar"android:fitsSystemWindows="true"android:layout_width="match_parent"android:layout_height="?attr/actionBarSize"app:layout_scrollFlags="scroll|enterAlways"app:popupTheme="@style/AppTheme.PopupOverlay" /></com.dean.articlecomment.ui.XAppBarLayout><include layout="@layout/content_scrolling" /><include layout="@layout/article_bottom_view" /></android.support.design.widget.CoordinatorLayout>toolbar在显示网页文章时是仿知乎的操作,向下滑动时隐藏toolbar和屏幕下方发表评论的视图,向上滚动时再显示。
toolbar的显示隐藏是通过设置其scrollFlags属性实现的。
enterAlways:向上滑时toolbar隐藏,向下滑动即展示。
enterAlwaysCollapsed:向上滑时toolbar隐藏,向下滑动直到NestedScrollView的底部时toolbar才展示。
exitUntilCollapsed:当你定义了一个minHeight,这个view将在滚动到达这个最小高度的时候消失。
snap:突然折断的意思,效果同enterAlwaysCollapsed,区别为滚动时手指离开屏幕时toolbar不会显示一半的状态,显示的部分大于一半时即全漏出来,小于一半时即隐藏掉。
article_bottom_viewarticle_bottom_view是屏幕下方的评论条,它的隐藏显示与toolbar同步,使用方式是通过AppBarLayout.OnOffsetChangedListener的状态监听与动画实现的。
@Overridepublic void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) { if (verticalOffset >= 0) {if (xAppBarListener != null) {xAppBarListener.onFingerDown();}} else {if (xAppBarListener != null) {xAppBarListener.onFingerUp();}}}content_scrollingcontent_scrolling布局如下:<com.dean.articlecomment.ui.XNestedScrollViewxmlns:android="/apk/res/android"xmlns:app="/apk/res-auto"xmlns:tools="/tools"android:id="@+id/scrollView"android:layout_width="match_parent"android:layout_height="match_parent"app:layout_behavior="@string/appbar_scrolling_view_behavior"tools:showIn="@layout/activity_scrolling"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"><!--详细--><FrameLayoutandroid:id="@+id/article_content_view"android:layout_width="match_parent"android:layout_height="wrap_content"></FrameLayout><!--评论--><FrameLayoutandroid:id="@+id/comment_content_view"android:layout_width="match_parent"android:layout_height="wrap_content"></FrameLayout></LinearLayout></com.dean.articlecomment.ui.XNestedScrollView>NestedScrollView中嵌套两个视图article_content_view,comment_content_view。
分别是用于显示文章Fragment视图和评论fragment视图。
文章Fragment文章Fragment中使用Webview来显示网页/文章。
Webview使用了腾讯的X5WebView,并在外层封装一个加载用的进度条。
评论fragment文章Fragment中使用了RecycleView(根据XRecyclerView改造)来显示添加评论,并且可以进行滑动加载更多。
值得注意的是NestedScrollview中嵌套RecycleView的问题,解决方法是:使用Android Support Library 23.2.0以上,设置layoutManager.setAutoMeasureEnabled(true);将recyclerView的高度设置为wrap_content设置recyclerView.setNestedScrollingEnabled(false)避免和NestedScrolling的滑动冲突。
由于禁用了recyclerView的滚动,所以在实现底部加载更多的时候需要监听外层的NestedScrollingViewMVP本Demo使用了MVP模式(关于MVP的文章网上很多,我这里就不过多介绍),主要借鉴了下面3个开源项目。
并作了一些改动。
googlesamples/android-architectureJessYanCoding/MVPArmscodeestX/GeekNews(主要参考)大多数MVP模式里都是View持有Presenter的引用。
一个fragment对应一个页面,一个页面对应一个Presenter,因此如果一个功能中页面较多时会导致逻辑复杂以及代码文件的增加。
我这里的处理是反过来使Presenter持有View的引用,即一个Activity持有一个Presenter,每个Fragment是一个View,用一个Presenter持有所有的View引用。
所有的逻辑和业务代码都放在Presenter中处理,Activity和Fragment只负责页面的显示。
这样的好处是结构简单,逻辑比较清晰,方便在多个view中交互操作。
缺点就是会导致Presenter中代码量过大。
代码如下:public class ArticlePresenter extends RxPresenter implements ArticleContract.Presenter {protected final ArticleContract.ArticleView articleView;protected final mentView commentView;protected final ArticleContract.View bottomView;@Injectpublic ArticlePresenter(ArticleContract.ArticleView articleView, mentView commentView, ArticleContract.View bottomView) {this.articleView = articleView;mentView = commentView;this.bottomView = bottomView;}@Injectvoid setupListeners() {// view中注入presenterarticleView.setPresenter(this);commentView.setPresenter(this);bottomView.setPresenter(this);}}Contract代码如下:public interface ArticleContract {interface Presenter extends BasePresenter {void addComment();void showBottomView();void hideBottomView();void onLoadingArticle();void onLoadingComment();void onLoadingMoreComment();void onLoadingArticleSuccess();void onLoadingArticleFailed();}interface CommentView extends BaseView<Presenter> {void showComments(ArrayList<ArticleComment> comments);void ments(ArrayList <ArticleComment> comments);void addComment(ArticleComment comment);void onScrollToPageEnd();}interface ArticleView extends BaseView<Presenter> {void showArticle(String url);}interface View extends BaseView<Presenter> {void showBottomView();void hideBottomView();void goToComment();void goToArticle();}}Rxjava/RxAndroidReactiveX/RxJavaReactiveX/RxAndroidRxjava也是最近才知道。