Android 之路 (7) - 对BaseActivity的简单封装

栏目: IOS · Android · 发布时间: 5年前

内容简介:终于到了BaseActivity的封装了,在本章中将对通用性的一些方法和操作进行抽取,放到Base中。先起个名字,我们的Base就叫CandyBaseActivity吧,Candy是糖果的意思,我希望这一套东西能让人像吃糖果一样的甜!在本篇中关于Base,我们需要进行两种封装:
  • overridePendingTransition(Activity的切换动画)
  • 创建 工具 类:ActivityAnimUtils
  • CandyBaseActivity创建两个方法:
  • CandyLoadingBaseActivity

引言

终于到了BaseActivity的封装了,在本章中将对通用性的一些方法和操作进行抽取,放到Base中。

正文

先起个名字,我们的Base就叫CandyBaseActivity吧,Candy是糖果的意思,我希望这一套东西能让人像吃糖果一样的甜!

分析

在本篇中关于Base,我们需要进行两种封装:

  1. CandyBaseActivity,最基本,最底层的Base,附带通用操作的封装。
  2. CandyLoadingBaseActivity继承至CandyBaseActivity,不是所有的页面都是需要弹窗的,像弹窗需要重写很多的方法,就不适合放到最底层。
  3. MVPBaseActivity,进行MVP分层的Base,里面包含生命周期的订阅和取消订阅。

CandyBaseActivity

首先确定我们现阶段能封装什么:

  • mActivity
  • 将T.showToast封装到底层
  • startActivity
  • overridePendingTransition(Activity的切换动画)
  • initToolbar

mActivity

每次要引用上下文都用类名的方式来指定,这就比较繁琐了。

protected Activity mActivity;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	mActivity = this;
}

showToast

直接通过工具类T来调用showToast方法在原来的基础上市比较方便,但是一直需要传递一个 context 就比较让人烦躁了,所以将showToast放到Base中。代码如下:

/**
 * 显示文本信息
 *
 * @param text 文本信息
 */
public void showToast(String text) {
	T.showToast(mActivity, text);
}

/**
 * 显示文本信息
 *
 * @param resId 文本资源id信息
 */
public void showToast(int resId) {
	T.showToast(mActivity, resId);
}

showToast方法定义为public,是为了能在其他地方,如:Presenter中进行 强转 后直接调用。

overridePendingTransition(Activity的切换动画)

关于Activity的切换动画有各种各样的,根据不同的喜好有不同的做法,我这里使用的是: 右滑进入、左滑退出 ,其他动画自行探索。

动画xml

setup_next_in.xml

<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="300"
    android:fromXDelta="100%p"
    android:fromYDelta="0"
    android:toXDelta="0"
    android:toYDelta="0" >
<!--
下一页  进入
		位置为100 到达0
  -->
</translate>

setup_next_out.xml

<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="300"
    android:fromXDelta="0"
    android:fromYDelta="0"
    android:toXDelta="-100%p"
    android:toYDelta="0" />

setup_pre_in.xml

<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="300"
    android:fromXDelta="-100%p"
    android:fromYDelta="0"
    android:toYDelta="0"
    android:toXDelta="0"/>

setup_pre_out.xml

<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="300"
    android:fromXDelta="0"
    android:fromYDelta="0"
    android:toXDelta="100%p"
    android:toYDelta="0" />

创建工具类:ActivityAnimUtils

/**
 * Activity的跳转动画
 *
 * @author aohanyao create in 2018年10月04日02:36:34
 */
public class ActivityAnimUtils {
    /**
     * 跳转到
     *
     * @param activity
     */
    public static void to(Activity activity) {
        activity.overridePendingTransition(R.anim.setup_next_in,
                R.anim.setup_next_out);

    }

    /**
     * 退出动画
     */
    public static void out(Activity activity) {
        activity.overridePendingTransition(R.anim.setup_pre_in, R.anim.setup_pre_out);
    }
}

CandyBaseActivity创建两个方法:

/**
 * 右边划出
 */
protected void slideLeftOut() {
	ActivityAnimUtils.out(mActivity);
}

/**
 * 进入
 */
protected void slideRightIn() {
	ActivityAnimUtils.to(mActivity);
}

