Hacer que Android View Binding sea conveniente con Kotlin

¡Hola! Me llamo Kirill Rozov. Soy el autor del canal Telegram Android Broadcast . Amo mucho a Kotlin y me gusta usar sus funciones para simplificar el desarrollo. Recientemente me enfrenté a este problema cuando comenzaron a usar el enlace de vista en un nuevo proyecto de Android .


imagen


Esta característica apareció en Android Studio 3.6, pero de hecho no es completamente nueva, sino una versión ligera de Android Data Binding . ¿Por qué tantas complicaciones? El problema era la velocidad: muchos desarrolladores Android Data Bindingsolo usaban para generar código con enlaces a Ver e ignoraban otras características de la biblioteca. Para acelerar la generación de código, creado View Binding. Sin embargo, la forma estándar de trabajar con él es duplicar el código del que desea deshacerse.


La forma estándar de trabajar con View Binding


Veamos el enlace de vista usando el ejemplo Fragmento. Tenemos un recurso de diseño con un nombre profile.xml(su contenido no importa). Si queremos usar ViewBinding, en la versión estándar se verá así:


class ProfileFragment : Fragment(R.layout.profile) {

    private var viewBinding: ProfileBinding? = null

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        viewBinding = ProfileBinding.bind(view)
        //   viewBinding
    }

    override fun onDestroyView() {
        super.onDestroyView()
        viewBinding = null
    }
}

Hay varios problemas aquí:


  • Mucho código extra
  • : Fragment
  • Property viewBinding nullable .

C Kotlin


Kotlin Delegated Property


property Kotlin . , ViewBinding. , ViewBinding :


class FragmentViewBindingProperty<T : ViewBinding>(
    private val viewBinder: ViewBinder<T>
) : ReadOnlyProperty<Fragment, T> {

    internal var viewBinding: T? = null
    private val lifecycleObserver = BindingLifecycleObserver()

    @MainThread
    override fun getValue(thisRef: Fragment, property: KProperty<*>): T {
        checkIsMainThread()
        this.viewBinding?.let { return it }

        val view = thisRef.requireView()
        thisRef.viewLifecycleOwner.lifecycle.addObserver(lifecycleObserver)
        return viewBinder.bind(view).also { vb -> this.viewBinding = vb }
    }

    private inner class BindingLifecycleObserver : DefaultLifecycleObserver {

        @MainThread
        override fun onDestroy(owner: LifecycleOwner) {
            owner.lifecycle.removeObserver(this)
            viewBinding = null
        }
    }
}

-, :


inline fun <reified T : ViewBinding> Fragment.viewBinding(): ReadOnlyProperty<Fragment, T> {
    return FragmentViewBindingProperty(DefaultViewBinder(T::class.java))
}

:


class ProfileFragment() : Fragment(R.layout.profile) {

    private val viewBinding: ProfileBinding by viewBinding()

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

, , . ?


, - ...


- View :


class ProfileFragment() : Fragment(R.layout.profile) {

    private val viewBinding: ProfileBinding by viewBinding()

    override fun onDestroyView() {
        super.onDestroyView()
        //  View  viewBinding
    }
}

, ViewBinding property . super.onDestroyView() . Fragment.viewLifecycleOwner.


ON_DESTROY Fragment.viewLifecycleOwner Fragment.onDestroyView(), FragmentViewBindingProperty , . . , Handler:


class FragmentViewBindingProperty<T : ViewBinding>(...) : ReadOnlyProperty<Fragment, T> {

    internal var viewBinding: T? = null

    private inner class BindingLifecycleObserver : DefaultLifecycleObserver {

        private val mainHandler = Handler(Looper.getMainLooper())

        @MainThread
        override fun onDestroy(owner: LifecycleOwner) {
            owner.lifecycle.removeObserver(this)
            mainHandler.post { viewBinding = null }
        }
    }
}

.


All Articles