Introduction: When dealing with Android, finViewById is probably the most troublesome way to write. A class is bulabula with a lot of duplicate code. Fortunately, later JakeWharton open source ButterKnife optimized the writing method, but it still has to write a lot of duplicate code, which looks boring. Google’s ViewBinding and the kitlin Android Extions package in Kotlin are both optimized for this.
Difference from findViewById
- Null security: Because the view binding creates a direct reference to the view, there is no risk that Null pointer exceptions will be thrown because the view ID is invalid.
In addition, if the view appears only in certain configurations of the layout, the @Nullable flag is used for fields in the binding class that contain references to it.
- Type safety: Fields in each binding class have a type that matches the view they reference in the XML file. This means that there is no risk of class conversion exceptions.
- These differences mean that incompatibilities between layout and code will cause builds to fail at compile time rather than run time. It’s easier to write robust code.
Comparison with databinding (databinding)
Both view binding and data binding generate binding classes that can be used to directly reference the view. However, view binding is designed to handle simpler use cases and has the following advantages over data binding:
- Faster compilation: View bindings do not need to handle annotations, so compilation times are shorter.
- Ease of use: View bindings do not require a specially marked XML layout file and are therefore faster to adopt in your application. When view binding is enabled in a module, it is automatically applied to all layouts of that module.
Conversely, view binding has the following limitations compared to data binding:
- View bindings do not support layout variables or layout expressions and therefore cannot be used to declare dynamic interface content directly in AN XML layout file.
- View binding does not support bidirectional data binding.
Databinding is a complex and difficult process to debug, and it is not considered unless it is for better bidirectional binding.
Which one does Kotlin Android Extensions and ViewBinding use?
Conditions of use
- The first one will definitely need to be developed using Kotlin, but there is a slight catch. The UI update must be done after the onCreateView method, which can also be done in this method, but the call is a little more complicated, and it’s usually done in the onViewCreate method to refresh the UI.
- ViewBinding supports Java and Kotlin.
How simple
- If developed in Kotlin, Android Extension doesn’t require any declaration and is very convenient to use.
- If you’re developing in Java, ViewBinding is the way to go.
Personally, ViewBinding was made for Java, and once Kotlin is used, Ktx feels more convenient and practical.
ButterKnife has been phased out, JK’s warehouse has been explained, again proving that Java developers need to embrace ViewBinding.
Attention: This tool is now deprecated. Please switch to view binding. Existing versions will continue to work, obviously, but only critical bug fixes for integration with AGP will be considered. Feature development and general bug fixes have Stopped. // This tool is deprecated, please switch to ViewBinding. The current version will continue to work, only serious bug fixes, the rest is up to scratch.
We’ll show you how to use ViewBinding in Java and Kotlin. Finally, we’ll show you how to encapsulate BaseActivity, BaseFragment, BaseAdapter, etc.
use
View binding makes it easier to write code that interacts with views. After view binding is enabled in a module, a binding class is generated for each XML layout file in that module. Instances of the binding class contain direct references to all views that have ids in the corresponding layout. Setting Notes – Note: View binding is available in Android Studio 3.6 Canary 11 and later. Remember to upgrade the junk software. – Currently, our projects are usually modular, and the view binding function can be enabled by module. If you want to enable a module, you can directly add the following code to the build.gradle file of that module.
android {
// Add to android block
viewBinding {
enabled = true}}Copy the code
- 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:
<LinearLayout
tools:viewBindingIgnore="true" >.</LinearLayout>
Copy the code
Usage When view binding is enabled for a module, a binding class is generated for each XML layout file contained in that module. Each binding class contains references to the root view as well as all views with ids. The system generates the name of the Binding class by converting the name of the XML file to camel case and adding the word “Binding” to the end. For example, suppose a layout file is named result_profile.xml:
<LinearLayout >
<TextView android:id="@+id/name" />
<ImageView android:cropToPadding="true" />
<Button android:id="@+id/button"
android:background="@drawable/rounded_button" />
</LinearLayout>
Copy the code
The name of the generated binding class is ResultProfileBinding. This class has two fields: a TextView named Name and a button named Button. The ImageView in this layout has no ID, so there is no reference to it in the binding class. Each binding class also contains a getRoot() method that provides a direct reference to the root view of the corresponding layout file. In this example, the getRoot() method in the ResultProfileBinding class returns the LinearLayout root view.
The following sections describe the use of generated binding classes in activities and Fragments.
To set up an instance of the binding class for your Activity to use, perform the following steps in the Activity’s onCreate() method: – Call the static inflate() method contained in the generated binding class. This action creates an instance of the binding class for the Activity to use. – Get a reference to the root view by calling the getRoot() method or using the Kotlin attribute syntax. – Pass the root view to setContentView() to make it the active view on the screen. Java
class ResultProfileActivity extends AppCompatActivity {
private ResultProfileBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); binding = ResultProfileBinding.inflate(getLayoutInflater()); View view = binding.getRoot(); setContentView(view); }}Copy the code
Kotlin
class ResultProfileActivity : AppCompatActivity() {
private lateinit var binding: ResultProfileBinding
override fun onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState)
binding = ResultProfileBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
}
}
Copy the code
You can now use an instance of the bound class to reference any view:
binding.getName().setText(viewModel.getName());
binding.button.setOnClickListener(new View.OnClickListener() {
viewModel.userClicked()
});
Copy the code
binding.name.text = viewModel.name
binding.button.setOnClickListener { viewModel.userClicked() }
Copy the code
To set up an instance of a binding class for your Fragment to use, perform the following steps in the Fragment’s onCreateView() method: – Call the static inflate() method contained in the generated binding class. This creates an instance of the bound class for use by the Fragment. – 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. Java
class ResultProfileFragment extends Fragment {
private ResultProfileBinding binding;
@Override
public View onCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
binding = ResultProfileBinding.inflate(inflater, container, false);
View view = binding.getRoot();
return view;
}
@Override
public void onDestroyView(a) {
super.onDestroyView();
binding = null; }}Copy the code
Kotlin
class ResultProfileFragment : Fragment() {
private var _binding: ResultProfileBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup? , savedInstanceState:Bundle?).: View? {
_binding = ResultProfileBinding.inflate(inflater, container, false)
val view = binding.root
return view
}
override fun onDestroyView(a) {
super.onDestroyView()
_binding = null}}Copy the code
You can now use an instance of the bound class to reference any view:
binding.getName().setText(viewModel.getName());
binding.button.setOnClickListener(new View.OnClickListener() {
viewModel.userClicked()
});
Copy the code
binding.name.text = viewModel.name
binding.button.setOnClickListener { viewModel.userClicked() }
Copy the code
The above brief introduction of the application, careful friend said, I have many activities and fragments in the application need to write this, it is not exploded.
Don’t worry, the package you need is below.
Let’s see if we can initialize binding directly in BaseXXX and then use it in the corresponding subinterface. Because kotlin is used in the project, I don’t want to convert it to Java, refer to kotlin’s implementation, Java is easy, if not, you can leave a message, later update. In view of the BaseActivity
abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
protected lateinit var binding: VB
override fun onCreate(savedInstanceState: Bundle?). {
super.onCreate(savedInstanceState)
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)
}
onCreated(savedInstanceState)
}
abstract fun onCreated(savedInstanceState: Bundle?).
}
Copy the code
You can use it directly in a specific Activity:
class MainActivity : FullActivity<ActivityMainBinding>() {
override fun onCreated(savedInstanceState: Bundle?). {
// XXX is the property name you defined in the activity_main layout
binding.xxx.text = "123"}}Copy the code
In view of the BaseFragment
abstract class BaseFragment<out VB : ViewBinding> : Fragment {
private var _binding: VB? = null
val binding: VB get() = _binding!!
constructor() : super(a)@ContentView
constructor(@LayoutRes contentLayoutId: Int) : super(contentLayoutId)
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup? , savedInstanceState:Bundle?).: View? {
// With reflection, the view is populated by calling the inflate method in the specified ViewBinding
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 onDestroyView(a) {
_binding = null
super.onDestroyView()
}
}
Copy the code
In view of the BaseAdapter
abstract class BaseRecyclerviewAdapter<VB : ViewBinding, M> :
RecyclerView.Adapter<BaseRecyclerviewAdapter.ViewHolder>() {
protected var data: List<M>? = null
protected lateinit var binding: VB
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
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.from(parent.context), parent, false) as VB
return ViewHolder(binding.root)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.setIsRecyclable(false)
bindData(binding, data!!!!! [position],position) }override fun getItemCount(a): Int {
return data? .size ? :0
}
fun setMoreData(newData: List<M>) {
data = newData
notifyDataSetChanged()
}
abstract fun bindData(binding: VB, item: M,position: Int)
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
}
Copy the code
In view of the BaseViewGroup
abstract class BaseViewGroup<out VB : ViewBinding>(
context: Context,
attr: AttributeSet? = null,
def: Int = 0
) : FrameLayout(context, attr, def) {
protected val binding: VB by lazy {
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
)
method.invoke(null, LayoutInflater.from(context), this.true) as VB
}
init {
binding.root
}
}
Copy the code
Good times are always so short. Bye, everybody. There is a need to please move the public number “programmer refers to north”, although I do not often write, but do not affect your attention ha ha.