在startActivity之后调用 slideRightIn()finish() 的时候调用 slideLeftOut()

startActivity

原本常用的方法是: startActivity(new Intent(mActivity, Target.class)); ,这其中 new Intent()这一部分都是冗余的,我们可以封装一下,其中关于值的传递采用的是一个 Pair<String, Object> 的可变参数,然后根据不同的类型,将数据填充到intent中。具体代码如下:

/**
 * 打开 Activity
 *
 * @param activity
 */
protected void launchActivity(Class<? extends Activity> activity) {
	startActivity(new Intent(mActivity, activity));
	// 加上动画
	slideRightIn();
}

/**
 * 打开 Activity
 *
 * @param activity
 */
protected void launchActivityForResult(Class<? extends Activity> activity, int requestCode) {
	startActivityForResult(new Intent(mActivity, activity), requestCode);
	// 加上动画
	slideRightIn();
}

/**
 * 打开新的 Activity
 *
 * @param activity 目标Activity
 * @param pairs    键值对
 */
protected void launchActivity(Class<? extends Activity> activity, Pair<String, Object>... pairs) {
	Intent intent = new Intent(mActivity, activity);
	// 填充数据
    IntentUtils.fillIntent(intent, pairs);
	startActivity(intent);
	// 加上动画
	slideRightIn();
}

/**
 * 打开新的 Activity
 *
 * @param activity 目标Activity
 * @param pairs    键值对
 */
protected void launchActivityForResult(Class<? extends Activity> activity, int requestCode, Pair<String, Object>... pairs) {
	Intent intent = new Intent(mActivity, activity);
	// 填充数据
	IntentUtils.fillIntent(intent, pairs);
	startActivityForResult(intent, requestCode);
	// 加上动画
	slideRightIn();
}

关于 fillIntent ,这个方法主要是判断参数中值的类类型,然后进行intent的填充:

/**
 * Intent工具类
 */
public class IntentUtils {
    /**
     * 填充intent数据,暂时只写了常用的一些数据格式,不常用的没写
     *
     * @param intent
     * @param pairs
     */
    public static void fillIntent(Intent intent, Pair<String, Object>[] pairs) {

        if (pairs != null) {
            for (Pair<String, Object> pair : pairs) {
                Object value = pair.second;
                //判断不同的类型,进行强转和存放
                if (value instanceof Boolean) {
                    intent.putExtra(pair.first, (Boolean) value);
                }
                if (value instanceof Byte) {
                    intent.putExtra(pair.first, (Byte) value);
                }
                if (value instanceof Short) {
                    intent.putExtra(pair.first, (Short) value);
                }
                if (value instanceof Long) {
                    intent.putExtra(pair.first, (Long) value);
                }

                if (value instanceof Float) {
                    intent.putExtra(pair.first, (Float) value);
                }

                if (value instanceof Double) {
                    intent.putExtra(pair.first, (Double) value);
                }
                if (value instanceof Integer) {
                    intent.putExtra(pair.first, (Integer) value);
                }
                if (value instanceof String) {
                    intent.putExtra(pair.first, (String) value);
                }
                if (value instanceof Parcelable) {
                    intent.putExtra(pair.first, (Parcelable) value);
                }
                if (value instanceof Serializable) {
                    intent.putExtra(pair.first, (Serializable) value);
                }

            }
        }
    }
}

综上,还把前面的动画用上了。

如何调用:

//普通
launchActivity(DialogExampleActivity.class);

//普通携带参数
launchActivity(ToastExampleActivityActivity.class,
                new Pair<String, Object>("key1", "value1"),
                new Pair<String, Object>("key1", "value1"));

//返回值
launchActivityForResult(LoginActivity.class,200);
 
 // 返回值携带参数
launchActivityForResult(LoginActivity.class,
			200,
			new Pair<String, Object>("key1", "value1"),
			new Pair<String, Object>("key1", "value1"));

initToolbar

关于Toolbar这里使用的是 android.support.v7.widget.Toolbar ,简单又方便,先看看我们在创建的时候系统生成的Toolbar:

<android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </android.support.design.widget.AppBarLayout>

