• Android ViewBinding和DataBinding的几个使用方式 - 上


    往期文章分享

    本文约10千字,新手阅读需要14分钟,复习需要3分钟收藏随时查阅不再迷路

    👉关于作者

    众所周知,人生是一个漫长的流程,不断克服困难,不断反思前进的过程。在这个过程中会产生很多对于人生的质疑和思考,于是我决定将自己的思考,经验和故事全部分享出来,以此寻找共鸣 !!!
    专注于Android/Unity和各种游戏开发技巧,以及各种资源分享(网站、工具、素材、源码、游戏等)
    有什么需要欢迎私我,交流群让学习不再孤单

    在这里插入图片描述

    👉前提

    这是小空坚持写的Android新手向系列,欢迎品尝。

    大佬(√)

    新手(√√√)

    👉实践过程

    😜查找控件的几种方式

    说这个之前我们先谈论下有几种查找控件的方式:

    1. findViewById,没错。最原始古老也是最基本的拿到控件的方法,刚学Android的同学一般最先会的就是这个,兼容性好,适用于所有的场景,当你刚学Android或不知道其他方案的时候,使用findViewById绝对没错。除了代码冗余一点,书写麻烦一点(不过也可以在Android Studio插件市场找到自动写findViewById的插件)。
    2. ButterKnife,这是在官方findViewById之后最先出来的第三方框架,需要进行依赖,简化代码,提升效率,增加了代码的可读性,不过也并没有解决和findViewById一样的缺点,而且作者已经三四年没更新,明确说明已经废弃。只不过不妨碍使用,尤其是老程序员以及老项目,不用没关系,看到了一定要知道这是啥。https://github.com/JakeWharton/butterknife
    3. kotlin-android-extensions,不仅要依赖三方库,而且只限于kotlin语言中使用,布局中写好控件,代码中可以直接引用,但是同样官方已经准备废弃了,老项目见到要知道是啥就行。原理还是findViewById。
    4. viewbinding和databinding,这是为了优化findViewById提出的另一个思路。通过视图绑定功能,就可以很轻松地实现与视图交互的代码。当启动后,系统会为该模块中的每个 XML 布局文件生成一个绑定类。绑定类的实例包含对在相应布局中具有 ID 的所有视图的直接引用。我们今天这篇文主要就将这俩。

    😜ViewBinding

    开始实操前,我们先了解她的优缺点
    优点

    • 避免了控件空指针的错误,findViewById中就可能造成空指针或类型转换错误。
    • 代码可读性高
    • 相比DataBinding,更加轻量级

    缺点

    • 会生成冗余的很多binding文件,虽然你不需要管理。
    • 灵活性不高,如果需要动态切换布局,则多个布局必须根节点必须一致。
    • 在 Activity、Fragment、Dialog、Adapter 中 ViewBinding 和 DataBinding 初始化方式有些不同
    • 需要单独处理 include 带 merge 标签的布局,和不带 merge 标签的布局等等

    配置说明

    Android Studio 3.6 版本开始,就内置在 Gradle 插件中了,不需要添加任何额外的库来使用它们,但是在 Android Studio 3.6 和 Android Studio 4.0 中使用方式不一样。
    Android Studio3.6以上

    android {
        viewBinding {
            enabled = true
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    Android Studio4.0以上

    android {
        buildFeatures {
            viewBinding = true
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    如果不想要某个布局文件生成binding,需要添加tools:viewBindingIgnore="true"属性

    <RelativeLayout
        ...
        tools:viewBindingIgnore="true" >
      	...
    </RelativeLayout>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    开启Bingding后,系统会利用驼峰命名法生成对应的绑定类,一Binding为后缀。
    例如:布局activity_main.xml,生成的为ActivityMainBinding类

    Activity中使用

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
         xmlns:tools="http://schemas.android.com/tools"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:orientation="vertical"
         tools:context=".MainActivity">
        <TextView
           android:id="@+id/testName"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content" />
    </LinearLayout>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    Java中

    public class MainActivity extends AppCompatActivity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());
            setContentView(binding.getRoot());
            binding.testName.setText("博客:芝麻粒儿");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    Kotlin中
    上面Java代码中ActivityMainBinding我们没有放到onCreate外面当作全局变量,而事实上,一个控件我们通常在多个地方使用,所以需要全局变量。Java没什么用特别的,在Kotlin中声明的变量都必须在声明的同时对其进行初始化。而这里我们显然无法在声明全局binding变量的同时对它进行初始化,所以这里又使用了lateinit关键字对binding变量进行了延迟初始化。

    class MainActivity : AppCompatActivity() {
        private lateinit var binding: ActivityMainBinding
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            binding = ActivityMainBinding.inflate(layoutInflater)
            setContentView(binding.root)
            binding.testName.text = "博客:芝麻粒儿"
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    Fragment中使用

    需要注意的是为了有效保证binding变量的有效生命周期是在onCreateView()函数和onDestroyView()函数之间,除了在onCreateView创建,也要在onDestroyView及时置空。
    Java中

    public class MainFragment extends Fragment {
    
        private FragmentMainBinding binding;
    
        @Override
        public View onCreateView(@NotNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            binding = FragmentMainBinding.inflate(inflater, container, false);
            return binding.getRoot();
        }
    
        @Override
        public void onDestroyView() {
            super.onDestroyView();
            binding = null;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    Kotlin中

    class MainFragment : Fragment() {
    
        private var _binding: FragmentMainBinding? = null
    
        private val binding get() = _binding!!
    
        override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
            _binding = FragmentMainBinding.inflate(inflater, container, false)
            return binding.root
        }
    
        override fun onDestroyView() {
            super.onDestroyView()
            _binding = null
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    Adapter中使用

    创建一个名为【adapter_item.xml】的布局文件
    Kotlin中

    /**
     * Created by akitaka on 2022-06-25.
     * @author akitaka
     * @filename MyAdapter
     * @describe
     * @email 960576866@qq.com
     */
    class MyAdapter (val params: List<MyModel>) : RecyclerView.Adapter<MyAdapter.ViewHolder>() {
    
        inner class ViewHolder(binding: AdapterItemBinding) : RecyclerView.ViewHolder(binding.root) {
            val tvName: TextView = binding.fruitName
        }
    
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
            val binding = AdapterItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
            return ViewHolder(binding)
        }
    
        override fun onBindViewHolder(holder: ViewHolder, position: Int) {
            val paramsTemp = params[position]
            holder.tvName.text = paramsTemp.name
        }
    
        override fun getItemCount() = MyModel.size
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    Java中

    /**
     * Created by akitaka on 2022-06-25.
     *
     * @author akitaka
     * @filename MyAdapter
     * @describe
     * @email 960576866@qq.com
     */
    public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
        private Context mContext;
        private ArrayList<String> mData;
        private final LayoutInflater inflater;
    
        public MyAdapter(Context context, ArrayList<String> data) {
            mContext = context;
            mData = data;
            inflater = LayoutInflater.from(context);
        }
    
        @NonNull
        @Override
        public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            AdapterItemBinding itemBinding = AdapterItemBinding.inflate(inflater, parent, false);
            return new ViewHolder(itemBinding);
        }
    
        @Override
        public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
            holder.textView.setText(mData.get(position));
        }
    
        @Override
        public int getItemCount() {
            return mData.size();
        }
    
        static class ViewHolder extends RecyclerView.ViewHolder {
            TextView textView;
    
            public ViewHolder(@NonNull AdapterItemBinding itemBinding) {
                super(itemBinding.getRoot());
                textView = itemBinding.textView;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    自定义View中使用

    自定义View的布局文件叫【layout_my_view.xml】

    private fun initView() {
        //加载布局
        val binding = LayoutMyViewBinding.inflate(LayoutInflater.from(context), this, true)
        //binding.布局中的控件名即可
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    就这么简单。
    在自定义View这有个需要特别提心的,比如横竖屏奇切换布局的时候
    竖屏布局

    <?xml version="1.0" encoding="utf-8"?>
    <cn.zhima.OneView
    xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/oneView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    横屏布局

    <?xml version="1.0" encoding="utf-8"?>
    <cn.zhima.TwoView
    xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/twoView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    我们知道binding会根据文件名利用驼峰命名来实现bind类,上面的横竖屏布局文件文件名相同,再切换引用的时候就会出现根元素的 id不一致的报错信息。

    Configurations for activity_main.xml must agree on the root element’s ID.

    所以我们需要为这两个布局额外嵌套一层相同的ViewGroup,比如RelativeLayout或LinearLayout。之后就可以正常运行了。

    Dialog中使用

    Dialog dialog = new Dialog(getContext());
    DialogTipBinding dialogTipBinding = DialogTipBinding.inflate(LayoutInflater.from(getContext()));
    dialog.setContentView(dialogTipBinding.getRoot());
    dialogTipBinding.tvTip.setText("弹框提示");
    dialog.show();
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    include和merge标签的使用

    布局中可以嵌套include标签,但是如果查找id的话,如果引入的这个布局根不是merge的话则必须include标签需要加上个id。
    布局【include_title.xml

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <TextView
            android:id="@+id/tvTesxt"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="芝麻粒儿" />
    </RelativeLayout>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    Activity布局:【activity_include.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".IncludeActivity">
        <include
            android:id="@+id/title"
            layout="@layout/include_title" />
    </LinearLayout>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    public class IncludeActivity extends AppCompatActivity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            ActivityIncludeBinding binding = ActivityIncludeBinding.inflate(getLayoutInflater());
            setContentView(binding.getRoot());
    
            binding.title.tvTesxt.setText("空名先生");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    merge和include最大的区别在于,使用merge标签引入的布局在某些情况下可以减少一层布局的嵌套,而更少的布局嵌套通常代表着更高的运行效率。
    titlebar.xml

    <merge xmlns:android="http://schemas.android.com/apk/res/android">
        <TextView
            android:id="@+id/title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:text="Title"
            android:textSize="20sp" />
    </merge>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    activity_main.xml

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <include
            layout="@layout/titlebar" />
    </LinearLayout>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    Kotlin的MainActivity

    class MainActivity : AppCompatActivity() {
        private lateinit var binding: ActivityMainBinding
        private lateinit var titlebarBinding: TitlebarBinding
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            binding = ActivityMainBinding.inflate(layoutInflater)
            titlebarBinding = TitlebarBinding.bind(binding.root)
            setContentView(binding.root)
            titlebarBinding.title.text = "芝麻粒儿"
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在onCreate()函数中可以看到,这里我们又定义了一个titlebarBinding变量。不用说大佬们也看出来了,这个TitlebarBinding就是Android Studio根据我们的titlebar.xml布局文件自动生成的Binding类。

    👉其他

    📢作者:小空和小芝中的小空
    📢转载说明-务必注明来源:https://zhima.blog.csdn.net/
    📢这位道友请留步☁️,我观你气度不凡,谈吐间隐隐有王者霸气💚,日后定有一番大作为📝!!!旁边有点赞👍收藏🌟今日传你,点了吧,未来你成功☀️,我分文不取,若不成功⚡️,也好回来找我。

    温馨提示点击下方卡片获取更多意想不到的资源。
    空名先生

  • 相关阅读:
    White Paper 4Understanding Electrical Overstress - EOS-3
    tb6612电机驱动与JGB37-520减速直流电机
    设计模式—命令模式
    【leetcode】【2022/9/11】857. 雇佣 K 名工人的最低成本
    CAD二次开发LineSegment2d
    【解决】tsc : 无法加载文件XXX, 因为在此系统上禁止运行脚本。
    开源无人机如何实现空对地框选撞击?
    【Python】使用 Matplotlib 和 Numpy 的 知识绘制一个简单的 二次函数图像 || 同一坐标系下绘制多个函数图像
    webgoat-(A1)SQL Injection
    OpenCV项目开发实战--forEach的并行像素访问与其它方法的性能比较
  • 原文地址:https://blog.csdn.net/qq_27489007/article/details/125431242