我一行代码都不写实现Toolbar!你却还在封装BaseActivity?

原文地址: https://juejin.im/post/590f09ec128fe100584ee6b0android

前言

距离 上篇文章 的发表时间已通过去两个多月了,这两个月时间里我没写文章但一直在更新着个人 MVPArms 框架,让他逐渐朝着 可配置化集成框架 发展git

就在前段时间我在 鸿洋公众号 上看到了一篇文章,大概是介绍怎么封装 BaseActivity ,让 Activity 经过几行代码就能够实现 ToolBargithub

恰好个人 MVPArms 框架也更新了一个功能:bash

经过非继承 Activity Fragment 来实现之前须要封装进 BaseActivity BaseFragment 经过继承来实现的一些公共逻辑,以及监听整个 App 全部 Activity 以及 Fragment 的生命周期(包括三方库),并可向其生命周期内插入代码app

** 那我就来讲说我怎么在不使用继承的状况下让 Activty 一行代码都不写就能实现 Toolbar **框架

为何我提倡少封装 BaseActvity 少用继承

BaseActivity 封装多了,除了很差管理外,还有最重要的一点就是, Java 只能单继承,当若是你的 Activity 须要使用到某个三方库,那个三方库必须让你继承于它的 Activity 可是你又须要你本身封装的 BaseActivity 的某些特性,这时你怎么办? 你不能改三方库的 Activity 因此你只有改你的 BaseActivity 让它去继承三方库的 Activity,可是当改了 BaseActivity 后,发现有不少继承于 BaseActivityActivity 并不须要这个三方库,却被迫继承了它的 Activity ,这时你又要从新封装一个 BaseActivityide

当这种状况发生的多了,你的 BaseActiviy 也愈来愈多,这样就恶性循环了,因此我不只提倡 App 开发者少封装 BaseActivity 少用继承,也提倡 三方库 开发者少封装 BaseActivity 少用继承,为何呢?由于当 App 开发者的 Activity 须要使用到两个三方库,两个三方库都须要继承它的 Activity,这时你让 App 开发者怎么办?因此做为一个可配置化集成框架做者,我不能让开发者去直接改个人 BaseActivity 我必须经过其余扩展的方式去解决这个问题布局

进入正题

好了进入正题,要想解决上面提到的问题,咱们就要思考咱们为何必定要封装 BaseActivity 经过继承来实现一些 Activity 的公共逻辑,而不能将公共逻辑封装到其余类里面?post

答案很简单,由于咱们必须使用到 Activity 的一些生命周期,在对应的生命周期里执行对应的逻辑,这个就是咱们不能经过封装其余类来实现的缘由,找到了问题关键,那咱们就从生命周期上下手来解决问题学习

ActivityLifecycleCallbacks

提到 Activity 的生命周期,这时我就要介绍一个接口了,它叫 ActivityLifecycleCallbacks ,不知道有同窗以前了解过它吗?不了解没关系,我如今来介绍介绍它

ActivityLifecycleCallbacksApplication 中声明的一个内部接口,咱们来看看它的结构

public interface ActivityLifecycleCallbacks {
        void onActivityCreated(Activity activity, Bundle savedInstanceState);
        void onActivityStarted(Activity activity);
        void onActivityResumed(Activity activity);
        void onActivityPaused(Activity activity);
        void onActivityStopped(Activity activity);
        void onActivitySaveInstanceState(Activity activity, Bundle outState);
        void onActivityDestroyed(Activity activity);
    }


复制代码

这个接口有什么用呢?

Application 提供有一个 registerActivityLifecycleCallbacks() 的方法,须要传入的参数就是这个 ActivityLifecycleCallbacks 接口,做用和你猜的没错,就是在你调用这个方法传入这个接口实现类后,系统会在每一个 Activity 执行完对应的生命周期后都调用这个实现类中对应的方法,请记住是每一个!

这个时候咱们就会想到一个需求实现,关闭全部 Activity !你还在经过继承 BaseActivityBaseActivityonCreate 中将这个 Activity 加入集合???

那我如今就告诉你这样的弊端,若是你 App 中打开有其余三方库的 Activity ,这个三方库确定不可能继承你的 BaseActivity ,这时你怎么办?怎么办?

