Why do

I didn’t take it seriously at that time. I had a problem writing a Demo two days ago and created a new project. The kotlin-Android-extensions extensions in the new project are not integrated with the kotlin-Android-extensions extensions.

One of the reasons why I started using Koltin was that I didn’t have to write findViewById anymore, but now I’m just throwing it away! What a pit.

Coincidentally, just two days after discovering this problem, “Guo Lin” posted an article:

Is the Kotlin-Android-Extensions plugin deprecated? Lifted me up

There is a detailed description of how to use ViewBinding, so I want to use it in my “Play Android”, but there are some minor problems during the use, just listen to me to 🐶

Then put down the Github address of the project: github.com/zhujiang521…

Note: this article does not write the basic usage of ViewBinding, guo Shen wrote the basic usage is much better than mine, you can follow the link above.

Began to make

To do this thoroughly, remove the Kotlin-Android-extensions dependency directly from the main module:

plugins {
    id 'com.android.library'
    id 'kotlin-android'
    id 'kotlin-android-extensions'
}
Copy the code

Delete the bottom line of the code above and add the ViewBinding dependency:

Android {... buildFeatures { viewBindingtrue}}Copy the code

That’s all pretty simple, but what’s the problem? In fact, this problem can be regarded as a pit dug by myself before. In order to adapt the layout of horizontal and vertical screens, I customized two different controls in the front page of the project. In the layout, the horizontal and vertical screens respectively put their respective layouts.


      
<com.zj.play.main.HomeBottomLandTabWidget xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/homeLandView"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
Copy the code

      
<com.zj.play.main.HomeBottomTabWidget xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/homeView"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
Copy the code

Very simple, in use directly judge the current screen state to select the loading layout.

override fun initView(a) {
    isPort = resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT
    when (isPort) {
        true -> homeView.init(supportFragmentManager, viewModel)
        false -> homeLandView.init(supportFragmentManager, viewModel)
    }
}
Copy the code

It works fine when ViewBinding is not used, but! When a ViewBinding is used, an error is reported:

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

@+id/homeLandView:
 - layout-land

@+id/homeView:
 - layout
Copy the code

The error message is obvious because the id of the root element of both layouts must be the same! In this case, put a layer of layout outside the layout:


      
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.zj.play.main.HomeBottomTabWidget
        android:id="@+id/homeView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</FrameLayout>
Copy the code

      
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.zj.play.main.HomeBottomLandTabWidget
        android:id="@+id/homeLandView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</FrameLayout>
Copy the code

After the change, it runs successfully.

The second problem is the use of ViewBinding in custom controls. Guo God wrote in the article about the use of ViewBinding in activities, fragments and Adapter, but did not write how to use it in custom View, actually write it or not, because it is very simple.

private fun initView(a) {
    // Load the layout
    val binding = LayoutToTopBinding.inflate(LayoutInflater.from(context), this.true)}Copy the code

The first parameter is an inflater, which is a context that can be created directly. The second parameter is a ViewGroup, which is also easy to get in a custom View. The third parameter is whether to bind to the root element. If you want to write it as false, you can also add a View to the layout using addView.

If you need to use it globally, you can define it as a global variable directly, and empty it when not in use.

The third question is what Guo Shen said in the article about the way ViewBinding is written in the Adapter. There is a different opinion in the comment:

Wanted to think and do, use ViewBinding is originally in order not to write the findViewById, the result like the one above to write some words instead of trouble, it is better to direct the findViewById fast, also don’t have to introduce ViewBinding.

Let’s create an Adapter base class:

abstract class BaseRecyclerAdapter<V : ViewBinding> :
    RecyclerView.Adapter<BaseRecyclerAdapter.BaseRecyclerHolder<V>>() {

    abstract override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseRecyclerHolder<V>

    override fun onBindViewHolder(holder: BaseRecyclerHolder<V>, position: Int) {
        onBaseBindViewHolder(position, holder.binding)
    }

    abstract fun onBaseBindViewHolder(position: Int, binding: V)

    open class BaseRecyclerHolder<V : ViewBinding>(val binding: V) :
        RecyclerView.ViewHolder(binding.root)

}
Copy the code

The base class code is very simple and without going into too much detail, let’s look at the modified code:

class FruitAdapter(val fruitList: List<Fruit>) : BaseRecyclerAdapter<FruitItemBinding>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseRecyclerHolder<FruitItemBinding> {
        val binding = FruitItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        return BaseRecyclerHolder(binding)
    }

    override fun onBaseBindViewHolder(holder: BaseRecyclerHolder<FruitItemBinding>, position: Int) {
        val fruit = fruitList[position]
        binding.apply{
            fruitImage.setImageResource(fruit.imageId)
            fruitName.text = fruit.name
        }
    }

    override fun getItemCount(a) = fruitList.size
}
Copy the code

I didn’t write much less code, but I felt a little cleaner.

To the end

The rest was enough as written by Guo God, and the modification was quite simple. After a few hours of “Playing Android”, I changed the whole project into ViewBinding.

Let’s hope Google doesn’t jump into ViewBinding too soon, because Compose is still waiting on ViewBinding…

Before the end, wish everyone a happy New Year, after all, not out of the first month is the year! But now a little late, old age, I wish you all: happy old age!