为了以后的扩展性和能够统一的在Base中操作,要做一下操作:

  • 为AppBarLayout和Toolbar定义id资源,通过统一id来管理
  • 为AppBarLayout和Toolbar定义style,将id加入到style中,在xml中直接引用style

定义id

<resources>
    <item name="base_toolbar" type="id" />
    <item name="base_appbar" type="id" />
</resources>

定义style

<resources>
    <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
    <style name="BaseAppBarLayoutStyle">
        <item name="android:layout_width">match_parent</item>
        <item name="android:layout_height">44dp</item>
        <item name="android:theme">@style/AppTheme.AppBarOverlay</item>
        <!--这里设置 标题栏的颜色-->
        <item name="android:id">@id/base_appbar</item>
    </style>

    <!--标题栏的样式-->
    <style name="BaseToolbarStyle" parent="Widget.AppCompat.Toolbar">
        <!--高度-->
        <item name="android:layout_height">?attr/actionBarSize</item>
        <!--id-->
        <item name="android:id">@id/base_toolbar</item>
        <!--宽度-->
        <item name="android:layout_width">match_parent</item>
        <!--背景颜色-->
        <item name="android:background">?attr/colorPrimary</item>
    </style>
</resources>

使用样式

将原本activity_layout的内容更改一下:

<android.support.design.widget.AppBarLayout
        style="@style/BaseAppBarLayoutStyle">

        <android.support.v7.widget.Toolbar
            style="@style/BaseToolbarStyle"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </android.support.design.widget.AppBarLayout>

initToolbar

在初始化Toolbar的时候需要有一些属性是需要可配置的,需要为这些配置项创建不同的方法,让子类来进行复写:

Toolbar颜色

Toolbar返回icon

Toolbar返回事件

前面定义了一个id资源 base_toolbar ,我们可以直接在Base中通过 findViewById 来获取Toolbar。

initToolbar具体代码如下:

/**
 * 是否初始化了toolbar
 */
private boolean isInitToolbar = false;

@Override
protected void onStart() {
	super.onStart();
	if (!isInitToolbar) {
		initToolbar();
	}
}

/**
 * 初始化toolbar
 */
private void initToolbar() {
	Toolbar mToolbar = findViewById(R.id.base_toolbar);
	if (null != mToolbar) {
		//设置返回按钮
		setSupportActionBar(mToolbar);
		mToolbar.setBackgroundColor(getToolbarBackground());
		mToolbar.setNavigationIcon(getNavigationIcon());
		mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				onNavigationOnClickListener();
			}
		});
		isInitToolbar = true;
	}
}

/**
 * 获取toolbar的背景
 * @return
 */
private int getToolbarBackground() {
	return getResources().getColor(R.color.colorPrimary);
}

/**
 * 返回按钮点击
 */
protected void onNavigationOnClickListener() {
	finish();
	slideLeftOut();
}

/**
 * 返回按钮
 *
 * @return
 */
protected int getNavigationIcon() {
	return R.drawable.ic_arrow_back_white_24dp;
}

子类不用管Toolbar的初始化 ,需要在xml中使用定义的style就可以完成初始化。

CandyLoadingBaseActivity

在本次CandyLoadingBaseActivity中需要封装的是 DialogHelper,让子类直接通过 show...() 的方式来显示弹窗,而不用加上 mDialogHelper 的方式。而封装的方式差不多是将DialogHelper的方法重写一遍,只不过方法体内的调用对象换成DialogHelper,具体直接看下面代码:

