Android UI 架构-常用的多级配置

1,772 阅读4分钟

UI给了空数据页的图,但是下一个模块又想换style,某些特殊的界面又要特殊定制,好烦!

ToolBar的样式都差不多,但是具体到项目中又时时有所调整,碎片化十分严重,怎么办?

BaseActivity 和 BaseFragment 已经膨胀得快炸掉了,还要往里面加东西?

所以我们需要一种可以灵活配置的手段,进可以全局配置,统一使用,退可以具体到某个Activity,进行个性化定制,并且除了全局Activity,我们还需要更细致的层级,比如位于全局Activity之间的Module等等,它们之间遵循某个规律,高权限可以覆盖低权限,越细致的配置越要做更多的事情,而如果仅仅是使用全局配置,就要令其尽可能少做事情,权限越大,责任越大。

用interface做配置,Lifecycle做切面,ContentProvider做初始化

上述一系列的问题都可以用这套组合拳解决部分问题;

1、对于要配置的属性抽象成interface,给具体的页面实现,当然一些非必要配置的属性可以利用Java8/Kotlin做默认实现;

2、然后在Application.ActivityLifecycleCallbacksFragmentManager.FragmentLifecycleCallbacks中获取到具体某个页面要配置的东西,做具体的配置,我们所说的多级配置的控制也是在这里去实现;

3、最后注册一个ContentProvider,在onCreate中获取到application,然后把上面配置好的Application.ActivityLifecycleCallbacks注册进去,而FragmentManager.FragmentLifecycleCallbacks则是在Application.ActivityLifecycleCallbacksonCreate中注册。

说完理论我们来举个例子吧,比如ToolBar:

1、我们配置三个接口,默认是ToolBarUI是三段式的:

/**
 * 提供给单个Class做特殊配置,最大程度实现灵活配置
 */
interface IInitToolBar {
    fun initToolBar(toolBar: IToolBar?)
}
/**
 * 控制单个Class是否初始化的开关
 */
interface IIsInitToolBar {
    fun initToolBar():Boolean
}
/**
 * ToolBar的具体属性抽象
 */
interface IToolBar {
    fun leftText(): Int? = null
    fun rightText(): Int? = null
    fun leftIcon(): Int? = null
    fun rightIcon(): Int? = null
    fun titleText(): Int? = null
    /**
     * 单位:像素
     */
    fun drawablePadding():Int?=null
    /**
     * 返回true代表被消费
     */
    fun onClickLeft(view: View) = false

    /**
     * 返回true代表被消费
     */
    fun onClickRight(view: View) = false

    /**
     * 初始化并且隐藏
     */
    fun hideToolBar() = false
}

2、提供具体的Application.ActivityLifecycleCallbacksFragmentManager.FragmentLifecycleCallbacks实现:

class ToolBarActivityLifecycleCallbacksImpl :
    DefaultActivityLifecycleCallbacks {

    companion object {
        private val handler = Handler(Looper.getMainLooper())
    }

    override fun onActivityCreated(activity: Activity?, savedInstanceState: Bundle?) {
        activity?.let {
            handler.post {
                /**
                 * 可能会用到toolBar,所以丢到队尾执行,确保initView完成
                 */
                ToolBarManager.initToolBar(it as? IToolBar)
            }
        }
        (activity as? FragmentActivity)?.supportFragmentManager?.registerFragmentLifecycleCallbacks(
            ToolBarFragmentLifecycleCallbacksImpl(),
            true
        )
    }
}

class ToolBarFragmentLifecycleCallbacksImpl : FragmentManager.FragmentLifecycleCallbacks() {
    companion object {
        private val handler = Handler(Looper.getMainLooper())
    }

    override fun onFragmentActivityCreated(
        fm: FragmentManager,
        f: Fragment,
        savedInstanceState: Bundle?
    ) {
        handler.post {
            /**
             * 可能会用到toolBar,所以丢到队尾执行,确保initView完成
             */
            ToolBarManager.initToolBar(f as? IToolBar)
        }
    }
}

3、用ContentProvider初始化可以把初始化工作交给lib实现,不需要在业务工程中手动注册:

class InitProvider : ContentProvider() {

    override fun onCreate(): Boolean {
        (context?.applicationContext as Application).apply {
            registerActivityLifecycleCallbacks(ToolBarActivityLifecycleCallbacksImpl())
        }
        return true
    }
    ...

三级控制+两级配置(级别越大,权限越高)

不难发现,上述配置的具体工作是交由ToolBarManager来完成,文章标题所谓的多级配置也是在这里完成的,我们看一下具体实现:


    override fun initToolBar(toolBar: IToolBar?) {
        //一级控制,全局控制
        var isInitToolBar = defaultIsToolBar
        //二级控制,Class控制
        if (toolBar is IIsInitToolBar) {
            isInitToolBar = toolBar.initToolBar()
        }
        /**
         * 三级控制,Object控制
         */
        isInitToolBar = when (toolBar) {
            is Activity -> {
                toolBar.intent.getBooleanExtra(EXTRA_INIT_TOOLBAR, isInitToolBar).apply {
                    //用完就遗弃,避免脏数据
                    toolBar.intent.removeExtra(EXTRA_INIT_TOOLBAR)
                }
            }
            is Fragment -> {
                toolBar.arguments?.getBoolean(EXTRA_INIT_TOOLBAR, isInitToolBar)?.apply {
                    //用完就遗弃,避免脏数据
                    toolBar.arguments?.remove(EXTRA_INIT_TOOLBAR)
                } ?: isInitToolBar
            }
            else -> {
                isInitToolBar
            }
        }

        if (isInitToolBar) {
            if (toolBar is IInitToolBar) {
                //二级控制,class控制
                toolBar.initToolBar(toolBar)
            } else {
                //一级控制,全局
                defaultInitToolBar.initToolBar(toolBar)
            }
        }

    }

ToolBarManager是个单例,defaultIsToolBar就是可以全局更改的字段,它代表权限最低的全局开关;

IIsInitToolBar只做一件事,那就是开关,它代表权限更高的Class开关;

通过intent/arguments来传递的是最高权限的Object开关,不过用完记得移除脏数据。

具体配置的时候,如果该页面的UI符合三段式UI,那么就沿用全局配置的defaultInitToolBar,否则也可以具体界面实现IInitToolBar,做特殊配置。

根据包名的Module级别

除了上述的3+2之外,我们也可以通过全局Map去缓存一些配置,可以用包名前缀做Key,从而实现按照不同的Module配置不同的样式,也可以通过注解的手段优化开发体验等等。

最后我也写了个demo,包括沉浸式配置、空数据页错误页配置、Loading配置、ToolBar配置,当然,目前还未完善,计划写完用在项目中,待项目完结再发个开源吧。

项目地址:github.com/chinwetang/…