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重磅來襲!!
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/302755.html
標籤:其他
上一篇:碼上功夫-搭建jenkins與gitlab實作web自動更新內容
下一篇:無法訪問Nginx容器的分析思路
