极简的MVP设计模式配合UI框架
MVP可以实现视图和数据的解耦
image
如图所示 MVP使M与V分离 实现了解耦
定义BaseView 所有视图的通用接口(activity fragment)
public interface BaseView {
/**
* 初始化数据
*/
void loadData();
/**
* 更新View的状态
* @param state
*/
void updateState(int state);
}
updateState整个方法是由我们来根据网络返回的数据或者来判断整个View的状态,定义了三个状态值,由此来展示loading页面 或者 error的页面
//加载状态
public static final int STATUS_LOADING = 100;
//成功状态
public static final int STATUS_SUCCESS = 101;
//失败状态
public static final int STATUS_ERROR = 102;
接着去定义通用的接口BaseViewImpl
public interface BaseViewImpl<D> extends BaseView{
void attachPre();
void showData(List<D> list);
}
attachPre 由View来关联相应的Presenter 可以在定义的BaseActivity里调用绑定Presenter
BasePresenter
public interface BasePresenter<V> {
/**
* 绑定View
* @param view
*/
void attachView(V view);
/**
* 解绑View
*/
void detachView();
}
BasePresenter的实现类 基类
public abstract class BasePresenterImpl<V extends BaseView> implements BasePresenter<V>{
public Context mContext;
public V mBaseView;
private CompositeSubscription mCompositeSubscription;
public BasePresenterImpl(Context context){
mContext = context;
}
@Override
public void attachView(V view) {
mBaseView = view;
}
@Override
public void detachView() {
this.mBaseView = null;
onUnsubscribe(); //解绑
}
/**
* 加载数据
*/
public abstract void loadData();
//RXjava取消注册,以避免内存泄露
public void onUnsubscribe() {
if (mCompositeSubscription != null && mCompositeSubscription.hasSubscriptions()) {
mCompositeSubscription.unsubscribe();
}
}
public void addSubscription(Observable observable, Subscriber subscriber) {
if (mCompositeSubscription == null) {
mCompositeSubscription = new CompositeSubscription();
}
mCompositeSubscription.add(subscriber);
}
}
在BasePresenterImpl里绑定View ,由此子类可以根据View来更新状态
MVP就是基类的实现就是这么简单,现在来创建activity来试试手, 首先创建一个BaseActivity,抽取一些公共的代码
public abstract class BaseActivity<T extends BasePresenter,D> extends AppCompatActivity implements BaseViewImpl<D>{
public T mPresenter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/**
* 绑定Presenter
*/
attachPre();
View view = initView(LayoutInflater.from(this));
setContentView(view);
ButterKnife.bind(this);
findView(view);
loadData();
}
protected abstract View initView(LayoutInflater from);
public abstract void findView(View view);
@Override
protected void onDestroy() {
super.onDestroy();
if(mPresenter != null)
mPresenter.detachView();
ButterKnife.unbind(this);
}
/**
* error的统一处理
* @param error
*/
public void error(Throwable error){
if(error instanceof NetworkErrorException){
Toast.makeText(this,"网络错误", Toast.LENGTH_SHORT).show();
}
}
}
创建HomeActivity去访问网络以及加载数据
public class HomeActivity extends BaseActivity<HomePresenter, NewsList.NewsEntity> {
@Bind(R.id.recy)
RecyclerView mRecy;
private NewsListAdapter mNewsListAdapter;
private LoadStuatus mLoadStuatus;
@Override
public View initView(LayoutInflater inflater) {
/**
* UI框架
*/
mLoadStuatus = new LoadStuatus(this);
/**
* 实现成功页面的View
*/
mLoadStuatus.addSuccessView(inflater.inflate(R.layout.activity_home, null));
/**
* 绑定BaseView来实现页面点击加载
*/
mLoadStuatus.bindView(this);
return mLoadStuatus;
}
@Override
public void findView(View view) {
mRecy.setLayoutManager(new LinearLayoutManager(this));
mNewsListAdapter = new NewsListAdapter(this);
mRecy.setAdapter(mNewsListAdapter);
}
@Override
public void attachPre() {
mPresenter = new HomePresenter(this);
mPresenter.attachView(this);
}
@Override
public void showData(List<NewsList.NewsEntity> list) {
mNewsListAdapter.flush(list);
}
@Override
public void loadData() {
mPresenter.loadData();
}
/**
* 更新页面
*/
@Override
public void updateState(int state) {
mLoadStuatus.updateView(state);
}
}
这里的loadStuatus是一个帧布局用来存放我们的三个页面
errorPage,LoadPage,SuccessPage都是根据P层的回调来实现
在来看一下P层的实现
public class HomePresenter extends BasePresenterImpl<HomeActivity>{
public HomePresenter(Context context) {
super(context);
}
@Override
public void loadData() {
Subscription subscribe = RetrofitUtils.getinstance(mContext).buildNews().getNews()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(bean -> mBaseView.showData(bean.getNews()), //onNext() 去更新页面
error -> {
mBaseView.error(error); // 请求网络失败 在BaseActivity定义了公共的处理方法
mBaseView.updateState(LoadStuatus.STATUS_ERROR); //更新页面 显示失败的页面
},
() -> mBaseView.updateState(LoadStuatus.STATUS_SUCCESS));// onComplete 完成页面更新,显示成功的页面
addSubscription(subscribe);
}
}
P层很简单 就是访问网络 当请求
接下来看一下我们的UI框架 定义了统一处理页面状态的状态码以及如何去正确的显示页面
public class LoadStuatus extends FrameLayout {
private static final String TAG = "TAG";
BaseView nView;
@Bind(R.id.error)
View mErrorView;
@Bind(R.id.loading)
View mLoadingView;
View successView;
//加载状态
public static final int STATUS_LOADING = 100;
//成功状态
public static final int STATUS_SUCCESS = 101;
//失败状态
public static final int STATUS_ERROR = 102;
public LoadStuatus(Context context) {
this(context, null);
}
public LoadStuatus(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public LoadStuatus(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public void addSuccessView(View view){
successView = view;
addView(successView);
successView.setVisibility(GONE);
}
public void bindView(BaseView view) {
this.nView = view;
mErrorView.setOnClickListener(v -> {
if(nView == null)
throw new NoBindViewException("没有绑定View");
nView.loadData();
});
}
public void init() {
View.inflate(getContext(), R.layout.status, this);
ButterKnife.bind(this);
}
public void updateView(int currentStatus) {
switch (currentStatus) {
case STATUS_ERROR:
successView.setVisibility(GONE);
mErrorView.setVisibility(VISIBLE);
mLoadingView.setVisibility(GONE);
break;
case STATUS_LOADING:
successView.setVisibility(GONE);
mErrorView.setVisibility(GONE);
mLoadingView.setVisibility(VISIBLE);
break;
case STATUS_SUCCESS:
successView.setVisibility(VISIBLE);
mErrorView.setVisibility(GONE);
mLoadingView.setVisibility(GONE);
break;
}
}
public class NoBindViewException extends RuntimeException{
public NoBindViewException(String info){
super(info);
}
}
}
github地址: 点此进入
网友评论