Android MVP框架搭建与使用(含源码)

Posted 初学者_Study

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android MVP框架搭建与使用(含源码)相关的知识,希望对你有一定的参考价值。


前言

  其实之前我是写过相关的MVP文章的,只不过当时在是天气APP中,而这里是单独拉出来讲,因此会有一些不同。

正文

先在android Studio中创建一个名为MvpDemo的项目。

Android


创建好如下图所示

Android

一、创建mvp模块

再创建一个依赖模块,File → New → New Module…

Android


选择Android Library

Android


Next,修改一下模块名字

Android


点击Finish,你的mvplibrary模块就创建完成了。

下面在app模块中依赖mvplibrary模块。

Android


在AS的右上角工具栏中找到上图中的图标按钮,点击进入如下页面。

Android


然后通过上面的操作,到最后一步点击这个Module Dependency会出现如下页面

Android


勾选上,点击OK。

Android


你会发现这里多了一个依赖,然后点击OK即可。现在已经依赖好了,下面就是MVP框架的搭建了,搭建过程中都是在mvplibrary中,与app模块无关。

二、搭建MVP框架

1. 创建Activity管理

首先在com.llw.mvplibrary下新建一个ActivityManager类,里面的代码如下:

package com.llw.mvplibrary;

import android.app.Activity;

import java.util.ArrayList;
import java.util.List;

/**
* 管理Activity
* @author llw
*/
public class ActivityManager

//保存所有创建的Activity
private List<Activity> activityList = new ArrayList<>();

/**
* 添加Activity
* @param activity
*/
public void addActivity(Activity activity)
if(activity != null)
activityList.add(activity);



/**
* 移除Activity
* @param activity
*/
public void removeActivity(Activity activity)
if(activity != null)
activityList.remove(activity);



/**
* 关闭所有Activity
*/
public void finishAllActivity()
for (Activity activity : activityList)
activity.finish();


2. 创建基类Application

在com.llw.mvplibrary下新建一个BaseApplication类,里面的代码如下:

package com.llw.mvplibrary;

import android.app.Application;
import android.content.Context;

/**
* 基类Application
* @author llw
*/
public class BaseApplication extends Application

private static ActivityManager activityManager;

private static BaseApplication application;

private static Context context;

@Override
public void onCreate()
super.onCreate();
//声明Activity管理
activityManager = new ActivityManager();
context = getApplicationContext();
application = this;



public static BaseApplication getApplication()
return application;


public static Context getContext()
return context;


public static ActivityManager getActivityManager()
return activityManager;

3. 创建base包(以及包下的类和接口)

在com.llw.mvplibrary下新建一个base包,然后在这个包下面新建一个IUiCallback接口,里面代码如下:

package com.llw.mvplibrary.base;

import android.os.Bundle;

/**
* UI回调接口
* @author llw
*/
public interface IUiCallback

void initBeforeView(Bundle savedInstanceState);

//初始化视图
void initData(Bundle savedInstanceState);

//获取布局Id
int getLayoutId();

之后再新建一个BaseView接口,这是一个空接口。

package com.llw.mvplibrary.base;

/**
* 基类View,可以根据实际情况写方法
* @author llw
*/
public interface BaseView

然后创建一个BasePresenter,在这里可以操作View,这是MVP中的核心思想,通过P层控制M和V,从而减低M和V的耦合,甚至让它们不存在直接关联。

package com.llw.mvplibrary.base;

import java.lang.ref.WeakReference;

/**
* Presenter基类 操作视图View
*
* @param <V>
* @author llw
*/
public class BasePresenter<V extends BaseView>

//弱引用View
protected WeakReference<V> mWeakReference;
private V mView;

/**
* 绑定View
*
* @param view
*/
public void attachView(V view)
mView = view;
mWeakReference = new WeakReference<V>(view);


/**
* 解绑View
*/
public void detachView()
mView = null;
if (mWeakReference != null)
mWeakReference.clear();
mWeakReference = null;



/**
* 获取view
*
* @return
*/
public V getView()
if (mWeakReference != null)
return mWeakReference.get();

return null;


/**
* View是否绑定
*
* @return
*/
public boolean isViewAttached()
return mView != null;

下面写BaseActivity,一般的Activity只要继承这个BaseActivity,重写里面的方法即可。

package com.llw.mvplibrary.base;

import android.app.Activity;
import android.app.Dialog;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;

import com.llw.mvplibrary.R;

import java.util.Objects;

/**
* 基类Activity,普通Activity继承即可。
*
* @author llw
*/
public abstract class BaseActivity extends AppCompatActivity implements IUiCallback

//Activity 上下文
protected Activity context;
//弹窗
private Dialog mDialog;

private static final int FAST_CLICK_DELAY_TIME = 500;
private static long lastClickTime;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState)
super.onCreate(savedInstanceState);
//绑定视图
initBeforeView(savedInstanceState);
//获取Activity的上下文
this.context = this;
BaseApplication.getActivityManager().addActivity(this);
//绑定视图XML
if (getLayoutId() > 0)
setContentView(getLayoutId());