public class CandyLoadingBaseActivity extends CandyBaseActivity implements OnDialogCancelListener {
    protected DialogHelper mDialogHelper;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (mDialogHelper == null) {
            mDialogHelper = new DialogHelper(mActivity, this);
        }
    }

    /**
     * 显示 loading 弹窗,默认不能点击空白处进行取消
     *
     * @param loadingTip 信息提示
     */
    public void showLoadingDialog(String loadingTip) {
        showLoadingDialog(loadingTip, true);
    }

    /**
     * 显示 loading 弹窗
     *
     * @param loadingTip 信息提示
     * @param cancelable 能不能点击空白的地方
     */
    public void showLoadingDialog(String loadingTip, Boolean cancelable) {
        mDialogHelper.showLoadingDialog(loadingTip, cancelable);
    }

    /**
     * 信息提示弹窗
     *
     * @param message 提示信息的内容
     */
    public void showMessageDialog(String message) {
        mDialogHelper.showMessageDialog(message);
    }

    /**
     * 信息提示弹窗
     *
     * @param message         提示信息的内容
     * @param confirmListener 确认按钮点击的回调
     */
    public void showMessageDialog(String message, OnDialogConfirmListener confirmListener) {
        mDialogHelper.showMessageDialog(message, confirmListener);
    }

    /**
     * 成功提示弹窗
     *
     * @param message 提示信息的内容
     */
    public void showSuccessDialog(String message) {
        mDialogHelper.showSuccessDialog(message);
    }

    /**
     * 成功提示弹窗
     *
     * @param message         提示信息的内容
     * @param confirmListener 确认按钮点击的回调
     */
    public void showSuccessDialog(String message, OnDialogConfirmListener confirmListener) {
        mDialogHelper.showSuccessDialog(message, confirmListener);
    }

    /**
     * 警告提示弹窗
     *
     * @param message 提示信息的内容
     */
    public void showWarningDialog(String message) {
        mDialogHelper.showWarningDialog(message);
    }

    /**
     * 警告提示弹窗
     *
     * @param message         提示信息的内容
     * @param confirmListener 确认按钮点击的回调
     */
    public void showWarningDialog(String message, OnDialogConfirmListener confirmListener) {
        mDialogHelper.showWarningDialog(message, confirmListener);
    }

    /**
     * 错误提示弹窗
     *
     * @param message 提示信息的内容
     */
    public void showErrorDialog(String message) {
        mDialogHelper.showErrorDialog(message);
    }

    /**
     * 错误提示弹窗
     *
     * @param message         提示信息的内容
     * @param confirmListener 确认按钮点击的回调
     */
    public void showErrorDialog(String message, OnDialogConfirmListener confirmListener) {
        mDialogHelper.showErrorDialog(message, confirmListener);
    }

    /**
     * 显示确认弹窗
     *
     * @param message         提示信息
     * @param confirmText     确认按钮文字
     * @param cancelText      取消按钮文字
     * @param confirmListener 确认按钮点击回调
     * @param cancelListener  取消按钮点击回调
     */
    public void showConfirmDialog(String message,
                                  String confirmText,
                                  String cancelText,
                                  final OnDialogConfirmListener confirmListener,
                                  final OnDialogCancelListener cancelListener) {

        mDialogHelper.showConfirmDialog(message, confirmText, cancelText, confirmListener, cancelListener);

    }

    /**
     * 显示确认弹窗
     *
     * @param message         提示信息
     * @param confirmText     确认按钮文字
     * @param cancelText      取消按钮文字
     * @param confirmListener 确认按钮点击回调
     */
    public void showConfirmDialog(String message,
                                  String confirmText,
                                  String cancelText,
                                  OnDialogConfirmListener confirmListener) {

        showConfirmDialog(message, confirmText, cancelText, confirmListener, null);
    }

    /**
     * 显示确认弹窗
     *
     * @param message         提示信息
     * @param confirmListener 确认按钮点击回调
     */
    public void showConfirmDialog(String message,
                                  OnDialogConfirmListener confirmListener) {
        showConfirmDialog(message, "确定", "取消", confirmListener, null);
    }

	 /**
     * 关闭弹窗
     */
    public void dismissDialog() {
        mDialogHelper.dismissDialog();
    }
	
    @Override
    public void onDialogCancelListener(AlertDialog dialog) {
        //空实现,让子类做自己想做的事情
    }
}

示例

将DialogExampleActivity继承至CandyLoadingBaseActivity,然后注释掉DialogHelper相关的代码。

Android 之路 (7) - 对BaseActivity的简单封装

MVPBaseActivity

关于MVPBaseActivity,这里要做的封装有:

  • 使用泛型 P 来表示Presenter。
  • 将Dialog与Presenter进行绑定:取消Dialog的同时取消订阅、请求完成关闭Dialog。
  • 将Presenter与Activity进行绑定:onDestroy的时候取消订阅,防止内存泄漏。

泛型P

