Android|MVPArch - Android MVP 快速开发框架
MVPArch 一个可有效提高Android开发效率的MVP框架
- 封装Activity/Fragment基类-BaseActivity/BaseFragment(Fragment懒加载开关配置)
- 封装MVP模式Activity/Fragment基类-BaseMVPActivity/BaseMVPFragment,V与P层生命周期监听和绑定,解决诸多内存泄漏问题
- 使用 LoadingHelper实现可定制化的页面LCE视图
- LoadingDialog加载框定制化,可随意切换
- 使用TitleBar 实现可全局配置、页面可定制化的Title,不用每个页面写繁琐的xml代码
- 沉浸式状态栏及状态栏颜色设置
- 封装了Log、Toast,可自定义代理实现自己的Log、Toast
- 封装了图片加载器、事件通知管理器,可通过配置切换
allprojects {repositories {...
maven {
url 'https://jitpack.io' }
}
}
在你的 Module build.gradle文件中添加:
dependencies {implementation 'com.github.HHotHeart:MVPArch:v1.0.2-beta1'
}
效果图
文章图片 |
文章图片 |
文章图片 |
文章图片 |
文章图片 |
文章图片 |
- UILog、UIToast
package com.littlejerk.mvparch.util;
import android.util.Log;
import com.littlejerk.library.manager.MVPArchConfig;
import com.littlejerk.library.manager.log.UILog;
/**
* @author : HHotHeart
* @date : 2021/9/24 10:01
* @desc : 自定义代理
*/
public class CustomLogDelegate implements UILog.LogDelegate {boolean isDebug = true;
@Override
public String getTag() {return "日志Tag";
}@Override
public UILog.LogDelegate init() {//做一些初始化工作,如log日志的开关
isDebug = MVPArchConfig.getInstance().isLoggable();
return this;
}@Override
public void v(String tag, String msg, Object... obj) {if (isDebug) {Log.v(tag, msg);
}
}@Override
public void d(String tag, String msg, Object... obj) {//自己的Log库
}@Override
public void i(String tag, String msg, Object... obj) {//自己的Log库
}@Override
public void w(String tag, String msg, Object... obj) {//自己的Log库}@Override
public void e(String tag, String msg, Object... obj) {//自己的Log库}@Override
public void xml(String tag, String msg) {//自己的Log库}@Override
public void json(String tag, String msg) {//自己的Log库}@Override
public void printErrStackTrace(String tag, Throwable throwable, Object... obj) {//自己的Log库}
}
然后在Application中将代理设置给UILog
UILog.setDelegate(new CustomLogDelegate().init());
Log日志开关可通过MVPArchConfig配置
MVPArchConfig.getInstance().setLoggable(BuildConfig.DEBUG)
Toast的代理设置也一样,如
UIToast.setDelegate(UIToast.ToastDelegate delegate);
- EventManager、ILFactory
MVPArchConfig.getInstance().setImageLoader(IImageLoader imageLoader)
两者调用方式
EventManager.getBus().post(IEventBus.AbsEvent event);
ILFactory.getLoader().loadNet(ImageView target, String url, IImageLoader.HOptions options);
- LCE-T
//设置状态栏颜色、标题属性
MVPArchConfig.getInstance()
.setLightStatusBar(false)
.setStatusBarColor(Color.BLACK)
.setTitleParam(new TitleParam()
.setLeftIcon(R.drawable.ic_arrow_back_black)
.setMiddleTextSize(17f)
.setMiddleTextColor(Color.BLACK)
.setTitleBarHeight(R.dimen.title_bar_height)
.setTittleBarBgColor(Color.WHITE)
.setRightIconVisible(false)
.setBottomLineColor(Color.LTGRAY)
.setBottomLineHeight(0.5f));
//设置全局LCE
LoadingHelper.setDefaultAdapterPool(adapterPool -> {adapterPool.register(ViewType.LOADING, new GLoadingAdapter());
adapterPool.register(ViewType.ERROR, new GErrorAdapter());
adapterPool.register(ViewType.EMPTY, new GEmptyAdapter());
return Unit.INSTANCE;
});
其中GLoadingAdapter、GErrorAdapter、GEmptyAdapter是框架实现的默认全局LCE,可参考将其替换成自己项目的LCE。因为LCE-T的设置是通过LCEDelegate(实现ILCEView)实现的,要想改变代理实现,可自定义代理CustomLCEDelegate实现ILCEView接口,然后通过清单文件AndroidManifest.xml去配置自定义的代理,如
package com.littlejerk.mvparch.util;
import android.content.Context;
import android.text.TextUtils;
import android.view.View;
import com.dylanc.loadinghelper.LoadingHelper;
import com.dylanc.loadinghelper.ViewType;
import com.kaopiz.kprogresshud.KProgressHUD;
import com.littlejerk.library.manager.lcet.ITitleView;
import com.littlejerk.library.manager.lcet.TitleBarAdapter;
import com.littlejerk.library.manager.lcet.ILCEView;
import org.json.JSONObject;
/**
* @author : HHotHeart
* @date : 2021/7/9 17:36
* @desc : 自定义LCE代理类,需在清单文件注册meta
*/
public class CustomLCEDelegate implements ILCEView {private Context mContext = null;
private View mRealRootView = null;
//加载中、加载失败、空布局视图 https://github.com/DylanCaiCoding/LoadingHelper
private LoadingHelper mLoadingHelper = null;
//加载框 https://github.com/Kaopiz/KProgressHUD
private KProgressHUD mKProgressHUD = null;
public CustomLCEDelegate(View rootView) {mContext = rootView.getContext();
mLoadingHelper = new LoadingHelper(rootView);
mRealRootView = mLoadingHelper.getDecorView();
}/**
* 获取真正的RootView
*
* @return
*/
@Override
public View getRealRootView() {return mRealRootView;
}/**
* 设置标题
* 如果页面滑动对标题有动作,不建议使用LoadingHelper设置标题
*
* @param titleParam
*/
@Override
public void setTitleBar(ITitleView titleParam) {mLoadingHelper.register(ViewType.TITLE, new TitleBarAdapter(titleParam));
mLoadingHelper.setDecorHeader(ViewType.TITLE);
}/**
* 空数据视图
* 调用此方法确保mLoadingHelper注册对应的Adapter
*/
@Override
public void stateEmptyView() {mLoadingHelper.showEmptyView();
}/**
* 错误视图
* 调用此方法确保mLoadingHelper注册对应的Adapter
*/
@Override
public void stateErrorView() {mLoadingHelper.showErrorView();
}/**
* 加载中视图
* 调用此方法确保mLoadingHelper注册对应的Adapter
*/
@Override
public void stateLoadingView() {mLoadingHelper.showLoadingView();
}/**
* 显示内容视图
*/
@Override
public void stateContentView() {mLoadingHelper.showContentView();
}@Override
public void loadingDialogShow() {loadingDialogShow(null);
}@Override
public void loadingDialogShow(boolean cancelable) {loadingDialogShow(null, cancelable);
}@Override
public void loadingDialogShow(String msg) {loadingDialogShow(msg, false);
}@Override
public void loadingDialogShow(String msg, boolean cancelable) {if (mKProgressHUD == null) {mKProgressHUD = KProgressHUD.create(mContext);
}
if (!TextUtils.isEmpty(msg)) {mKProgressHUD.setLabel(msg);
} else {mKProgressHUD.setLabel(null);
}
mKProgressHUD.setCancellable(cancelable);
mKProgressHUD.show();
}/**
* 显示加载框的扩展
*
* @param msg
* @param cancelable
* @param extraData拓展json字符串
*/
@Override
public void loadingDialogShow(String msg, boolean cancelable, JSONObject extraData) {}/**
* 关闭加载框
*/
@Override
public void loadingDialogDismiss() {if (mKProgressHUD != null && mKProgressHUD.isShowing()) {mKProgressHUD.dismiss();
}
}/**
* 释放资源
*/
@Override
public void release() {mKProgressHUD = null;
mContext = null;
mLoadingHelper = null;
mRealRootView = null;
}public LoadingHelper getLoadingHelper() {return mLoadingHelper;
}public KProgressHUD getKProgressHUD() {return mKProgressHUD;
}
}
其中KProgressHUD 的加载框可改变,LoadingHelper不可更改(LCE的实现原理)。如果是页面定制化,应该如何呢?比如我们的标题
/**
* @Author : HHotHeart
* @Time : 2021/8/14 15:42
* @Description : 标题属性Demo
*/
public class TitleDemoActivity extends BaseActivity {@Override
protected void initContentView(@Nullable Bundle savedInstanceState) {setContentView(R.layout.activity_title_demo, new TitleParam("Title Demo")
.setRightText("完成").setRightTextColor(Color.RED).setRightTextSize(17f)
.setOnTitleBarListener(new TitleParam.SimpleTitleBarListener() {@Override
public void onLeftClick(View view) {finish();
}@Override
public void onRightClick(View view) {UIToast.showShort("点击完成");
}
}));
}@Override
protected void doBusiness(Bundle savedInstanceState) {ILFactory.getLoader().loadNet(findViewById(R.id.imageView1),
"https://dss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=218024201,1599297029&fm=26&gp=0.jpg",
IImageLoader.HOptions.defaultOptions());
}
}
我们需要继承框架的BaseActivity,如果是MVP架构,可继承BaseMVPActivity(Fragment同理),页面的标题相关属性会覆盖全局配置的属性。当然,页面LCE的配置也是可覆盖全局配置的LCE,如
/**
* @Author : HHotHeart
* @Time : 2021/9/23 10:39
* @Description : 自定义加载布局Demo
*/
public class CustomLCEActivity extends BaseActivity {@Override
protected void initContentView(@Nullable Bundle savedInstanceState) {setContentView(R.layout.activity_custom_lce, "自定义LCE");
}@Override
public void doPreBusiness() {LoadingHelper loadingHelper = ((CustomLCEDelegate) getLCEDelegate()).getLoadingHelper();
loadingHelper.register(ViewType.LOADING, new CLoadingAdapter());
//loadingHelper.register(ViewType.ERROR, "自定义的错误布局");
//loadingHelper.register(ViewType.EMPTY, "自定义的空布局");
}@Override
protected void doBusiness(Bundle savedInstanceState) {stateLoadingView();
HttpUtils.requestNet(this, new NetCallback() {@Override
public void onSuccess(Long aLong) {stateContentView();
}@Override
public void onFailure(String msg) {stateErrorView();
UIToast.showShort(msg);
}
});
}
}
更多用法查看代码。
- MVP模式
/**
* @author : HHotHeart
* @date : 2021/8/14 11:50
* @desc : 描述
*/
public class AContract {public interface MyActivityModel {void requestNet(NetCallback netCallback);
}public interface MyActivityView extends IView {void showToast();
}public interface MyActivityPresenter {void loadData();
default void onReload() {}
}
}
P层实现
/**
* @Author : HHotHeart
* @Time : 2021/6/11 11:53
* @Description : Activity Presenter
*/
public class MvpDemoActivityPresenter extends BasePresenter
implements AContract.MyActivityPresenter {private static final String TAG = "MvpDemoActivityPresenter";
@Override
public void loadData() {getV().stateLoadingView();
getM().requestNet(new NetCallback() {@Override
public void onSubscribe(Disposable d) {getV().addDispose(d);
}@Override
public void onSuccess(Long o) {getV().stateContentView();
getV().showToast();
}@Override
public void onFailure(String msg) {getV().stateErrorView();
UIToast.showShort(msg);
}
});
}@Override
public void onReload() {getV().stateLoadingView();
getM().requestNet(new NetCallback() {@Override
public void onSubscribe(Disposable d) {getV().addDispose(d);
}@Override
public void onSuccess(Long aLong) {getV().stateContentView();
}@Override
public void onFailure(String msg) {getV().stateErrorView();
UIToast.showShort(msg);
}
});
}@Override
public void onResume(@NonNull @NotNull LifecycleOwner owner) {super.onResume(owner);
}/**
* BasePresenter实现了和Activity或Fragment生命周期绑定,重写即可
*
* @param owner
*/
@Override
public void onDestroy(@NonNull LifecycleOwner owner) {super.onDestroy(owner);
UILog.d(TAG, TAG + " onDestroy被调用");
}}
V层实现
/**
* @Author : HHotHeart
* @Time : 2021/8/14 15:09
* @Description : Activity MVP例子
*/
public class MvpDemoActivity extends BaseMVPActivity implements AContract.MyActivityView {private static final String TAG = "MvpDemoActivity";
@BindView(R.id.imageView1)
ImageView mImageView1;
@Override
protected void initContentView(@Nullable Bundle savedInstanceState) {setContentView(R.layout.activity_test_mvp, new TitleParam("Activity MVP模式"));
}@Override
protected void doBusiness(Bundle savedInstanceState) {ILFactory.getLoader().loadNet(mImageView1,
"https://dss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=218024201,1599297029&fm=26&gp=0.jpg",
IImageLoader.HOptions.defaultOptions());
findView(R.id.btn_test, v -> UIToast.showLong("测试Toast"));
UILog.e(TAG, "isVisible:" + isVisible(findView(R.id.btn_test)));
getP().loadData();
}@Override
public void showToast() {UIToast.showLong("loadData加载完成");
}}
M层实现
/**
* @Author : HHotHeart
* @Time : 2021/6/11 15:46
* @Description : 描述
*/
public class MvpDemoActivityModel extends BaseModel implements AContract.MyActivityModel {@Override
protected void initData() {UIToast.showLong("测试TestModel");
}@Override
public void requestNet(NetCallback netCallback) {Observable.timer(2, TimeUnit.SECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer() {@Override
public void onSubscribe(@NonNull Disposable d) {if (netCallback != null) {netCallback.onSubscribe(d);
}
}@Override
public void onNext(@NonNull Long aLong) {if (netCallback != null) {netCallback.onSuccess(aLong);
}
}@Override
public void onError(@NonNull Throwable e) {if (netCallback != null) {netCallback.onFailure(e.getMessage());
}
}@Override
public void onComplete() {UILog.e("onComplete()");
}
});
}
}
BaseActivity和BaseFragment实现了Disposable的管理,每执行一个Rxjava任务时,应手动调用方法
getV().addDispose(d);
添加任务的Disposable,在页面销毁时会把任务中断。除此之外还可以调用
observable.compose(bindUntilEvent(ActivityEvent event));
将Rxjava任务与页面生命周期绑定,ActivityEvent对应Actiivity的生命周期,如ActivityEvent.DESTROY,具体可查看RxLifecycle的用法。
后续会实现网络请求相关模块,将其请求与页面和Presneter生命周期完美结合起来,敬请期待!!!
【Android|MVPArch - Android MVP 快速开发框架】Android 最强RecyclerView分割线XRecyclerViewDivider重磅来袭!!
推荐阅读
- android第三方框架(五)ButterKnife
- Android中的AES加密-下
- 带有Hilt的Android上的依赖注入
- android|android studio中ndk的使用
- Android事件传递源码分析
- RxJava|RxJava 在Android项目中的使用(一)
- Android7.0|Android7.0 第三方应用无法访问私有库
- 深入理解|深入理解 Android 9.0 Crash 机制(二)
- android防止连续点击的简单实现(kotlin)
- Android|Android install 多个设备时指定设备