Use of Android View Binding

Please indicate the source of Reprint: http://blog.csdn.net/zhaoyanjun6/article/details/120751996
This article comes from [Zhao Yanjun's blog]

What is View Binding

View Binding is a new feature introduced by Android Studio 3.6 to replace findviewbyid (internal implementation or use findViewById).. After starting View Binding, the system will generate a binding class for each xml file in the module. The instance of the binding class contains direct references to all views with ID S in the corresponding layout.

Advantages of View Binding

  • Null security: since view binding creates a direct reference to the view, there is no risk of null pointer exception due to invalid view ID. In addition, if the view appears only in some configurations of the layout, the fields in the binding class that contain its references are marked with @ Nullable.
  • Type safe: fields in each binding class have types that match the views they reference in the XML file. This means that there is no risk of class conversion exceptions.

How to enable View Binding

android {
   
    buildFeatures {
        viewBinding true
    }
}

If you want to ignore a layout file when generating binding classes, add the tools:viewBindingIgnore="true" attribute to the root view of the corresponding layout file:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:viewBindingIgnore="true"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

How to use View Binding

After using the view Binding function, the system will generate a Binding class for each XML layout file contained in the module. The class name of this class is named after the XML layout file name, with the word capitalized and Binding removed. Such as activity_ Class ActivityMainBinding.xml generated by main.xml

Activity binding view

  • Step 1: call the static inflate() method contained in the generated binding class. This creates an instance of the binding class for use by the Activity.

  • Step 2: get the reference to the root view by calling the getRoot() method or using the Kotlin attribute syntax.

  • Step 3: pass the root view to setContentView() to make it the active view on the screen.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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=".MainActivity">

    <TextView
        android:id="@+id/tv"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="#77f"
        android:text="Zhao Yanjun"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

use:

class MainActivity : AppCompatActivity() {

    lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //critical code
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        //Get control
        binding.tv.setOnClickListener {
            Toast.makeText(this, "hahha", Toast.LENGTH_SHORT).show()
        }

    }
}

Fragment binding view

How to use view binding in a Fragment? Please perform the following steps in the onCreateView() method of the Fragment (Note: the Fragment exists longer than its view. Be sure to clear all references to bound class instances in the onDestroyView() method of the Fragment.)

  • Call the static inflate() method contained in the generated binding class. This creates an instance of the binding class for Fragment use.

  • Get a reference to the root view by calling the getRoot() method or using the Kotlin attribute syntax.

  • Returns the root view from the onCreateView() method, making it the active view on the screen

//fragment_my.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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=".MainActivity">

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="40dp"
        android:text="This is Fragment Button"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView" />


    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="40dp"
        android:gravity="center"
        android:text="This is FragmentTextView"
        app:layout_constraintBottom_toTopOf="@+id/button"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
public class MyFragment extends Fragment {
  private FragmentMyBinding binding;

  public MyFragment() {

  }

  @Override
  public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);

  }

  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container,
                           Bundle savedInstanceState) {
      binding = FragmentMyBinding.inflate(inflater, container, false);
      return binding.getRoot();
  }

  @Override
  public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
      super.onViewCreated(view, savedInstanceState);
      binding.textView.setText("This is Fragment");
      binding.button.setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View view) {
              Log.d("Fragment", "Click the button");
          }
      });
  }

  @Override
  public void onDestroy() {
      super.onDestroy();
      binding = null;
  }

Using View Binding in onViewCreated

//Using View Binding in onViewCreated
public class MyFragment extends Fragment {
  private FragmentMyBinding binding;

  public MyFragment() {

  }

  @Override
  public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);

  }

  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container,
                           Bundle savedInstanceState) {
      return inflater.inflate(R.layout.fragment_my,container,false);
  }

  @Override
  public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
      super.onViewCreated(view, savedInstanceState);
      FragmentMyBinding binding = FragmentMyBinding.bind(view);
      this.binding = binding;

      binding.textView.setText("This is Fragment");
      binding.button.setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View view) {
              Log.d("Fragment", "Click the button");
          }
      });
  }

  @Override
  public void onDestroy() {
      super.onDestroy();
      binding = null;
  }

Base class written using View Binding

1. Through reflection (this method is not recommended and may cause confusion)