abstract public class MVPBaseActivity<P extends BasePresenter> extends CandyLoadingBaseActivity {

    private P mPresenter;

    /**
     * 底层获取P
     *
     * @return P
     */
    protected synchronized P getP() {
        if (mPresenter == null) {
            mPresenter = createPresenter();
        }
        return mPresenter;
    }


    /**
     * 创建Presenter
     *
     * @return 返回Presenter的实例
     */
    protected abstract P createPresenter();
}

将需要MVP的Activity继承至MVPBaseActivity,并且传入一个BasePresenter子类的P,然后实现 createPresenter() 返回具体的实现类,最后直接通过 getP() 来调用Presenter,为了演示效果,我已经将 LoginActivity 做了更改,详情请查看源码,核心代码如下:

public class LoginActivity extends MVPBaseActivity<LoginPresenter> {
    ...
    private void initEvent() {
        ...
        getP().login(account, password);
        ...
    }
 
    @Override
    protected LoginPresenter createPresenter() {
        return new LoginPresenter(this);
    }
}

绑定Dialog

前面在封装MVP的时候我们定义了一个 BaseView ,里面包含了 onComplete 方法,所以我们可以直接让MVPBaseActivity实现BaseView,核心代码如下:

@Override
public void onComplete() {
	// 请求完成、关闭dialog
	dismissDialog();
}

@Override
public void onDialogCancelListener(AlertDialog dialog) {
	super.onDialogCancelListener(dialog);
	// dialog取消,取消订阅
	getP().unDisposable();
}

绑定Activity

其实就是在onDestroy的时候取消订阅一下,代码如下:

@Override
protected void onDestroy() {
	super.onDestroy();
	// 销毁 取消订阅
	getP().unDisposable();
}

整体代码如下:

/**
 * Mvp BaseActivity
 *
 * @param <P>
 */
abstract public class MVPBaseActivity<P extends BasePresenter> extends CandyLoadingBaseActivity
        implements BaseView {

    private P mPresenter;

    /**
     * 底层获取P
     *
     * @return P
     */
    protected synchronized P getP() {
        if (mPresenter == null) {
            mPresenter = createPresenter();
        }
        return mPresenter;
    }


    /**
     * 创建Presenter
     *
     * @return 返回Presenter的实例
     */
    protected abstract P createPresenter();

    @Override
    public void onComplete() {
        // 请求完成、关闭dialog
        dismissDialog();
    }

    @Override
    public void onDialogCancelListener(AlertDialog dialog) {
        super.onDialogCancelListener(dialog);
        // dialog取消,取消订阅
        getP().unDisposable();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 销毁 取消订阅
        getP().unDisposable();
    }

    @Override
    public void onFailure(String message) {
//        showWarningDialog(message);
        // 暂不实现,后面有一篇文章:统一错误管理
    }
}

结束

总结

终于完成了三个BaseActivity的封装了,可能有人有疑惑:为什么没有BaseFragment呢?其实BaseFragment和BaseActivity相差不大,只是一些方法调用的时机不一样,还有就是没用Toolbar的初始化,这里由于篇幅的问题就没用一一列出,不过我在源码中已经完成了BaseFragment的封装,可以直接查看源码。

源码-tag-v0.07

最后

关于Base的封装还没有完呢?预计还有还几章的篇幅,敬请期待!

软广

一个痴心妄想想成为一个全屏(栈)工程师的程序猿。

来来,关注一下吧!

Android 之路 (7) - 对BaseActivity的简单封装

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Namo Webeditor5.5一看就懂.

Namo Webeditor5.5一看就懂.

吳聲毅 / 金禾資訊 / 20040214 / NT$ 169

一看就懂系列書全以初學者的角度切入,全書以STEP BY STEP方式撰寫,並以豐富的圖片搭配教學,在最後更加上日常生活實例運用講解,一路學來一氣呵成。為了增進學習的效率更採用高級紙品全彩印刷,這麼好的書,您還在等什麼,一看就懂系列書保證是您最佳入門學習好伙伴。 本書特色: 1、一看就懂:Step by Step操作詳盡說明、讓您一看就懂 2、精選範例:精彩實務範例生動活......一起来看看 《Namo Webeditor5.5一看就懂.》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具