这时 ActivityLifecycleCallbacks 就派上用场了, App 中的全部 Activity 只要执行完生命周期就必定会调用这个接口实现类的对应方法, 那你就能够在 onActivityCreated 中将全部 Activity 加入集合,这样无论你是否是三方库的 Activity 我均可以遍历集合 finish 全部的 Activity

使用 ActivityLifecycleCallbacks 实现 ToolBar

设置 NoActionBar 主题

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
</style>

复制代码
<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme">
    ...
</application>

复制代码

建立 ToolBar 的布局并引用

你们不要纠结个人 Toolbar 布局方式,这里只是实现思想,你能够本身改为本身的布局方式

<me.jessyan.art.widget.autolayout.AutoToolbar
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="120px"
    android:background="?attr/colorPrimary"
    app:contentInsetStart="0dp"
    >

	// toolbar 中的返回按钮
    <com.zhy.autolayout.AutoRelativeLayout
        android:id="@+id/toolbar_back"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:gravity="left"
        >

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="30px"
            android:layout_marginRight="30px"
            android:layout_centerVertical="true"
            android:src="@mipmap/login_return"/>

    </com.zhy.autolayout.AutoRelativeLayout>

	// toolbar 中的标题名
    <TextView
        android:id="@+id/toolbar_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="18dp"
        android:textColor="#fff"
        android:layout_gravity="center"
        tools:text="MVPArt"/>


</me.jessyan.art.widget.autolayout.AutoToolbar>
复制代码

在你须要 ToolbarActivity 布局中 include 上面的布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    >

	//将 Toolbar 包裹进来
    <include layout="@layout/include_title"/>

    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/swipeRefreshLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingLeft="15px"
        android:paddingTop="15px"
        >

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recyclerView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scrollbars="vertical"
            tools:listitem="@layout/recycle_list"
            />

    </android.support.v4.widget.SwipeRefreshLayout>
</LinearLayout>

复制代码

实现 ActivityLifecycleCallbacks 并注册给 Application

下面只是对 ToolBar 简单的设置,你能够本身配置更多复杂的功能,你想象力有多丰富,这里就有多强大

public class WEApplication extends BaseApplication{

    @Override
    public void onCreate() {
        super.onCreate();
        registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
            @Override
            public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
                //这里全局给Activity设置toolbar和title,你想象力有多丰富,这里就有多强大,之前放到BaseActivity的操做均可以放到这里
                if (activity.findViewById(R.id.toolbar) != null) { //找到 Toolbar 而且替换 Actionbar
                    if (activity instanceof AppCompatActivity) {
                        ((AppCompatActivity) activity).setSupportActionBar((Toolbar) activity.findViewById(R.id.toolbar));
                        ((AppCompatActivity) activity).getSupportActionBar().setDisplayShowTitleEnabled(false);
                    } else {
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                            activity.setActionBar((android.widget.Toolbar) activity.findViewById(R.id.toolbar));
                            activity.getActionBar().setDisplayShowTitleEnabled(false);
                        }
                    }
                }
                if (activity.findViewById(R.id.toolbar_title) != null) { //找到 Toolbar 的标题栏并设置标题名
                    ((TextView) activity.findViewById(R.id.toolbar_title)).setText(activity.getTitle());
                }
                if (activity.findViewById(R.id.toolbar_back) != null) { //找到 Toolbar 的返回按钮,而且设置点击事件,点击关闭这个 Activity
                    activity.findViewById(R.id.toolbar_back).setOnClickListener(v -> {
                        activity.onBackPressed();
                    });
                }
            }

            ...
            
        });
    }
}

复制代码

在 AndroidManifest.xml 中设置 Label

之后你想让一个 Activity 实现 ToolBar ,只用作两件事情:

  1. 在布局文件中
<include layout="@layout/include_title"/>
复制代码
  1. AndroidManifest.xml 给这个 Activity 设置 Label ,这个 Label 就是标题名
<activity
            android:name=".mvp.ui.activity.SplashActivity"
            android:label="@string/app_name"
            />

复制代码

