一个可有效提高Android开发效率的MVP框架
- 封装Activity/Fragment基类-BaseActivity/BaseFragment(Fragment懒加载开关配置)
- 封装Activity/Fragment基类-BaseBindingActivity/BaseBindingFragment,支持ViewBinding的泛型,无需设置contentView、layouId
- 封装MVP模式Activity/Fragment基类-BaseMVPActivity/BaseMVPFragmentV与P层生命周期监听和绑定,解决诸多内存泄漏问题;除此之外还扩展封装了BaseBindingMVPActivity/BaseBindingMVPFragment以支持ViewBinding
- 使用 LoadingStateView实现可定制化的页面LCE视图
- LoadingDialog加载框定制化,可随意切换
- 添加P层按键返回事件拦截
- 使用TitleBar 实现可全局配置、页面可定制化的Title,不用每个页面写繁琐的xml代码
- 沉浸式状态栏及状态栏颜色设置
- 封装了Log、Toast,可自定义代理实现自己的Log、Toast
- 封装了图片加载器、事件通知管理器,可通过配置切换
- ktx版本更新中...
在你的 Project build.gradle文件中添加:
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
在你的 Module build.gradle文件中添加:
dependencies {
implementation 'com.github.huangxiaolianghh.ArchDroid:mvp:1.0.8'
}
ViewBinding的配置,在你的 Module build.gradle文件中添加:
android {
...
buildFeatures {
viewBinding true
}
...
}
- UILog、UIToast
框架默认实现了UILog、UIToast的代理UILogDelegate、UIToastDelegate,如果不满足需求,可实现自己定义的代理(实现UILog.LogDelegate、UIToast.ToastDelegate即可),具体可参考框架的实现,这里简单实现了CustomLogDelegate
package com.huangxiaoliang.mvparchdemo.util;
import android.util.Log;
import com.huangxiaoliang.mvplib.manager.MVPArchConfig;
import com.huangxiaoliang.mvplib.manager.log.UILog;
/**
* @author : HHotHeart
* @date : 2021/9/24 10:01
* @desc : 自定义代理
*/
public class CustomLogDelegate implements UILog.LogDelegate {
@Override
public String getTag() {
return "日志Tag";
}
@Override
public UILog.LogDelegate init() {
//做一些初始化工作
return this;
}
@Override
public void v(String tag, String msg, Object... obj) {
Log.v(tag, msg);
}
@Override
public void d(String tag, String msg, Object... obj) {
//自己的Log库
Log.d(tag, msg);
}
@Override
public void i(String tag, String msg, Object... obj) {
//自己的Log库
Log.i(tag, msg);
}
@Override
public void w(String tag, String msg, Object... obj) {
//自己的Log库
Log.w(tag, msg);
}
@Override
public void e(String tag, String msg, Object... obj) {
//自己的Log库
Log.e(tag, msg);
}
@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) {
//自己的Log库
}
}
然后在Application中将代理设置给UILog
MVPArchConfig.get().setLogDelegate(new CustomLogDelegate().init())
Log日志开关可通过MVPArchConfig配置
MVPArchConfig.get().setLoggable(BuildConfig.DEBUG)
Toast的代理设置,如
UIToast.setDelegate(UIToast.ToastDelegate delegate);
- EventManager、ILFactory
框架默认实现了EventBusImpl事件通知和GlideLoader图片加载器,可以自由切换(实现IEventBus、IImageLoader接口即可),实现了之后可通过MVPArchConfig配置,如替换GlideLoader、EventBusImpl
MVPArchConfig.get().setImageLoader(IImageLoader imageLoader)
MVPArchConfig.get().setEventBus(IEventBus eventBus);
两者调用方式
EventManager.getBus().post(IEventBus.AbsEvent event);
ImageLoaderFactory.get().loadImage(ImageView target, Object model, ImageOptions<T> options);
其中图片的封装的ImageOptions属性配置尚未配置完成,但基本能满足大部分需求,未来会继续完善这一块。
- LCE-T
框架实现了L(加载视图)、C(内容视图)、E(错误视图、空视图)、T(标题)的逻辑处理,这里主要使用了两个库LoadingHelper和TitleBar ,具体实现原理可去Github上看看,框架可全局配置LCE-T,如
MVPArchConfig.getInstance()
.setLogDelegate(new CustomLogDelegate().init())
.setLoggable(BuildConfig.DEBUG)
.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)
.setBottomLineColor(Color.LTGRAY)
.setBottomLineHeight(0.5f));
//设置全局LCE
LoadingStateView.setViewDelegatePool(pool ->
pool.register(new GLoadingViewDelegate(), new GErrorViewDelegate(), new GEmptyViewDelegate()));
其中GLoadingViewDelegate、GErrorViewDelegate、GEmptyViewDelegate是框架实现的默认全局LCE,可参考将其替换成自己项目的LCE,某个页面都可以通过LoadingStateView对象去设置特定的LCE。
因为Loading弹窗的设置是通过ILoadingPopupView实现的,要想改变代理实现,可自定义代理CustomLoadingPopupDelegate实现ILoadingPopupView接口,然后通过清单文件AndroidManifest.xml去配置自定义的代理,如
<meta-data
android:name="MVPArch.LoadingPopupDelegate"
android:value="com.huangxiaoliang.mvparchdemo.util.CustomLoadingPopupDelegate" />
package com.huangxiaoliang.mvparchdemo.util;
import android.app.Activity;
import android.text.TextUtils;
import com.huangxiaoliang.mvplib.manager.lcet.ILoadingPopupView;
import com.kaopiz.kprogresshud.KProgressHUD;
/**
* <pre>@author HHotHeart</pre>
* <pre>@date 2021/7/9 17:36</pre>
* <pre>@desc 自定义LoadingPopupView代理类,需在清单文件注册meta</pre>
*/
public class CustomLoadingPopupDelegate implements ILoadingPopupView {
private Activity mActivity;
/**
* 加载框 https://github.com/Kaopiz/KProgressHUD
*/
private KProgressHUD mKProgressHUD;
/**
* 必不可少的构造函数,实例化时用到
*
* @param activity Activity
*/
public CustomLoadingPopupDelegate(Activity activity) {
mActivity = activity;
}
@Override
public void showLoadingPopup() {
showLoadingPopup(null);
}
@Override
public void showLoadingPopup(boolean cancelable) {
showLoadingPopup(null, cancelable);
}
@Override
public void showLoadingPopup(String msg) {
showLoadingPopup(msg, false);
}
@Override
public void showLoadingPopup(String msg, boolean cancelable) {
if (mKProgressHUD == null) {
mKProgressHUD = KProgressHUD.create(mActivity);
}
if (!TextUtils.isEmpty(msg)) {
mKProgressHUD.setLabel(msg);
} else {
mKProgressHUD.setLabel(null);
}
mKProgressHUD.setCancellable(cancelable);
mKProgressHUD.show();
}
/**
* 关闭加载框
*/
@Override
public void dismissLoadingPopup() {
if (mKProgressHUD != null && mKProgressHUD.isShowing()) {
mKProgressHUD.dismiss();
}
}
/**
* 释放资源
*/
@Override
public void release() {
mKProgressHUD = null;
mActivity = null;
}
}
其中KProgressHUD 的加载框可改变,这个代理的是全局加载Loading弹窗。
除此之外,LCE-T状态页面定制化,应该如何呢?比如我们的标题
package com.huangxiaoliang.mvparchdemo.activity;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import com.huangxiaoliang.mvparchdemo.databinding.ActivityTitleDemoBinding;
import com.huangxiaoliang.mvparchdemo.util.HttpUtils;
import com.huangxiaoliang.mvplib.manager.imageloader.ImageLoaderFactory;
import com.huangxiaoliang.mvplib.manager.lcet.ITitleView;
import com.huangxiaoliang.mvplib.manager.lcet.TitleParam;
import com.huangxiaoliang.mvplib.manager.toast.UIToast;
import com.huangxiaoliang.mvplib.mvp.BaseBindingActivity;
/**
* <pre>@author HHotHeart</pre>
* <pre>@date 2021/8/14 15:42</pre>
* <pre>@desc 标题属性Demo</pre>
*/
public class TitleDemoActivity extends BaseBindingActivity<ActivityTitleDemoBinding> {
/**
* {@link TitleParam}的属性设置并不是很全面,如果需要设置{@link com.hjq.bar.TitleBar}的更多属性,可以获取它的实例对象进行设置,
* 或者自己利用当前页面的{@link com.dylanc.loadingstateview.LoadingStateView}实现标题栏,参考{@link CustomLCEActivity}
*/
@Override
public ITitleView getPageTitleView() {
return new TitleParam("自定义标题Demo")
.setRightText("完成").setRightTextColor(Color.WHITE).setRightTextSize(17f)
.setTittleBarBgColor(Color.RED)
.setOnTitleBarListener(new TitleParam.SimpleTitleBarListener() {
@Override
public void onLeftClick(View view) {
finish();
}
@Override
public void onRightClick(View view) {
UIToast.showShort("点击完成");
}
});
}
@Override
protected void onBusiness(Bundle savedInstanceState) {
ImageLoaderFactory.get().loadNet(getBinding().imageView1, HttpUtils.IMG);
}
}
我们需要继承框架的BaseActivity,如果是MVP架构,可继承BaseMVPActivity或BaseBindingMVPActivity(Fragment同理),页面的标题相关属性会覆盖全局配置的属性,当然我们也可以拿到LoadingStateView对象,对单个页面设置标题的layout。
除此之外,页面LCE的配置也是可覆盖全局配置的LCE,如
package com.huangxiaoliang.mvparchdemo.activity;
import android.os.Bundle;
import com.huangxiaoliang.mvparchdemo.databinding.ActivityCustomLceBinding;
import com.huangxiaoliang.mvparchdemo.delegate.CLoadingViewDelegate;
import com.huangxiaoliang.mvparchdemo.listener.NetCallback;
import com.huangxiaoliang.mvparchdemo.util.HttpUtils;
import com.huangxiaoliang.mvplib.manager.toast.UIToast;
import com.huangxiaoliang.mvplib.mvp.BaseBindingActivity;
import androidx.annotation.Nullable;
/**
* <pre>@author HHotHeart</pre>
* <pre>@date 2021/9/23 10:39</pre>
* <pre>@desc 自定义加载布局Demo</pre>
*/
public class CustomLCEActivity extends BaseBindingActivity<ActivityCustomLceBinding> {
@Override
public String getPageTitle() {
return "自定义LCE";
}
@Override
public void onBeforeBusiness(@Nullable Bundle savedInstanceState) {
getLoadingStateView().register(new CLoadingViewDelegate());
}
@Override
protected void onBusiness(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 {
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, MvpDemoActivity>
implements AContract.MyActivityPresenter {
private static final String TAG = "MvpDemoActivityPresenter";
@Override
public void loadData() {
getMvpView().stateLoadingView();
getMvpModel().requestNet(new NetCallback<Long>() {
@Override
public void onSubscribe(Disposable d) {
getMvpView().addDispose(d);
}
@Override
public void onSuccess(Long o) {
getMvpView().stateContentView();
getMvpView().showToast();
}
@Override
public void onFailure(String msg) {
getMvpView().stateErrorView();
UIToast.showShort(msg);
}
});
}
@Override
public void onReload() {
getMvpView().stateLoadingView();
getMvpModel().requestNet(new NetCallback<Long>() {
@Override
public void onSubscribe(Disposable d) {
getMvpView().addDispose(d);
}
@Override
public void onSuccess(Long aLong) {
getMvpView().stateContentView();
}
@Override
public void onFailure(String msg) {
getMvpView().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层实现
package com.huangxiaoliang.mvparchdemo.activity.mvp;
import android.os.Bundle;
import com.huangxiaoliang.mvparchdemo.R;
import com.huangxiaoliang.mvparchdemo.databinding.ActivityTestMvpBinding;
import com.huangxiaoliang.mvparchdemo.util.HttpUtils;
import com.huangxiaoliang.mvplib.manager.imageloader.ImageLoaderFactory;
import com.huangxiaoliang.mvplib.manager.log.UILog;
import com.huangxiaoliang.mvplib.manager.toast.UIToast;
import com.huangxiaoliang.mvplib.mvp.BaseBindingMVPActivity;
/**
* <pre>@author HHotHeart</pre>
* <pre>@date 2021/8/14 15:09</pre>
* <pre>@desc Activity MVP例子</pre>
*/
public class MvpDemoActivity
extends BaseBindingMVPActivity<MvpDemoActivityPresenter, ActivityTestMvpBinding>
implements AContract.MyActivityView {
private static final String TAG = "MvpDemoActivity";
@Override
public String getPageTitle() {
return "Activity MVP模式";
}
@Override
protected void onBusiness(Bundle savedInstanceState) {
getBinding().btnTest.setText("btnTest Toast");
ImageLoaderFactory.get().loadNet(getBinding().imageView1, HttpUtils.IMG);
findView(R.id.btn_test, v -> UIToast.showLong("测试Toast"));
UILog.e(TAG, "isVisible:" + isVisible(R.id.btn_test));
getMvpPresenter().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任务时,应手动调用方法
getMvpView().addDispose(d);
添加任务的Disposable,在页面销毁时会把任务中断。除此之外还可以调用
observable.compose(bindUntilEvent(ActivityEvent event));
将Rxjava任务与页面生命周期绑定,ActivityEvent对应Actiivity的生命周期,如ActivityEvent.DESTROY,具体可查看RxLifecycle的用法。