32

利用 ViewBinding 和反射封装的基类,从此再也不用 findViewById 了

 4 years ago
source link: http://mp.weixin.qq.com/s?__biz=MzIxNzU1Nzk3OQ%3D%3D&%3Bmid=2247489982&%3Bidx=1&%3Bsn=ecf4406143b5db78f06a457bbe6437c4
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

code小生 一个专注大前端领域的技术平台 公众号回复 Android 加入安卓技术群

作者:段颖超丨乐拼

链接:https://www.jianshu.com/p/ea395a83c666

声明:本文已获 段颖超丨乐拼 授权发表,转发等请联系原作者授权

今天从Google公众号了解到关于ViewBinding的相关文章,赶上手头项目正在做优化,于是结合反射封装了BaseActivity和BaseFragment,记录下来同时和大家一起探讨学习。先简单介绍下ViewBinding:

从 Android Studio 3.6 开始,ViewBinding能够通过生成绑定对象来替代 findViewById,从而可以帮你简化代码,并且从 findViewById 的模版代码中解脱出来。ViewBinding的主要优点是所有绑定类都是由Gradle插件生成的,因此它对构建时间没有影响,并且具有编译时安全性。

ViewBinding与DataBinding的区别:

DataBinding 仅处理使用 代码创建的数据绑定布局;ViewBinding 不支持布局变量或布局表达式,因此它不能用于在 XML 中将布局与数据绑定。

ViewBinding相较ButterKnife、Kotlin Android Extensions的几点优势:

目前Android开发中完成View映射的方法主要有 findViewById、 ButterKnife, 如果使用kotlin的话还可以使用Kotlin Android Extensions。

偷个懒,直接引用下其它文章中的描述:

Type safety:findViewById, ButterKnife 均存在类型转换问题,例如不小心将一个TextView错误的赋值给一个Button变量,都会报错,这一错误很容易出现,关键在错误还出现在运行时,而不是编译时!而ViewBinding中,产生的binding类中的属性是依据XML layout文件生成的,所以类型不会错,生成的时候已经处理好了。

Null safety: findViewById, ButterKnife与Kotlin Android Extensions 均存在Null不安全问题。这个什么意思呢?就是在我们访问那个View的时候它不存在。为什么会出现这种情况呢?例如不小心使用了错误的Id,或者访问的时候那个view还不存在。使用了错误Id这个估计大家都有此类经历,但是访问时候那个view不存在怎么理解呢?例如我们在手机横屏和竖屏的时候分别使用一套XML layout文件,假设横屏中包含了一个竖屏中没有的view,那么在屏幕从横屏旋转到竖屏的时候,NullPointer问题就出现了。而ViewBinding中, 产生的binding类中的属性是依据XML layout文件生成的,所以Id不会错。而且其将仅存在某一个配置下的layout文件的那些view对应的字段标记为@Nullable,而且,生成类中还会很贴心的给你加上详细的注释。这一切都是为了提醒程序员,注意对这个view特别处理,它在某些情况下为Null。

简洁优雅:将绑定view的模板代码自动生成到了其他类中,使controlor类(Activity,Fragment)更加清晰了。

原文链接: https://blog.csdn.net/ShuSheng0007/article/details/103246642

ViewBinding的常规使用:

  1. 在 build.gradle 中开启视图绑定 开启视图绑定无须引入额外依赖,从 Android Studio 3.6 开始,视图绑定将会内建于 Android Gradle 插件中。需要打开视图绑定的话,只需要在 build.gradle 文件中配置 viewBinding 选项:

// 需要 Android Gradle Plugin 3.6.0
android {
	viewBinding {
		enabled =true
	}
}

在 Android Studio 4.0 中,viewBinding 变成属性被整合到了 buildFeatures 选项中,所以配置要改成:

//Android Studio 4.0
android{
	buildFeatures{
		viewBinding=true
	}
}

配置完成后,视图绑定就会根据现有的 XML 文件,为 Module 内所有的布局文件生成绑定对象。无须修改原有布局的 XML 文件,视图绑定将根据你现有的布局自动完成所有工作。你可以在任何需要填充布局的地方使用绑定对象,比如 Fragment、Activity、甚至是 RecyclerView Adapter(或者说是 ViewHolder 中)。

  1. 在 Activity 中使用视图绑定 假如你有一个布局文件名叫 activity_my.xml,其中包含了一个按钮和两个文本视图。视图绑定会为这个布局生成一个名叫 ActivityMyBinding 的类,布局文件中所有拥有 id 的视图,都会在这个类中有一个对应的属性:

overridefunonCreate(savedInstanceState:Bundle?){
	super.onCreate(savedInstanceState)
	val binding = ActivityMyBinding.inflate(layoutInflater)
	binding.title.text ="Hello"
	binding.subtext.text ="World"
	binding.button.setOnClickListener {
		/* ... */
	}
	setContentView(binding.root)
}
  1. 在 Fragment,RecyclerView Adapter等的使用可参考文章:

https://blog.csdn.net/fly_with_24/article/details/104337067

————————————————

BaseActivity的封装和使用:

abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
    lateinit var binding: VB
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //利用反射,调用指定ViewBinding中的inflate方法填充视图
        val type = javaClass.genericSuperclass
        if (type is ParameterizedType) {
            val clazz = type.actualTypeArguments[0] as Class<VB>
            val method = clazz.getMethod("inflate", LayoutInflater::class.java)
            binding = method.invoke(null, layoutInflater) as VB
            setContentView(binding.root)
        }

        init()
    }

    abstract fun init()
}

此时,我们的Activity的使用就比较简单了:

class MyActivity : BaseActivity<ActivityMyBinding>() {
    override fun init() {
        binding.title.text ="Hello"
        binding.subtext.text ="World"
        binding.button.setOnClickListener {
            /* ... */
        }
        /* ... */
    }
}

BaseFragment的封装和使用:

abstract class BaseFragment<VB : ViewBinding> : Fragment() {
    private var _binding : VB? = null
    val binding :VB get() = _binding!!

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        //利用反射,调用指定ViewBinding中的inflate方法填充视图
        val type = javaClass.genericSuperclass
        val clazz = (type as ParameterizedType).actualTypeArguments[0] as Class<VB>
        val method = clazz.getMethod("inflate", LayoutInflater::class.java, ViewGroup::class.java, Boolean::class.java)
        _binding = method.invoke(null, layoutInflater, container, false) as VB
        return _binding!!.root
    }

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

    abstract fun init()

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

接着在我们的Fragment使用吧!

class MyFragment : BaseFragment<FragmentMyBinding>() {
    override fun init() {
        binding.title.text ="Hello"
        binding.subtext.text ="World"
        binding.button.setOnClickListener {
            /* ... */
        }
        /* ... */
    }
}

哈哈,世界清净了!!!

本文仅作为基类封装的参考,未考虑其它因素,欢迎各位给出己见,共同进步。

写作不易,如有帮助,还望点个赞。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK