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
- 封装了图片加载器、事件通知管理器,可通过配置切换
项目引入该库
在你的 Project build.gradle文件中添加:
allprojects { repositories { ...maven { url 'https://jitpack.io' }}}
在你的 Module build.gradle文件中添加:
dependencies { implementation 'com.github.HHotHeart:MVPArch:v1.0.2-beta1'}
效果图
| |
| |
| |
功能实现
- UILog、UIToast
框架默认实现了UILog、UIToast的代理UILogDelegate、UIToastDelegate,如果不满足需求,可实现自己定义的代理(实现UILog.LogDelegate、UIToast.ToastDelegate即可),具体可参考框架的实现,这里简单实现了CustomLogDelegate
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
框架默认实现了EventBusImpl事件通知和GlideLoader图片加载器,可以自由切换(实现IEventBus、IImageLoader接口即可),实现了之后可通过MVPArchConfig配置,如替换GlideLoader
MVPArchConfig.getInstance().setImageLoader(IImageLoader imageLoader)
两者调用方式
EventManager.getBus().post(IEventBus.AbsEvent event); ILFactory.getLoader().loadNet(ImageView target, String url, IImageLoader.HOptions options);
- LCE-T
框架实现了L(加载视图)、C(内容视图)、E(错误视图、空视图)、T(标题)的逻辑处理,这里主要使用了两个库LoadingHelper和TitleBar ,具体实现原理可去Github上看看,框架可全局配置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去配置自定义的代理,如
<meta-data android:name="MVPArch.LCEDelegate" android:value="com.littlejerk.mvparch.util.CustomLCEDelegate" />
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<Long>() { @Override public void onSuccess(Long aLong) { stateContentView(); } @Override public void onFailure(String msg) { stateErrorView(); UIToast.showShort(msg); } }); }}
更多用法查看代码。
- MVP模式
框架简易封装了MVP架构,使用了lifecycle管理Activity、Fragment和P层的生命周期,使用RxLifecycle管理Rxjava和Activity、Fragment的生命周期,有效地避免内存泄漏和P层销毁,延时任务造成的空指针问题,Activity(Fragment同理)业务逻辑实现的AContract管理MVP契约类
/** * @author : HHotHeart * @date : 2021/8/14 11:50 * @desc : 描述 */public class AContract { public interface MyActivityModel { void requestNet(NetCallback<Long> 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<MvpDemoActivityModel, AContract.MyActivityView> implements AContract.MyActivityPresenter { private static final String TAG = "MvpDemoActivityPresenter"; @Override public void loadData() { getV().stateLoadingView(); getM().requestNet(new NetCallback<Long>() { @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<Long>() { @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<MvpDemoActivityPresenter> 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<Long> netCallback) { Observable.timer(2, TimeUnit.SECONDS) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<Long>() { @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 最强RecyclerView分割线XRecyclerViewDivider重磅来袭!!
更多相关文章
- 浅谈Java中Collections.sort对List排序的两种方法
- python list.sort()根据多个关键字排序的方法实现
- android 全屏 webview 加载的h5的输入框,被键盘遮挡的解决
- Android(安卓)Window PhoneWindow DecorView
- Android(安卓)LinearLayout线性布局
- android APP隐私政策弹框的实现代码实例
- Android(安卓)实现 WheelView
- Android:简易弹幕效果实现,android弹幕
- android activity 实现半透明Translucent效果