献给android原生应用层开发初学者技术架构选型和整合的方案思路(七)

续前篇《献给android原生应用层开发初学者技术架构选型和整合的方案思路(六)》,本篇想作为终结篇,着重表达下 UI 的集成和一些注意点。

  1. 修改启动项LauncherActivity在AndroidManifest.xml中的注册配置,修改 theme 主题为 NoActionBar,因为我们仅把此对象作为启动入口项,不需要任何 UI。只在里面做一点逻辑业务而已,比如申请权限,跳转到业务activity等等。配置如下:
            <activity android:name=".activities.LauncherActivity"
                      android:theme="@style/Theme.AppCompat.Light.NoActionBar">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN"/>
                    <action android:name="android.intent.action.VIEW"/>
                    <category android:name="android.intent.category.LAUNCHER"/>
                </intent-filter>
            </activity>

    其中 action为View是 IDE 提示至少需要一个,关于 action intent 请点击查阅

  2. 在 activities 这个包下面新建一个业务上的 Activity。在 android stuido 中右击 activities然后选择 New--Activity--Empty Activity,取名 LoginActivity。需要勾选Generate Layout File,Language为 Kotlin。兼容 AppCompact 是否勾选不是必需的,因为我们会让 LoginActivity继承CoreActivity,而这个BaseActivity 是兼容AppCompact 的。注册配置如下:
            <activity android:name=".activities.LogInActivity"              
                      android:configChanges="keyboardHidden|orientation|screenSize"
                      android:theme="@style/SwipeBackActivityTheme"
                      android:windowSoftInputMode="stateAlwaysHidden|adjustResize">
            </activity>

    其中,windowSoftInputMode配置键盘弹出策略,请参考《windowSoftInputMode属性解析》,configChanges配置 UI调整由哪几个事件触发,比如 orientation 屏幕旋转,且配置了此项后,activity 的生命周期不再经历 destroy再 re-new 一个新的实例而给程序员带来要恢复 UI 及数据等比较高深的工作。此时会触发如下回调,您可以 override它实现自己的需求:

        /**
         * 屏幕旋转等 UI 改变时不销毁对象实例,而是会回调此方法
         * android:configChanges="keyboardHidden|orientation|screenSize"
         */
        override fun onConfigurationChanged(newConfig: Configuration?) {
            super.onConfigurationChanged(newConfig)
        }

    当然,如果你没有配置此项导致 activity在屏幕旋转等操作中被 destroy 又 re-new,此时也不必太担心,因为 CoreActivity 底层的 MvRx MVVM库已经做好了恢复数据状态(restore data state)及 UI 的工作(当然您没有用 mvrx的 data state 记录 数据仍然需要自己管理,否则也会消失)。这个需要您熟悉 android 组件的生命周期各个回调函数的知识才能处理得好,请查阅mvrx有关acitivity封装的源代码。另外配置的SwipeBackActivityTheme会在后面提及,用来处理启用swipeback带来的问题。

  3. 由于我们的架构是采用单 Activity+多个 Fragment 的形式(activity 尽量少,可以是多个),尽量只用Activity来承载 Fragments业务模块并大量复用 Fragments 来形成业务模块跳转流程,所以,在 LogInActivity的 xml的布局中我们基本上只需要配置一个FrameLayout就可以用来安顿 Fragment 的展示了。
    <?xml version="1.0" encoding="utf-8"?>
    <android.support.constraint.ConstraintLayout
            xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:tools="http://schemas.android.com/tools"
            xmlns:app="http://schemas.android.com/apk/res-auto"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:context=".activities.LogInActivity">
        <com.demo.mvvm.general.core.ui.WindowInsetsFrameLayout
                android:id="@+id/mContainer"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
        >
        </com.demo.mvvm.general.core.ui.WindowInsetsFrameLayout>
    </android.support.constraint.ConstraintLayout>

    id 配置为 mContainer,并且作为以下方法的返回值,是集成的 Fragmatation 库的方法中用到的值,多个 fragments栈管理用到的容器值:

    override fun getContextViewId(): Int = R.id.mContainer

     

  4. 上面mContainer的类类型是我们封装的WindowInsetsFrameLayout,主要是为了解决fitsSystemWindows = true 无效的现象,而fitsSystemWindows = true是为了启用沉浸式状态栏,如果失效,我们只能在第一个加载的 Fragment 中看到 header 加宽,后续显示的Fragment 的 header 和 status bar 重叠。解决方案请参阅《Android Fragment 布局使用 fitsSystemWindows = true 无效解决方案》。android:fitsSystemWindows="true"这个配置项我们会在具体的 Fragment 的 xml 布局中用到。
  5. 在 Activity 上面打上 ARouter的路由注解如:
    @Route(path = RouterPath.activity_login)

    实现路由和组件类的绑定,您需要先脑补 ARouter 的相关知识,如编译期生成代码技术APT,此处只带领如何使用。在您后续编写 epoxy 需要的View 组件时,也需要先编译下才可使用,此处也用到了APT。

  6. 在一个 Activity 中加载首个 Fragment时,您都必须在第一次调用loadRootFragmentView方法加载第一个业务 Fragment,且要保证getContextViewId的返回值是一个正整数。
  7. 有关权限申请的部分,示例操作如下:
        <uses-permission android:name="android.permission.INTERNET"/>
        <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
        <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
        <uses-permission android:name="android.permission.CAMERA"/>
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

    先在AndroidManifest.xml里面配置您的程序需要的权限列表(如上代码段),然后可采用以下代码申请并处理用户的选择操作:

        /**
         * 打开 app第一个 activity时要发起权限请求,常用在启动页
         */
        private fun requestPermissions() {
            //获取未经同意的权限列表,过滤掉已经经过用户同意的,无需用户再次确认
            val unGrantedPerms =
                PermissionUtils.getPermissions().filter { !PermissionUtils.isGranted(it) }.toTypedArray()
            if (unGrantedPerms.isEmpty()) {
                return
            }
    
            val launchPerms = RxPermissions(this)
                .requestEach(
                    *unGrantedPerms
                )
                .bindToLifecycle(this)
                .subscribe { permission ->
                    when {
                        permission.granted -> {
                        }
                        permission.shouldShowRequestPermissionRationale -> {
    
                        }
                        else -> {
                        }
                    }
                }
        }

    此处逻辑是先过滤过用户已经授权的列表,留下未授权的再次请求权限。用到了 androidUtilCode库和 rxPermisson2库的操作,rxJava2流式代码风格以及 bindToLifeCycle 解决可能出现的内存泄露问题。rxPermisson2不是必须的,您可以采用androidUtilCode中有关权限申请的方法。

  8. 通过QMUI 的集成实现全局 theme 的修改。和解决集成 Swipeback 功能时的坑。修改 res/styles.xml 中原来的 AppTheme parent继承parent="QMUI.Compat.NoActionBar",一般可以照抄 QMUI 里的 demo 代码再自己修改以实现全局主题的替换。
    <resources>
    
        <!-- Base application theme. -->
        <style name="AppTheme" parent="QMUI.Compat.NoActionBar">
    。。。
    。。。
        </style>
        <style name="SwipeBackActivityTheme" parent="AppTheme">
            <item name="android:windowIsTranslucent">true</item>
        </style>
    </resources>

    在需要滑动退出的 Activity 中需要在 AndroidManifest.xml修改绑定的Theme 为上面的SwipeBackActivityTheme,其实是在 AppTheme 的基础上添加了背景透明android:windowIsTranslucent = true,不然滑动时背景黑屏(Fragmatation库在 Github的 ReadMe 中有说明)。

  9. 在创建的业务Fragment 中,继承 CoreFragment 后,在通过注解注册 Arouter 的 url 后,我们需要实例化recycleView和将其与 epoxyController 绑定才实现 date state 与 epoxy 的响应,另外如果当前 Fragment 需要实现 swipeback,还要关联attachToSwipeBack方法:
    @Route(path = RouterPath.fragment_account_login)
    class AccountLogInFragment : CoreFragment() {
    
        private val viewModel by fragmentViewModel(AccountLogInViewModel::class)
    
    //    @JvmField
    //    @Autowired(name = MvRx.KEY_ARG)
    //    var params: Account? = null
    
        private val params: Account by args()
    
    
        override fun getContextViewId(): Int = R.layout.fragment_account_login
    
        override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    
            val view = inflater.inflate(R.layout.fragment_account_login, container, false).apply {
                recycleViewInstance = findViewById(R.id.mAccountLoginRecycleView)
                recycleViewInstance.setController(epoxyControllerInstance)
            }
            //配置当前fragment与滑动退出功能进行视图关联
            return attachToSwipeBack(view)
        }
    }

    值得注意的是,在 onCreateView 生命周期方法中,生成的view尚未返回给系统,故无法使用 kotlin 的 android 扩展功能直接把 id 当作成员变量操作,在 apply 方法中,我们仍然需要使用 findViewById 方法。 getContextViewId 为当前 fragment 的 xml RId,调用者传入的参数我们用mvRx 的 args()作为by 委托可提取出参数对象,关键点在于 key都为 MvRx.KEY_ARG.如果您要使用 ARouter 的参数自动注入的功能,类似于 spring的@Autowired注解,在 Kotlin类中还需要配上@JvmField

  10. 如果你在 Fragment 中集成了 attachToSwipeBack(view)但是不想启用滑动退出,请调用setSwipeBackEnable(false),另外就是您得解决一些内存泄露的问题,比如:
    RxToast.error(context?.applicationContext!!, it.message ?: "", Toast.LENGTH_SHORT, true).show()

    context 采用applicationContext而不是当前 activity,主要是 Toast 在显示时如果已经退出了 activity,由于引用关系不会 release 此对象的实例内存,故产生 memory leak,基本在在代码里除了有关 layout 的,都可以使用applicationContext。

  11. 在 componets 中封装了一些基本的 loading UI,暂时基于 QMUI 的小组件,您可 replece这些公用的组件。

以上算是大体的讲解完结了,请读者还需要掌握那些 lib 的使用,和 Kotlin语法,再根据本项目 debug 走下流程,才能觉得得更好。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值