MVVM + Databinding was used as the main architecture for one of the company’s reconstruction projects. This architecture is more front-end style, lighter and smarter than previous MVP, and also conforms to the idea of FLUTTER. The key is to bind data to THE UI, and the UI changes are driven by data. Much less get() set(), wySIWYG is what you get

Start databindding on Gradle for Android. Databinding is Google’s official library, so this line automatically integrates the relevant code:

    dataBinding {
        enabled true
    }
Copy the code

Binding class generation and use

There are several different ways to initialize the injection binding in your project’s activity, depending on your project’s needs

  • 1. Modify the BaseActivity class, suitable for the new project, use DataBindingUtil. The setContentView

The ActivityDemoBinding class is automatically generated by databinding from an XML file, just like ButterKnife

activity_demo.xml :

  • 2. Bind XML directly in the Activity implementation class, which is the same Activity as above. This time, the original project structure is not changed, and bind XML in demoActivity, which is suitable for modification on the basis of the original project

Actually DataBindingUtil. Finally the setContentView method is also called the bind () method to bind the layout, but first in the previous step calls the setContentView activity

DataBindingUtil.setContentView :

Build the ViewModel

The corresponding ViewModel object is then generated in the specific activity

@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mBinding = DataBindingUtil.bind(getContainerView());
    mBinding.setModel(new DemoViewModel());
}
Copy the code

Declare some data in the viewModel:

public class DemoViewModel {
    public ObservableField<String> demoString = new ObservableField<>("");
    public ObservableBoolean demoBoolean = new ObservableBoolean();
    public ObservableInt demoNum = new ObservableInt();

    public void onButtonClick(View v){
        //TODO    
    }
}
Copy the code

ObservableInt is a databinding class that encapsulates some basic data types. ObservableInt can also be customized to encapsulate its own classes. It mainly implements the listener callback interface, uses the observer mode, and notifies the bound View to refresh the UI when data changes

Android typically obtains views using a butterKnife or, more primitive, findviewById(). This method is redundant. For example, with a handy butterKnife, you must declare the View object, the View type, and the View ID on Acelasticity. Using databinding, you can get the mapping class of the XML layout directly. Using a Binding object, you can use it directly in an Activity without declaring it. Even simple operations such as setting text, images, and clicking events do not require Activity intervention. You bind specific data or methods directly in XML.

Use method, in XML:

<? The XML version = "1.0" encoding = "utf-8"? > <layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="model" type="com.XXX.DemoViewModel" /> <import type="android.view.View"/> </data> <FrameLayout android:id="@+id/container_demo"  android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/tv_demo" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{model.demoString}" android:visibility="@{model.demoBoolean? View.VISIBLE : View.GONE}" /> <Button android:id="@+id/bt_demo" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="@{model::onButtonClick}" /> </FrameLayout> </layout>Copy the code

Add the Layout tag to the root layout, add the Data tag, and type is the viewModel created by the baseActivity above

The most basic textView shows:

<TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="#FF4A4A4A"
        android:textSize="18sp"
        android:drawablePadding="10dp"
        android:text="@{model.demoString,default= 123}"
/>
Copy the code

editext

                <EditText
                    android:layout_width="match_parent"
                    android:layout_weight="1"
                    ...
                    android:text="@={viewModel.phoneNo}"
                    binding:textChanged="@{viewModel.textChangeCommand}"
                />
Copy the code

The difference between “@{}” and “@={}” is that the “@{}” is unidirectional and notifies the UI of changes in data, while the “@={}” is bidirectional and notifies the UI of changes in data, typically the Edittext input box

If you use the primitive method, such as findViewById or Butteknife, to declare a View object at the start of your activity, you need to use the VIEW ID and type, and click events need to be added one by one, otherwise the click won’t work. GetText ().toString() is used in the input box to get user input data. It is very tedious. Any error in the input box will cause bugs to appear and affect all subsequent operations. You don’t need to declare a View object in your activity, you don’t need to worry about the type of View, you don’t need to worry about the ID of the View, you just need to bind data to the View in XML, and thanks to the bindingAdapter, you can also do some intermediate operations, such as preventing quick clicks. Load network pictures, etc., simplify many steps into one step

3. BindingAdapter

Like in normal development, we don’t just use the basic properties of the View, so in a custom View, ImageView loads the web image or something like that, we still need to get the View object and do something in Java or Kotlin code, is there a way to do that in databinding, The BindingAdapter is a View adapter that uses the AOP pattern and generates code at compile time to modify the Binding class to use a DADpter View

For example, if an Imageview is used to set the avatar, the common method is to grab the Imageview object and use Glide or some other image request framework to load the Url. Dababinding looks like this:

<ImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    binding:url="@{model.imgUrl}"
    binding:placeholderRes="@drawable/bg1"
/>
Copy the code

Binding :url Specifies the image address

Binding :placeholderRes

So how do we do that? We’re going to introduce a bindingAdapter, right

@BindingAdapter(value = {"url", "placeholderRes"}, requireAll = false) public static void setImageUri(ImageView imageView, String url, int placeholderRes) { if (! Glide. With (imageView.getContext()).load(url).apply(new) RequestOptions().placeholder(placeholderRes)) .into(imageView); }}Copy the code

There is @bindingAdapter annotation, value is URL and placeholder, requireAll requires all parameters, ImageView specifies the view type, and Glide requests the web image. Placeholder image, configure a place of code that can be used in all imageViews.

There are also many common methods, such as rounded corners of the control, display different images according to different types, press to change the background, list item animation, list delimiter… And so on can be used Adapter to complete, greatly reduce the amount of code in the Activity, and can unify the format problem, change a place, everywhere

Note that the BindingAdapter will take effect if it is written anywhere in the project. Since the AOP schema is used, the code will be checked at compile time and will not be repeated. When the BindingAdapter conflicts with the original attributes in the XML, the Settings in bindingDadpter will override the original attributes

Use notes

Advantages:

The amount of code is greatly reduced

Bindingadapter unified