Activity base class design

//Java
public class BaseActivity<T extends ViewBinding> extends AppCompatActivity {
  protected T viewBinding;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      ParameterizedType type = (ParameterizedType) getClass().getGenericSuperclass();
      Class cls = (Class) type.getActualTypeArguments()[0];
      try {
          Method inflate = cls.getDeclaredMethod("inflate", LayoutInflater.class);
          viewBinding = (T) inflate.invoke(null, getLayoutInflater());
          setContentView(viewBinding.getRoot());
      } catch (NoSuchMethodException | IllegalAccessException| InvocationTargetException e) {
          e.printStackTrace();
      }
  }
}

//use

public class MainActivity extends BaseActivity<ActivityMainBinding> {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        viewBinding.button.setText("This is MainActivity ViewBinding");
        viewBinding.button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Log.d("MainView","Click the button");
            }
        });
    }
}
//Kotlin
open class BaseActivity<T : ViewBinding> : AppCompatActivity() {
    protected lateinit var binding: T
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val type = javaClass.genericSuperclass as ParameterizedType
        val aClass = type.actualTypeArguments[0] as Class<*>
        val method = aClass.getDeclaredMethod("inflate", LayoutInflater::class.java)
        binding = method.invoke(null, layoutInflater) as T
        setContentView(binding.root)
    }
}

class MainActivity : BaseActivity<ActivityMainBinding>() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding.textView.text = "This is MainActivity"
    }
}

Fragment base class design

//Java
public class BaseFragment<T extends ViewBinding> extends Fragment {
    protected T viewBinding;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        ParameterizedType type = (ParameterizedType) getClass().getGenericSuperclass();
        Class cls = (Class) type.getActualTypeArguments()[0];
        try {
            Method inflate = cls.getDeclaredMethod("inflate", LayoutInflater.class, ViewGroup.class, boolean.class);
            viewBinding = (T) inflate.invoke(null, inflater, container, false);
        }  catch (NoSuchMethodException | IllegalAccessException| InvocationTargetException e) {
            e.printStackTrace();
        }
        return viewBinding.getRoot();
    }
}

//use
public class MainFragment extends BaseFragment<FragmentMainBinding>{
    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        viewBinding.button.setText("This is MainFragment ViewBinding");
        viewBinding.button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Log.d("MainView","Click the button");
            }
        });
    }
}

//Kotlin
open class BaseFragment<T:ViewBinding>:Fragment(){
    lateinit var binding: T
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        val type = javaClass.genericSuperclass as ParameterizedType
        val aClass = type.actualTypeArguments[0] as Class<*>
        val method = aClass.getDeclaredMethod("inflate", LayoutInflater::class.java,ViewGroup::class.java,Boolean::class.java)
        binding = method.invoke(null,layoutInflater,container,false) as T
        return binding.root
    }
}

class FirstFragment : BaseFragment<FragmentFirstBinding>() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding.textView.text = "This is FirstFragment"
    }
}

2. Not through reflection

Activity base class design

abstract class BaseActivity<T : ViewBinding> : AppCompatActivity() {
    private lateinit var _binding: T
    protected val binding get() = _binding;

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        _binding = getViewBinding()
        setContentView(_binding.root)
    }

    protected abstract fun getViewBinding(): T
}

class MainActivity : BaseActivity<ActivityMainBinding>() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding.textView.text = "This is MainActivity"
    }

    override fun getViewBinding() = ActivityMainBinding.inflate(layoutInflater)
}

Fragment base class design

abstract class BaseFragment<T : ViewBinding> : Fragment() {
    private lateinit var _binding: T
    protected val binding get() = _binding;
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        _binding = getViewBinding(inflater, container)
        return _binding.root
    }
    
    protected abstract fun getViewBinding(inflater: LayoutInflater, container: ViewGroup?): T
}

class FirstFragment : BaseFragment<FragmentFirstBinding>() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding.textView.text = "This is FirstFragment"
    }

    override fun getViewBinding(
        inflater: LayoutInflater,
        container: ViewGroup?
    ) = FragmentFirstBinding.inflate(inflater, container, false)
}

Keywords: Android

Added by ElectricRain on Wed, 13 Oct 2021 15:14:15 +0300