initData(savedInstanceState);



/**
* Toast消息提示 字符
* @param llw
*/
protected void showMsg(CharSequence llw)
Toast.makeText(context, llw, Toast.LENGTH_SHORT).show();


/**
* Toast消息提示 资源ID
* @param resourceId
*/
protected void showMsg(int resourceId)
Toast.makeText(context, resourceId, Toast.LENGTH_SHORT).show();


/**
* 弹窗出现
*/
protected void showLoadingDialog()
if (mDialog == null)
mDialog = new Dialog(context, R.style.loading_dialog);

mDialog.setContentView(R.layout.dialog_loading);
mDialog.setCancelable(false);
Objects.requireNonNull(mDialog.getWindow()).setBackgroundDrawableResource(android.R.color.transparent);
mDialog.show();


/**
* 弹窗隐藏
*/
protected void hideLoadingDialog()
if (mDialog != null)
mDialog.dismiss();

mDialog = null;


/**
* 返回 不需要参数
*/
protected void Back()
context.finish();
if(!isFastClick())
context.finish();



/**
* 返回 toolbar控件点击
*
* @param toolbar
*/
protected void Back(Toolbar toolbar)
toolbar.setNavigationOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
context.finish();
if (!isFastClick())
context.finish();


);




/**
* 两次点击间隔不能少于500ms
*
* @return flag
*/
protected static boolean isFastClick()
boolean flag = true;
long currentClickTime = System.currentTimeMillis();
if ((currentClickTime - lastClickTime) >= FAST_CLICK_DELAY_TIME)
flag = false;

lastClickTime = currentClickTime;

return flag;



一般来说这样就可以了,但是以我的开发习惯来说我还会加上这个页面加载的弹窗和页面销毁的控制,那么下午我给加上。

首先需要在drawable下添加两个图片,这两个图片有一些特殊,建议你直接在我的源码里面复制出来,因为我现在贴出来你拿过去直接用是达不到实际的效果的。

Android


Android


Android

然后com.llw.mvplibrary下新建一个view包,然后新建一个LoadingView类,里面的代码如下:

package com.llw.mvplibrary.view;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.util.AttributeSet;

import androidx.annotation.Nullable;

import com.llw.mvplibrary.R;

import java.lang.ref.SoftReference;


/**
* 加载框
* @author llw
*/
public class LoadingView extends androidx.appcompat.widget.AppCompatImageView
private int mCenterRotateX;//图片旋转点x
private int mCenterRotateY;//图片旋转点y
private LoadingRunnable mRunnable;

public LoadingView(Context context)
this(context, null);


public LoadingView(Context context, @Nullable AttributeSet attrs)
this(context, attrs, 0);


public LoadingView(Context context, @Nullable AttributeSet attrs, int defStyleAttr)
super(context, attrs, defStyleAttr);
init();


private void init()
setScaleType(ScaleType.MATRIX);
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.icon_loading);
setImageBitmap(bitmap);
mCenterRotateX = bitmap.getWidth() / 2;
mCenterRotateY = bitmap.getHeight() / 2;


/**
* onDraw()之前调用
*/
@Override
protected void onAttachedToWindow()
super.onAttachedToWindow();
if (mRunnable==null)
mRunnable=new LoadingRunnable(this);

if (!mRunnable.isLoading)
mRunnable.start();



/**
* view销毁时调用
*/
@Override
protected void onDetachedFromWindow()
super.onDetachedFromWindow();
if (mRunnable!=null)
mRunnable.stop();

mRunnable=null;


class LoadingRunnable implements Runnable
private boolean isLoading;
private Matrix mMatrix;
private SoftReference<LoadingView> mLoadingViewSoftReference;
private float mDegrees = 0f;

public LoadingRunnable(LoadingView loadingView)
mLoadingViewSoftReference = new SoftReference<LoadingView>(loadingView);
mMatrix = new Matrix();


@Override
public void run()
if (mLoadingViewSoftReference.get().mRunnable != null && mMatrix != null)
mDegrees += 30f;
mMatrix.setRotate(mDegrees, mCenterRotateX, mCenterRotateY);
mLoadingViewSoftReference.get().setImageMatrix(mMatrix);
if (mDegrees==360)
mDegrees=0f;

if (isLoading)
mLoadingViewSoftReference.get().postDelayed(mLoadingViewSoftReference.get().mRunnable, 100);




public void stop()
isLoading = false;


public void start()
isLoading = true;
if (mLoadingViewSoftReference.get().mRunnable != null && mMatrix != null)
mLoadingViewSoftReference.get().postDelayed(mLoadingViewSoftReference.get().mRunnable, 100);




再新建一个LoadingTextView,里面的代码如下:

package com.llw.mvplibrary.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Shader;
import android.util.AttributeSet;

import androidx.appcompat.widget.AppCompatTextView;

/**
* 颜色波浪TextView
* @author llw
*/
public class LoadingTextView extends AppCompatTextView

