Android|Android ViewBinding使用介绍
目录
- 一、kotlin-android-extensions
- 二、ViewBinding使用
- 1.gradle配置
- 2.在Activity 使用
- 3.在Fragment使用
- 4.在Adapter中使用
- 5.在Dialog中使用
- 6.Include中使用
- 三、ViewBinding封装
- 1.在BaseActivity中封装
- 2.通过反射的方式封装
- 3.反射+基类
- 1.在Activity 中使用
- 2.在Fragment中使用
- 4.委托的方式
一、kotlin-android-extensions 在使用ViewBinding之前,我们一直使用的是kotlin-android-extensions,使用kotlin-android-extensions可以节约很多写findViewById的时间。不过这个kotlin-android-extensions插件已经废弃了,简单说一下kotlin-android-extensions存在的问题:
1.通过反编译kotlin-android-extensions的代码,发现会创建一个HashMap,用来存放所有的id和对应的View的缓存,如果缓存中没有View,那么就通过findViewById去创建并存入缓存,否则就直接获取。所以会存在内存问题。
private HashMap _$_findViewCache; public View _$_findCachedViewById(int var1) {if (this._$_findViewCache == null) {this._$_findViewCache = new HashMap(); }View var2 = (View)this._$_findViewCache.get(var1); if (var2 == null) {View var10000 = this.getView(); if (var10000 == null) {return null; }var2 = var10000.findViewById(var1); this._$_findViewCache.put(var1, var2); }return var2; }public void _$_clearFindViewByIdCache() {if (this._$_findViewCache != null) {this._$_findViewCache.clear(); }}// $FF: synthetic methodpublic void onDestroyView() {super.onDestroyView(); this._$_clearFindViewByIdCache(); }
2.由于kotlin-android-extensions是通过view的id名直接引用的,如果多个布局间的同名id,就需要手动对import进行重命名处理,如果引用错误的布局文件,就会出现crash。所以存在资源重名的问题。
3.只有Kotlin才可以使用。
【Android|Android ViewBinding使用介绍】所以ViewBinding优势有:java,kotlin都可以使用,可以有效避免NullPointerException。
二、ViewBinding使用
1.gradle配置
buildFeatures {开启ViewBinding之后,在编译时,AGP会自动帮我们给每个xml布局创建一个Binding类,位于build/generated/data_binding_base_class_source_out/目录下。
viewBinding true
}
文章图片
public final class FragmentLoginBinding implements ViewBinding {@NonNullprivate final ConstraintLayout rootView; @NonNullpublic final ConstraintLayout container; @NonNullpublic final ProgressBar loading; @NonNullpublic final Button login; @NonNullpublic final EditText password; @NonNullpublic final EditText username; private FragmentLoginBinding(@NonNull ConstraintLayout rootView,@NonNull ConstraintLayout container, @NonNull ProgressBar loading, @NonNull Button login,@NonNull EditText password, @NonNull EditText username) {this.rootView = rootView; this.container = container; this.loading = loading; this.login = login; this.password = password; this.username = username; }@Override@NonNullpublic ConstraintLayout getRoot() {return rootView; }@NonNullpublic static FragmentLoginBinding inflate(@NonNull LayoutInflater inflater) {return inflate(inflater, null, false); }@NonNullpublic static FragmentLoginBinding inflate(@NonNull LayoutInflater inflater,@Nullable ViewGroup parent, boolean attachToParent) {View root = inflater.inflate(R.layout.fragment_login, parent, false); if (attachToParent) {parent.addView(root); }return bind(root); }@NonNullpublic static FragmentLoginBinding bind(@NonNull View rootView) {// The body of this method is generated in a way you would not otherwise write.// This is done to optimize the compiled bytecode for size and performance.int id; missingId: {ConstraintLayout container = (ConstraintLayout) rootView; id = R.id.loading; ProgressBar loading = rootView.findViewById(id); if (loading == null) {break missingId; }id = R.id.login; Button login = rootView.findViewById(id); if (login == null) {break missingId; }id = R.id.password; EditText password = rootView.findViewById(id); if (password == null) {break missingId; }id = R.id.username; EditText username = rootView.findViewById(id); if (username == null) {break missingId; }return new FragmentLoginBinding((ConstraintLayout) rootView, container, loading, login,password, username); }String missingId = rootView.getResources().getResourceName(id); throw new NullPointerException("Missing required view with ID: ".concat(missingId)); }}
注意:
1.因为这些类编译时就生成了,就不会占用运行时内存。
2.未使用的Binding文件会在混淆时被删除,所以对包大小影响很小。
3.编译器生成Binding文件是增量更新的。
那么如何不生成Binding类呢?tools:viewBindingIgnore="true"
2.在Activity 使用
class TestViewBindingActivity : AppCompatActivity() {private lateinit var bindding: ActivityTestViewBindingBindingoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)bindding = ActivityTestViewBindingBinding.inflate(layoutInflater)setContentView(bindding.root)changeText()}private fun changeText() {bindding.titleTv.text = "哈哈,在Activity中使用ViewBinding了"}}
文章图片
3.在Fragment使用
class TextViewBindingFragment : Fragment() {private var param1: String? = nullprivate var param2: String? = nullprivate var _binding: FragmentTextViewBindingBinding? = nullprivate val binding get() = _binding!!override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)arguments?.let {param1 = it.getString(ARG_PARAM1)param2 = it.getString(ARG_PARAM2)}}override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,savedInstanceState: Bundle?): View? {_binding = FragmentTextViewBindingBinding.inflate(layoutInflater, container, false)return binding.root}override fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)changeText()}private fun changeText() {binding.tvTitle.text = "哈哈,在Fragment中使用ViewBinding"}override fun onDestroyView() {super.onDestroyView()_binding = null}companion object {@JvmStaticfun newInstance(param1: String, param2: String) =TextViewBindingFragment().apply {arguments = Bundle().apply {putString(ARG_PARAM1, param1)putString(ARG_PARAM2, param2)}}@JvmStaticfun newInstance() = TextViewBindingFragment()}}
class TestViewBindingActivity : AppCompatActivity() {private lateinit var bindding: ActivityTestViewBindingBindingoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)bindding = ActivityTestViewBindingBinding.inflate(layoutInflater)setContentView(bindding.root)val newInstance = TextViewBindingFragment.newInstance()addFragment(supportFragmentManager,newInstance,isAllowStateLoss = true,frameId = R.id.fragmentFrame)}}
文章图片
4.在Adapter中使用
class TestAdapterActivity : AppCompatActivity() {private lateinit var binding: ActivityTestAdapterBindingoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)binding = ActivityTestAdapterBinding.inflate(layoutInflater)setContentView(binding.root)initView()}companion object {val ITEMS = mutableListOf("1", "2", "3", "4", "5", "6")}private fun initView() {with(binding.contentRcycler) {layoutManager = GridLayoutManager(context, 4)adapter = TestRecyclerViewAdapter(ITEMS)}}}
class TestRecyclerViewAdapter(private val values: List) :RecyclerView.Adapter() {inner class ViewHolder(binding: RecyclerItemLayoutBinding) :RecyclerView.ViewHolder(binding.root) {val textTv = binding.contentTv}override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {return ViewHolder(RecyclerItemLayoutBinding.inflate(LayoutInflater.from(parent.context),parent,false))}override fun onBindViewHolder(holder: ViewHolder, position: Int) {val item = values[position]holder.textTv.text = item}override fun getItemCount(): Int = values.size}
文章图片
5.在Dialog中使用
class CommonDialog(context: Context) : Dialog(context) {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(DialogLayoutBinding.inflate(layoutInflater).root)}}
文章图片
6.Include中使用
class TestIncludeActivity : AppCompatActivity() {private lateinit var binding: ActivityTestIncludeBindingoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)binding = ActivityTestIncludeBinding.inflate(layoutInflater)setContentView(binding.root)initView()}private fun initView() {binding.itemInclude.itemContentTv.text = "哈哈, this is include"}}
文章图片
三、ViewBinding封装
1.在BaseActivity中封装
abstract class BaseViewBindingActivity: AppCompatActivity() {protected val binding by lazy {getViewBinding()}override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(binding.root)}protected abstract fun getViewBinding(): T}
class ChildViewBindingMainActivity :BaseViewBindingActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)binding.titleTv.text = "哈哈,this is child binding activity"}override fun getViewBinding(): ActivityChildViewBindingMainBinding {return ActivityChildViewBindingMainBinding.inflate(layoutInflater)}}
文章图片
2.通过反射的方式封装
class TestViewBindingMainActivity : AppCompatActivity() {private val binding by inflate()override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)binding.titleTv.text = "哈哈,通过反射封装ViewBinding"}}inline funinflateByViewBinding(layoutInflater: LayoutInflater) =T::class.java.getMethod("inflate", LayoutInflater::class.java).invoke(null, layoutInflater) as Tinline fun Activity.inflate() = lazy {inflateByViewBinding (layoutInflater).apply {setContentView(root)}}
文章图片
3.反射+基类
1.在Activity 中使用
abstract class BaseBindingMainActivity2: AppCompatActivity() {protected lateinit var binding: Toverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)val type = javaClass.genericSuperclassif (type is ParameterizedType) {val clazz = type.actualTypeArguments[0] as Class val method = clazz.getMethod("inflate", LayoutInflater::class.java)binding = method.invoke(null, layoutInflater) as T}setContentView(binding.root)}}
class ChildViewBindingMainActivity2 :BaseBindingMainActivity2() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)binding.titleTv.text = "哈哈,这是反射+基类的方式"}}
文章图片
2.在Fragment中使用
abstract class BaseBindingViewFragment: Fragment() {private var _binding: T? = nullprotected val binding get() = _binding!!override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,savedInstanceState: Bundle?): View? {val type = javaClass.genericSuperclassval clazz = (type as ParameterizedType).actualTypeArguments[0] as Class val method = clazz.getMethod("inflate",LayoutInflater::class.java,ViewGroup::class.java,Boolean::class.java)_binding = method.invoke(null, layoutInflater, container, false) as Tthis.viewLifecycleOwner.lifecycle.addObserver(object : LifecycleEventObserver {override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {if (event == Lifecycle.Event.ON_DESTROY) {Log.v(TAG, "onDestroy binding be null")_binding = null}}})return binding.root}companion object {const val TAG = "BaseBindingViewFragment"}}
class ChildBindingFragment : BaseBindingViewFragment() {override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,savedInstanceState: Bundle?): View? {return super.onCreateView(inflater, container, savedInstanceState)}companion object {@JvmStaticfun newInstance() = ChildBindingFragment()}}
class TestBindingMainActivity3 : BaseBindingMainActivity2() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)val newInstance = ChildBindingFragment.newInstance()addFragment(supportFragmentManager,newInstance,isAllowStateLoss = true,frameId = R.id.frame)}}
文章图片
4.委托的方式
class TestViewBindingFragment2 : Fragment(R.layout.fragment_test_view_binding2) {private val binding by inflate()override fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)binding.root}companion object {@JvmStaticfun newInstance() = TestViewBindingFragment2()}}inline fun Fragment.inflate() =FragmentViewBindingDelegate(T::class.java)class FragmentViewBindingDelegate (private val clazz: Class ) :ReadOnlyProperty {private var binding: T? = nulloverride fun getValue(thisRef: Fragment, property: KProperty<*>): T {if (binding == null) {binding =clazz.getMethod("bind", View::class.java).invoke(null, thisRef.requireView()) as TthisRef.viewLifecycleOwner.lifecycle.addObserver(object : LifecycleEventObserver {override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {if (event == Lifecycle.Event.ON_DESTROY) {binding = null}}})}return binding!!}}
文章图片
到此这篇关于Android ViewBinding使用介绍的文章就介绍到这了,更多相关Android ViewBinding内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
推荐阅读
- Android三方依赖冲突Gradle中exclude的使用
- ViewBinding|ViewBinding 与 Kotlin 委托双剑合璧
- uni-app|uni-app中使用自定义组件全局化.....
- ShareSDK Android端分享与授权示例代码
- #|Mybatis的if else妙用(Choose标签使用)
- java|如何在 ACK 中使用 MSE Ingress
- Unity3D|Unity3D 中UGUI事件系统简述及使用方法总结
- vue.js|使用vue-cli搭建SPA项目
- axios|axios基本的使用
- Android|Android资源管理框架(编译和打包)