再介绍给你们一个全局配置全部 Activity 进入退出过分动画的方法,设置 Theme 属性

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
        <item name="android:windowAnimationStyle">@style/AnimaActivity</item>
    </style>
    <style name="AnimaActivity">
        <item name="android:activityOpenEnterAnimation">@anim/translate_right_to_center</item>
        <item name="android:activityOpenExitAnimation">@anim/translate_center_to_left</item>
        <item name="android:activityCloseEnterAnimation">@anim/translate_left_to_center</item>
        <item name="android:activityCloseExitAnimation">@anim/translate_center_to_right</item>
    </style>

复制代码

Activity 中你根本不用继承任何 Activity ,不用写任何一行代码,就能够实现不少繁琐复杂的功能

不少公共逻辑均可以写到 ActivityLifecycleCallbacks 中,只要勇于尝试,你想象力有多丰富,这里就有多强大

扩展

由于全部 Activity 在执行对应生命周期时, ActivityLifecycleCallbacks 对应的方法都会被调用,有些 Activity 可能不须要 Toolbar ,好比三方库的 Activity ,虽然在 onActivityCreated 方法中,判断了 ToolBarId 找不到就不执行设置 ToolBar 的逻辑,可是未免不够优雅

自定义接口

其实咱们可让 Activity 实现对应的自定义接口, 在 onActivityCreatedinstanceof 这个自定义接口,若是不实现这个自定义接口,就说明不须要设置 ToolBar ,这样就优雅不少

储存数据

ActivityLifecycleCallbacks 中,全部 Activity 执行对应的生命周期后,它对应的方法都会被调用,因此咱们必须区分这个 Activity 究竟是哪一个 Activity ,因此 ActivityLifecycleCallbacks 每一个方法都会传入 Activity 作为参数,咱们就能够用来区分 Activity

public void onActivityCreated(Activity activity, Bundle savedInstanceState){
	if (activity instanceof xxxActivity){
		....
	}

}
复制代码

可是又有一个问题出现这个 ActivityLifecycleCallbacks 是公用的,当一个 ActivityonCreate 方法产生了一个对象 ,咱们须要在这个 Activity 执行 onDestroy 时用到这个对象,怎么办?由于每一个 Activity 都要产生这个对象,咱们不可能把这个对象存储在 ActivityLifecycleCallbacks 中啊

如今就能够用到 Activity.getIntent 来存储一些数据, Intent 中持有一个 Bundle 对象能够存储一些数据,

举个例子

咱们须要使用 ActivityLifecycleCallbacks 实现给全部 Activity 执行 ButterKnife.bind(activity)

Bundle 中能够存储 Parcelable 对象

public class ActivityBean extends Parcelable {
	private Unbinder unbinder;
	public void setUnbinder(Unbinder unbinder){
		thid.unbinder = unbinder;
	}
	
	public Unbinder getUnbinder(){
		return unbinder;
	}
}

复制代码

ActivityLifecycleCallbacks 执行对应逻辑

public class WEApplication extends BaseApplication{

    @Override
    public void onCreate() {
        super.onCreate();
        registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
            @Override
            public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
                ActivityBean bean = new ActivityBean();
            	Unbinder unbinder = ButterKnife.bind(activity);
            	bean.setUnbinder(unbinder);
                activity.getIntent().putExtra("ActivityBean", bean);
           }

            ...
            
            @Override
            public void onActivityDestroyed(Activity activity) {
				ActivityBean bean = activity.getIntent().getParcelableExtra("ActivityBean");
				bean.getUnbinder().unbind();
            }
        }
            
        });
    }
}

复制代码

须要Activity初始化某些事,或者提供某些数据

BaseActivity 有些时候须要,子 Activity 实现某些方法,或者提供某些数据,如须要子 Activity 实现 initView 返回 setContentView() 中的布局 ID ,实现 initData 初始化一些数据,这样就能够不须要 Activity 再重写 onCreate ,达到规范的目的, 这样使用 ActivityLifecycleCallbacks 一样能作到,那我该怎么作呢?

只须要 Activity 实现某个自定义接口

public interface IActivity {

    int initView();

    void initData();

}

复制代码

而后在 ActivityLifecycleCallbacksonActivityCreated 中调用这些方法,就能够实现

public class WEApplication extends BaseApplication{

    @Override
    public void onCreate() {
        super.onCreate();
        registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
            @Override
            public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
            if (activity instanceof IActivity) {
               activity.setContentView(((IActivity)activity).initView());
               ((IActivity)activity).initData();          
            }
              
           }

            ...
       
        }
            
        });
    }
}

复制代码

注意事项

因为 ActivityLifecycleCallbacks 中全部方法的调用时机都是在 Activity 对应生命周期的 Super 方法中进行的,因此在 ActivityonCreate 方法中使用 setContentView 必须在 super.onCreate(savedInstanceState); 以前,否则在 onActivityCreated 方法中 findViewById 会发现找不到

@Override
    protected void onCreate(Bundle savedInstanceState) {
        setContentView(R.layout.activity_home);
        super.onCreate(savedInstanceState);
    }


复制代码

也能够结合上面的方式使用自定义接口,调用 initView 后,在 findViewById() 找到 ToolBar

一不当心实现了这么多之前必须写到 BaseActivity 中经过继承才能达到的功能,发现没有 BaseActivity ,也并无什么不舒服的地方,忽然感受本身好牛逼 🤒,赶快关注我吧,虽然我并不会常常更新博客,可是我更新的文章在质量上绝对有保证!

总结

值得注意的是 ActivityLifecycleCallbacks 能够注册多个,能够针对不一样状况添加各类 ActivityLifecycleCallbacks 按照须要进行组合从而达到不一样的需求 ,和 OkhttpInterceptor 相似

如今整个 App 的全部 ActivityFragment 只要生命周期调用都会被拦截,应用到我框架上就能够动态的向全部 ActivityFragment 的对应生命周期插入任意代码,好比说 LeakCanaryRefWatcher.watch(fragment) 也能够直接插入到三方库的 Fragment 中,而且若是代码有任何改动也不用再去改基类,有点Aop的意思

以上提到的思想以及解决方案已经使用到了个人 MVPArms 框架中,想知道更详细的用法能够去看看个人框架实现,我上面提到的全部的实现,其实都是最简单的一些需求,相信已经颠覆了之前的实现方式了,并且更加优雅(由于我是站在三方库设计者的角度提出这个功能的,我必须将唯一继承的机会留给其余 Activity ,你若是相较于以前的方式以为不够优雅,那就当我没说),更不用担忧 Java 单继承的束缚

可是千万不要觉得, ActivityLifecycleCallbacks 就只能实现这些简单的需求,它还能够用到更多更复杂的功能之上,仍是我以前的话, 只要勇于尝试,你想象力有多丰富,这里就有多强大 ,思想以及解决方案已经介绍的很清楚了,至于更多需求的实现就靠你们去尝试咯,虽然我不敢保证之前封装 BaseActivity 经过继承实现的全部功能都能被 ActivityLifecycleCallbacks 所替代,可是我想大多数功能仍是能实现的

之前你们都是分享本身封装的 BaseActivity ,说不定有一天你们就开发分享本身写的 ActivityLifecycleCallbacks 呢! 当之前的方式已经不能知足咱们的需求,勇于跳出传统的方式,尝试不一样的解决方案,才能扩宽本身的视野,增加本身的技术, MVPArms 正在不断的努力着!


这里还要说一句,每一个人的思路不同,考虑的角度也不同,你认同我也好, 不认同我也好,都不会影响个人脚步,至少我是在用个人思路创新,解决一些我认为有必要解决的问题,和上一篇的文章同样,我就是喜欢使用不同的思路解决一样的问题,无论你是否以为可行,我至少用这个你以为不可行的思路实现了我想达到的效果,仁者见仁智者见智,若是咱们思惟没有碰撞,那也请珍惜个人劳动成果

对于一些评论我再说一句, registerActivityLifecycleCallbacks() 内部是把 ActivityLifecycleCallbacks 加到一个集合中,因此 ActivityLifecycleCallbacks 能够添加多个,而且 ActivityLifecycleCallbacks 只是在项目初始化的时候被装到集合中,并不会初始化任何东西,和添加监听器一个道理,使用的是观察者模式,因此不要说 Application 代码这么多会怎么怎么样, OkhttpInterceptor 的代码更多,也是在 Okhttp 初始化时被添加,你以为会有什么影响吗?

公众号

扫码关注个人公众号 JessYan,一块儿学习进步,若是框架有更新,我也会在公众号上第一时间通知你们


Hello 我叫 JessYan,若是您喜欢个人文章,能够在如下平台关注我

-- The end

相关文章
相关标签/搜索