代码重构之美

随着代码量增多,越来越觉得有必要构建fragment基类,今天实在受不了,于是重构开始…

注入viewmodel

实际上是要真正意义上对viewmodel进行自动注入的,由于我使用的是viewmodelFactory需要传参所以这里没有实现注入

BaseVmFragment.kt

package com.example.module_main.base

import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModel

//虽然没法注入viewmodel但是可以使写法一眼知道碎片对应的ViewModel是什么
abstract class BaseVmFragment<VM: ViewModel>: Fragment() {
    //这里的VM并没有实际意义,没有使用到,仅仅用于可观性,让人一眼就知道碎片所对应的viewmodel
    lateinit var mActivity: AppCompatActivity


    //这个属性用来是否处理 使用jetpack navigation组件导航后,fragment是否重新执行onCreateView方法,重新执行就要重新初始化,之前初始化的状态丢失
    abstract var isHandleFragmentAgainOnCreateView: Boolean

    protected var isNavigationViewInit = false//记录是否已经初始化过一次视图
    protected var lastView: View? = null//记录上次创建的view

    //在Fragment直接只用getActivity()获得上下文有时会得到空引用,所以需要重写onAttach方法拿到上下文
    override fun onAttach(context: Context) {
        super.onAttach(context)
        mActivity = activity as AppCompatActivity
        onFragmentAttach()
    }

    /*
    * 在fragment 贴附时执行的方法,其实就是onAttach()罢了,只不过我们想要原来的onAttach方法获取上下文对象罢了
    * */
    abstract fun onFragmentAttach()

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? = inflater.inflate(layoutId(),container,false)

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        if(isHandleFragmentAgainOnCreateView){
            if(!isNavigationViewInit){
                init(savedInstanceState)
                isNavigationViewInit = true
            }
        }else{
            init(savedInstanceState)
        }
    }



    fun init(savedInstanceState: Bundle?){

        initBeforeBinding(savedInstanceState)
        initBinding(savedInstanceState)
        initAfterBinding(savedInstanceState)
    }

    /*
    * 可以初始化自己想要的设置
    * */
    abstract fun initAfterBinding(savedInstanceState: Bundle?)

    /*
    * 可以初始化如容器转换的设置
    * */
    abstract fun initBeforeBinding(savedInstanceState: Bundle?)

    /*
    * 初始化绑定事件
    * */
    abstract fun initBinding(savedInstanceState: Bundle?)


    /*
    * 布局资源如R.layout.fragment_test
    * */
    abstract fun layoutId(): Int
}

注入DataBinding

BaseVmDbFragment.kt

package com.example.module_main.base

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.databinding.ViewDataBinding
import androidx.lifecycle.ViewModel

//自动注入viewmodel和databinding
abstract class BaseVmDbFragment<VM : ViewModel, DB : ViewDataBinding> : BaseVmFragment<VM>() {
    private var _binding: DB? = null
    val mBinding: DB get() = _binding!!

    //这个属性用来是否处理 使用jetpack navigation组件导航后,fragment是否重新执行onCreateView方法,重新执行就要重新初始化,之前初始化的状态丢失
    abstract override var isHandleFragmentAgainOnCreateView: Boolean


    //注意这里重写方法会直接覆盖BaseVmFragment,因此不用担心BaseVmFragme里onCreateView方法下实现的方法有影响
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        if (isHandleFragmentAgainOnCreateView) {
            //通过这种方式解决解决Android jetpack导航组件Navigation返回Fragment重走onCreateView方法刷新视图的问题
            if (lastView == null) {
                _binding = DataBindingUtil.inflate(inflater, layoutId(), container, false)
                //mBinding.lifecycleOwner = this
                // 因为这里和数据相关联着,所以这行一定要注释,否则会出现崩溃,这行正确做法应该放到onViewCreated()方法中!!!
                lastView = mBinding.root
            }
            return lastView
        } else {
            _binding = DataBindingUtil.inflate(inflater, layoutId(), container, false)
            mBinding.lifecycleOwner = this
            lastView = mBinding.root
            return lastView
        }



    }


    override fun onDestroy() {
        super.onDestroy()
        onFragmentDestroy()
        _binding = null
    }

    //- - - 注意以下两个方法除非是特殊需要否则直接覆盖后不执行操作即可!!!之所以把方法暴露出来是为了防止有特殊需求 ^.^

    /*
    * 在fragment销毁时执行的方法,其实就是onDestroy()罢了,只不过我们想要原来的onDestory()方法执行_binding = null操作而已
    * */
    abstract fun onFragmentDestroy()

    /*
    * 在fragment 贴附时执行的方法,其实就是onAttach()罢了,只不过我们想要原来的onAttach方法获取上下文对象罢了
    * */
    abstract override fun onFragmentAttach()

}

fragment基类的创建

BaseFragment.kt

package com.example.module_main.base

import android.os.Bundle
import androidx.databinding.ViewDataBinding
import androidx.lifecycle.ViewModel

//创建这个类没有实际作用,主要就是为了看起来好看,暴露所有需要或者重要的方法给使用者看, 老强迫症了
abstract class BaseFragment<VM: ViewModel, DB: ViewDataBinding>: BaseVmDbFragment<VM, DB>(){
    //- - - 注意以下onFragmentDestroy和onFragmentAttach两个方法通常覆盖后不操作!!!   之所以把方法暴露出来是为了防止有特殊需求 ^.^
    //- - - layoutId方法必须覆盖!!!


    /*
    * 这个属性用来是否处理 使用jetpack navigation组件导航后,fragment是否重新执行onCreateView方法,重新执行就要重新初始化,之前初始化的状态丢失
    * 为false表示不处理fragment重新初始化
    * 为true表示处理fragment重新初始化
    * */
    abstract override var isHandleFragmentAgainOnCreateView: Boolean

    /*
    * 在fragment销毁时执行的方法,其实就是onDestroy()罢了,只不过我们想要原来的onDestory()方法执行_binding = null操作而已
    * 在onDestory()方法内执行
    * */
    abstract override fun onFragmentDestroy()

    /*
    * 在fragment 贴附时执行的方法,其实就是onAttach()罢了,只不过我们想要原来的onAttach方法获取上下文对象罢了
    * 在onAttach()方法内执行
    * */
    abstract override fun onFragmentAttach()

    /**
     * 当前Fragment绑定的视图布局
     */
    abstract override fun layoutId(): Int


    /**
     * Fragment执行onViewCreated后触发,可以初始化如容器转换的设置,如果没有容器转换或fragment的进出动画要设置可以不初始化这个方法内容
     * 在initBinding方法前调用
     */
    abstract override fun initBeforeBinding(savedInstanceState: Bundle?)

    /*
    * 初始化绑定事件
    * */
    abstract override fun initBinding(savedInstanceState: Bundle?)

    /*
    * 初始化除了绑定操作外用户想自己定义的内容
    * 在initBinding方法后调用
    * */
    abstract override fun initAfterBinding(savedInstanceState: Bundle?)
}