private LinearGradient mLinearGradient;
private Matrix mGradientMatrix;
private Paint mPaint;
private int mViewWidth = 0;
private int mTranslate = 0;

private boolean mAnimating = true;

public LoadingTextView(Context context, AttributeSet attrs)
super(context, attrs);


@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh)
super.onSizeChanged(w, h, oldw, oldh);
if (mViewWidth == 0)
mViewWidth = getMeasuredWidth();
if (mViewWidth > 0)
mPaint = getPaint();
mLinearGradient = new LinearGradient(-mViewWidth, 0, 0, 0,
new int[]0x33ffffff, 0xffd81e06, 0x33ffffff,
new float[]0, 0.5f, 1, Shader.TileMode.CLAMP);
mPaint.setShader(mLinearGradient);
mGradientMatrix = new Matrix();




@Override
protected void onDraw(Canvas canvas)
super.onDraw(canvas);
if (mAnimating && mGradientMatrix != null)
mTranslate += mViewWidth / 10;
if (mTranslate > 2 * mViewWidth)
mTranslate = -mViewWidth;

mGradientMatrix.setTranslate(mTranslate, 0);
mLinearGradient.setLocalMatrix(mGradientMatrix);
postInvalidateDelayed(20);


然后在res下新建一个layout文件夹,在这个文件夹下新建一个dialog_loading.xml,里面的布局代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layout_loading"
android:layout_width="120dp"
android:layout_height="120dp"
android:layout_gravity="center"
android:background="@drawable/ic_loading_bg"
android:gravity="center"
android:orientation="vertical">

<!--旋转的图-->
<com.llw.mvplibrary.view.LoadingView
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

<!--变色的字-->
<com.llw.mvplibrary.view.LoadingTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="Loading"
android:textColor="#fff"
android:textSize="@dimen/sp_14"

还需要在valuse下新建一个styles.xml,里面的代码如下:

<?xml version="1.0" encoding="utf-8"?>
<resources>

<!--加载弹窗的样式-->
<style name="loading_dialog" parent="@android:style/Theme.Dialog">
<item name="android:windowFrame">@null</item>
<item name="android:windowIsFloating">true</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowNoTitle">true</item>
<item name="android:background">@null</item>
<item name="android:windowBackground">@null</item>
<item name="android:backgroundDimEnabled">false</item>
</style>

</resources>

然后在BaseActivity中新增如下两个方法。

//弹窗
private Dialog mDialog;

/**
* 弹窗出现
*/
protected void showLoadingDialog()
if (mDialog == null)
mDialog = new Dialog(context, R.style.loading_dialog);

mDialog.setContentView(R.layout.dialog_loading);
mDialog.setCancelable(false);
Objects.requireNonNull(mDialog.getWindow()).setBackgroundDrawableResource(android.R.color.transparent);
mDialog.show();


/**
* 弹窗隐藏
*/
protected void hideLoadingDialog()
if (mDialog != null)
mDialog.dismiss();

mDialog = null;

然后再增加页面返回的处理。

private static final int FAST_CLICK_DELAY_TIME = 500;
private static long lastClickTime;

/**
* 返回 不需要参数
*/
protected void Back()
context.finish();
if(!isFastClick())
context.finish();



/**
* 返回 toolbar控件点击
*
* @param toolbar
*/
protected void Back(Toolbar toolbar)
toolbar.setNavigationOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
context.finish();
if (!isFastClick())
context.finish();


);




/**
* 两次点击间隔不能少于500ms
*
* @return flag
*/
protected static boolean isFastClick()
boolean flag = true;
long currentClickTime = System.currentTimeMillis();
if ((currentClickTime - lastClickTime) >= FAST_CLICK_DELAY_TIME)
flag = false;

lastClickTime = currentClickTime;

return flag;

这一步为止,这个BaseActivity就写完了,下面该写BaseFragment了。

package com.llw.mvplibrary.base;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

/**
* 基类Fragment,普通Fragment继承即可。
* @author llw
*/
public abstract class BaseFragment extends Fragment implements IUiCallback

protected View rootView;
protected LayoutInflater layoutInflater;
protected Activity context;

@Override
public void onCreate(@Nullable Bundle savedInstanceState)
super.onCreate(savedInstanceState);
initBeforeView(savedInstanceState);


@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)
layoutInflater = inflater;
if(rootView == null)
rootView = inflater.inflate(getLayoutId(),null);
else
ViewGroup viewGroup = (ViewGroup) rootView.getParent();
if(viewGroup != null)
viewGroup.removeView(rootView);


return rootView;


@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState)
super.onActivityCreated(savedInstanceState);
initData(savedInstanceState);


/**
* 绑定
Android MVP框架MVPro的使用和源码分析

Android 教你一步步搭建MVP+Retrofit+RxJava网络请求框架

Android二手交易平台,dagger2+mvp+Bmob后台云搭建

android悬浮球实现各种功能快速开发框架单词笔记本应用市场应用等源码

框架模式MVC与MVP在Android中的应用

android采用MVP漫画APP适配刘海屏小黄车主界面录音波浪动画综合APP等源码