从零开始搭建一个完善的MVP开发框架(四) —对View(Activity,Fragment等)层组件进行封装简化View层的开发
2017-06-28 20:17
1321 查看
摘要: 通过上面三篇文章所提到的关于MVP框架的封装,我们已经能够大大简化MVP模式中MP层的开发流程。但是还有一个问题,就是在开发的时候我们的 View层组件还需要处理较多的事情,例如错误处理,进度条显示等。所以我们需要对View层的组建进行封装,优化开发的流程。
笔者根据封装好的MVP的特点,对View层的组建进行了不同的封装。里面包含2个Activity、两个Fragment和一个Adapter基类。它们的名称分别是:MvpActivity 、MvpListActivity、MvpFragment、MvpListFragment、BaseListAdapter。它们的目的都是为了优化项目的代码和结构,加快项目的开发速度。
在这里笔者只对MvpActivity 这个类进行解析,其他的可以通过查看笔者github上的源码来学习实现方式。MvpActivity中封装了前面我们提到的一些公共方法,它的作用是结合我们前面封装好的BasePresenter使用,实现对项目进行优化的目的。使用普通的presenter获取数据的activity可以通过继承这个类来减少需要自己手动实现的代码。
如果读者想进一步了解其他几个组件的具体实现的话,请到笔者的github上下载。
笔者在开发这个框架的时候是采用Material Design风格进行开发的,采用这个风格进行开发的时候会有一个问题,就是每个Activity都需要重新实现一个toolbar。所以对Activity进行优化的第一项任务就是解决这个问题。
我们先来看看MvpActivity的界面实现:
activity_mvp.xml:
上面的代码和我们平时的Material Design页面布局时一样的。下面我们来看下
从布局可以看到,
就好像下面这样:
这个onCreateView就是在子Activity中需要实现的方法,和fragment的onCreateView使用方法是一样的。而contentViewGroup就是我们上面说的fragment。
这样就可以通过MvpActivity基类简化这一步操作。
IMvpActivity的代码如下:
IMvpActivity继承了IActivity接口,下面我们来看看IActivity接口的代码
IActivity:
上面的两个接口中大部分方法都十分简单,只是对activity执行一些初始化操作而已。其中setProgressType(int type)方法是用于设置我们在使用presenter发起获取数据请求的时候显示的进度条样式的。在这里笔者定义了三种样式,分别由dialog样式,默认样式和SwipeRefreshLayout样式。这个进度条样式在收到
boolean show)的时候会显示/隐藏进度条。
我们先来看看IMvpView接口的定义:
可以看到,这就是我们前面的文章中提到的IBaseView接口,这个接口的主要是用于处理错误回调与进度条的显示/隐藏。
下面我们来看看几个方法的实现:
showProgress(boolean show)
由于我们已经生成了不同样式的进度条实例,所以我们在这里根据设置的进度条样式来显示具体的进度条。这里需要注意的是,为了更好的人机交互体验,我们要根据不同的进度条样式来选择不同重连机制的处理方式,这里分别实现了点击错误提示重连和下拉重连,更多的实现读者可以自己进行拓展。
showNetworkError和showServerError
可以看到这两个错误回调的最终实现时onError方法,下面是onError的代码:
错误信息的展现形式也是和进度条的样式有关的,这样做是为了优化交互体验。下面我们来看看这三种不同设置的实际效果。
Demo展示图片
前几篇文章中,关于BasePresenter的设计有一个小小的补充,这是由于笔者编写代码的时候的一个小的bug。
错误代码:
错误的地方在于关于baseModel的调用,在这里我们应该使用getModel获取Model实例。为什么要这样做呢?因为BasePresenter默认使用的是VolleyModle,当在子Presenter需要使用其它类型的Model的时候,可以通过覆盖getModel()这个方法来重设Modle实例。
修改后的代码为:
限于篇幅,笔者在这里只是对MvpActivity作了一个简单的介绍,但是核心思想是一样的,通过对一些公共方法的封装(不同activity的初始化,错误处理,进度条的现实隐藏都是类似的),实现简化我们的代码的目的。这个项目涉及的技术点较多,但是都是相对简单的,所以笔者在这里就不对其他view组件展开讨论了。
对View组建进行优化
笔者根据封装好的MVP的特点,对View层的组建进行了不同的封装。里面包含2个Activity、两个Fragment和一个Adapter基类。它们的名称分别是:MvpActivity 、MvpListActivity、MvpFragment、MvpListFragment、BaseListAdapter。它们的目的都是为了优化项目的代码和结构,加快项目的开发速度。在这里笔者只对MvpActivity 这个类进行解析,其他的可以通过查看笔者github上的源码来学习实现方式。MvpActivity中封装了前面我们提到的一些公共方法,它的作用是结合我们前面封装好的BasePresenter使用,实现对项目进行优化的目的。使用普通的presenter获取数据的activity可以通过继承这个类来减少需要自己手动实现的代码。
如果读者想进一步了解其他几个组件的具体实现的话,请到笔者的github上下载。
处理Toolbar
笔者在开发这个框架的时候是采用Material Design风格进行开发的,采用这个风格进行开发的时候会有一个问题,就是每个Activity都需要重新实现一个toolbar。所以对Activity进行优化的第一项任务就是解决这个问题。我们先来看看MvpActivity的界面实现:
activity_mvp.xml:
1234567891011121314151617181920212223242526272829303132333435 | <?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:layoutwidth="matchparent" android:layoutheight="matchparent" android:fitsSystemWindows="true" tools:context="com.mvp.framework.module.base.view.activity.MvpActivity"> <android.support.design.widget.AppBarLayout android:layoutwidth="matchparent" android:layoutheight="wrapcontent" android:theme="@style/AppTheme.AppBarOverlay"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layoutwidth="matchparent" android:layoutheight="?attr/actionBarSize" android:background="?attr/colorPrimary" app:popupTheme="@style/AppTheme.PopupOverlay" /> </android.support.design.widget.AppBarLayout> <include layout="@layout/contentmvp" /> <android.support.design.widget.FloatingActionButton android:id="@+id/fab" android:layoutwidth="wrapcontent" android:layoutheight="wrapcontent" android:layoutgravity="bottom|end" android:layoutmargin="@dimen/fabmargin" android:visibility="gone" app:srcCompat="@android:drawable/icdialogemail" /> </android.support.design.widget.CoordinatorLayout> |
content_mvp的代码:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657 | <?xml version="1.0" encoding="utf-8"?> <android.support.v4.widget.SwipeRefreshLayout 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/refresh_layout" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:context="com.mvp.framework.module.base.view.activity.MvpActivity" tools:showIn="@layout/activity_mvp"> <FrameLayout android:id="@+id/content_base" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" > <ProgressBar android:id="@+id/base_progress_bar" android:layout_width="64dp" android:layout_height="wrap_content" android:layout_gravity="center" android:visibility="gone" style="?android:attr/progressBarStyle" /> <LinearLayout android:id="@+id/base_error_layout" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" android:visibility="gone" android:layout_gravity="center" android:gravity="center"> <ImageView android:id="@+id/base_error_img" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@mipmap/ic_launcher"/> <TextView android:id="@+id/base_error_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="网络连接错误,点击从新连接"/> </LinearLayout> </FrameLayout> </android.support.v4.widget.SwipeRefreshLayout> |
content_mvp中包含了SwipeRefreshLayout、ProgressBar、和显示错误的layout等。那么是如何实现让子Activity不需要处理toolbar的呢?笔者这里用了个简单暴力的办法。你看见上面布局中的FrameLayout了吗?看见了对吧?我就是用FrameLayout的addView方法把子Activity中的布局加进去的。
就好像下面这样:
1234 | contentView = onCreateView(getLayoutInflater(),contentViewGroup,savedInstanceState); FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams( FrameLayout.LayoutParams.MATCHPARENT,FrameLayout.LayoutParams.MATCHPARENT); contentViewGroup.addView(contentView,lp);` |
这样就可以通过MvpActivity基类简化这一步操作。
实现IMvpActivity接口
IMvpActivity的代码如下:12345678910111213141516171819202122232425262728293031323334 | public interface IMvpActivity extends IActivity{ / @Method: onCreateView @author create by Tang @date date 16/10/20 下午2:32 @Description: 创建子activity布局 / @NonNull View onCreateView(LayoutInflater inflater,ViewGroup container,Bundle savedInstanceState); / @Method: onReconnection @author create by Tang @date date 16/10/20 下午4:00 @Description: 重新连接 这里要注意的是,如果一个页面里面有多个获取数据的presenter, 需要确认获取数据失败的presenter 具体需要根据实际业务来处理 / void onReconnection(); / @Method: setProgressType @author create by Tang @date date 16/10/25 上午10:54 @Description: 设置加载进度的样式 初始化为默认模式, 在子类中调用该方法可以重设进度条的样式 / void setProgressType(int progressType); } |
IActivity:
12345678910111213141516171819202122 | public interface IActivity { / @Method: setTitle @author create by Tang @date date 16/10/20 上午11:06 @Description: 设置页面标题 / void setTitle(String title); / @Method: setDisplayHomeAsUpEnabled @author create by Tang @date date 16/10/20 上午11:16 @Description: ActionBar上是否显示返回按钮 / @NonNull boolean setDisplayHomeAsUpEnabled(); FloatingActionButton getFloatingActionButton(); } |
IMvpView接口的showProgress(final
boolean show)的时候会显示/隐藏进度条。
实现IMvpView接口
我们先来看看IMvpView接口的定义:1234567891011 | public interface IMvpView extends IBaseMvpView{ / @Method: isSucceed @author create by Tang @date date 16/10/26 下午4:33 @Description: 通知View层获取数据成功 在获取简单数据的时候用到 / void showSucceed(boolean isSucceed); } |
12345678910111213141516171819202122232425262728 | public interface IBaseMvpView { / @Method: showProgress @author create by Tang @date date 16/10/26 上午11:06 @Description: 是否显示加载进度 / void showProgress(final boolean show); / @Method: showNetworkError @author create by Tang @date date 16/10/26 上午11:08 @Description: 网络连接错误时Presenter层会调用该方法通知View层 / void showNetworkError(int errorCode, String errorDesc, String ApiInterface); / @Method: showServerError @author create by Tang @date date 16/10/26 上午11:09 @Description: 服务器返回错误时Presenter层会调用该方法通知View层 / void showServerError(int errorCode, String errorDesc); } |
下面我们来看看几个方法的实现:
showProgress(boolean show)
12345678910111213141516171819202122232425262728293031323334 | @Override public void showProgress(boolean show) { switch (progressType){ case PROGRESSTYPEDEFAULT: if (show){ if (contentView != null){ contentView.setVisibility(View.GONE); } defaultProgress.setVisibility(View.VISIBLE); }else { defaultProgress.setVisibility(View.GONE); if(contentView != null){ contentView.setVisibility(View.VISIBLE); } } break; case PROGRESSTYPEDIALOG: if(show){ dialogProgress.show(); }else { dialogProgress.dismiss(); } break; case PROGRESSTYPEDROPDOWN: refreshLayout.setRefreshing(show); break; } } |
showNetworkError和showServerError
12345678910111213 | @Override public void showNetworkError(int errorCode, String errorDesc, String ApiInterface) { showProgress(false); LogUtil.e(getClass(), "showNetworkError: " + ApiInterface); onError(errorCode,errorDesc); } @Override public void showServerError(int errorCode, String errorDesc) { onError(errorCode,errorDesc); } |
1234567891011121314151617181920212223242526272829303132333435363738394041424344 | @Override public void onError(int errorCode, String errorDesc) { showProgress(false); LogUtil.e(getClass(), "showServerError: error code = " + errorCode + " & error desc = " + errorDesc); switch (progressType) { case PROGRESSTYPEDEFAULT: if (setErrorImageResource() != 0) { errorImage.setImageDrawable(getResources().getDrawable(setErrorImageResource())); } if (!TextUtils.isEmpty(errorDesc)) { errorText.setText(errorDesc); } contentView.setVisibility(View.GONE); errorLayout.setVisibility(View.VISIBLE); break; case PROGRESSTYPEDIALOG: contentView.setVisibility(View.GONE); errorLayout.setVisibility(View.VISIBLE); break; case PROGRESSTYPEDROPDOWN: if (setErrorImageResource() != 0) { errorImage.setImageDrawable(getResources().getDrawable(setErrorImageResource())); } if (!TextUtils.isEmpty(errorDesc)) { errorText.setText(errorDesc); } if (!isSucceed) { contentView.setVisibility(View.GONE); errorLayout.setVisibility(View.VISIBLE); }else { Snackbar.make(fab,errorDesc,Snackbar.LENGTHSHORT).show(); } break; } } |
Demo展示图片
对前面文章的补充
前几篇文章中,关于BasePresenter的设计有一个小小的补充,这是由于笔者编写代码的时候的一个小的bug。错误代码:
1234567891011 | @Override public void accessServer(Params params) { this.params = params; mvpView.showProgress(true); baseModel.sendRequestToServer(); } @Override public void cancelRequest() { baseModel.cancelRequest(); } |
修改后的代码为:
1234567891011 | @Override public void accessServer(Params params) { this.params = params; mvpView.showProgress(true); getModel().sendRequestToServer(); } @Override public void cancelRequest() { getModel() cancelRequest(); } |
小结
限于篇幅,笔者在这里只是对MvpActivity作了一个简单的介绍,但是核心思想是一样的,通过对一些公共方法的封装(不同activity的初始化,错误处理,进度条的现实隐藏都是类似的),实现简化我们的代码的目的。这个项目涉及的技术点较多,但是都是相对简单的,所以笔者在这里就不对其他view组件展开讨论了。
相关文章推荐
- 从零开始搭建一个完善的MVP开发框架(二),通过泛型和抽象,简化MVP框架。
- 从零开始搭建一个完善的MVP开发框架(三),对列表型数据请求进行抽象,优化列表型数据的处理
- 从零开始搭建一个完善的MVP开发框架
- 从零开始搭建一个完善的MVP开发框架(五),通过组件化开发优化项目的结构
- 从零开始搭建 一个完善的 MVP模式开发框架(一),MVP模式的简单介绍篇
- 安卓项目快速开发框架, MVP + Retrofit + RxJava,Activity 和 Fragment 结合 MVP 模式的完整封装,大大减少代码量
- Android开发从一个activity设置跳转到另一个activity中的一个fragment中的一个viewpager中的某一个页面
- 手把手教你如何搭建一个自己的安卓快速开发框架之BaseActivity(一)
- Activity,Fragment的基类封装,简化findViewById,Fragment懒加载和不重复加载等
- Activity处于后台Handler进行UI操作View组件的isShow()方法慎用
- Android游戏开发6:SurfaceView中添加组件并进行数据交互的方法
- 用.Net打造一个移动客户端(Android/IOS)的服务端框架NHM(三)——搭建Android开发环境,用Hibernate生成Android项目的Model层
- Android快速开发框架Android_BaseLib,集成了常用工具类,自定义View控件,Base基类封装,常用开源框架
- 【iOS7开发笔记】tableview之使用xib封装一个view的步骤
- 使用Jquery+EasyUI进行框架项目开发案例解说之中的一个---员工管理源代码分享
- 使用Jquery+EasyUI进行框架项目开发案例解说之中的一个---员工管理源代码分享
- OpenGL + Win32 SDK 开发框架的搭建(C++语言版)(先补上一个问题!!)
- activity怎么控制fragment中的textview组件
- [libgdx游戏开发教程]使用Libgdx进行游戏开发(2)-游戏框架搭建
- 使用Jquery+EasyUI进行框架项目开发案例解说之中的一个---员工管理源代码分享