If you like WeaponZhi’s article, you can keep track of what’s going on on our public account WeaponZhi



MVVM

MVVMLight

The third party library “MVVMLight” is actually an extension of the Databinding library and encapsulates all View events uniformly through ReplyCommand and ResponseCommand, which IS what I think MVVMLight is most useful for

MVVMLight official introduction blog MVVMLight source address

Let’s look at ReplyCommand in action. Let’s use the common PullToRefreshLayout drop-down refresh control as an example.

We know that if you want to customize the event of a control, you need to use the @BindingAdapter annotation, such as ImageView, which uses the URL property to download the image directly from the address and display it:

@BindingAdapter("bind:urlImage")  
public static void getInternetImage(ImageView iv, String userface) {  
    Picasso.with(iv.getContext()).load(userface).into(iv);  
}Copy the code
<ImageView  
    android:id="@+id/iv"  
    android:layout_width="wrap_content"  
    android:layout_height="wrap_content"  
    app:urlImage="@{user.urlImage}"/>Copy the code

This is usually a simple case, because it only operates on a property, but what if we want to customize an event, such as the onClick event, we might have to write the interface:

@BindingAdapter("setImageOnClick")
    public static void setImageOnClick(ImageView imageView, final ImageOnClickListener listener){
        if(listener ! =null) {
            imageView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) { listener.onClick(v); }}); }}interface ImageOnClickListener{
    void onClick(View v);
}Copy the code

To use this, you need to define an ImageOnClickListener member variable in the VM to write the specific onClick implementation method, This event is then bound in XML via app:setImageOnClick=”viewModel.listener”.

Of course, you can do this directly via Android :onClick, but this is just an example.

It doesn’t seem like a lot of trouble, but you might have to define a special interface for each of these events. Can we encapsulate that?

That’s what MVVMLight does with ReplyCommand and ResponseCommand. These two classes encapsulate callback methods that specify the number of request parameters and the number of return values. It is convenient to specify the types of request parameters and return values in the generic.

For example, PullToRefreshLayout is a refresh list control, and our listener for pull-down refresh and pull-up load by using ReplyCommand is written like this:

@BindView(R.id.refresh_listview) PullToRefreshLayout pullToRefreshLayout; .@BindingAdapter (value = {"onRefreshCommand"."onLoadCommand"}, requireAll = false)
public static void onRefreshLoadCommand(
    final PullToRefreshLayout pullToRefreshLayout, final ReplyCommand onRefreshCommand, final ReplyCommand onLoadCommand) {

    pullToRefreshLayout.setOnRefreshListener(new PullToRefreshLayout.OnRefreshListener() {
        @Override
        public void onRefresh(PullToRefreshLayout pullToRefreshLayout) {
            if(onRefreshCommand ! =null) { onRefreshCommand.execute(); }}@Override
        public void onLoadMore(PullToRefreshLayout pullToRefreshLayout) {
            if(onLoadCommand ! =null) { onLoadCommand.execute(); }}}); }Copy the code

We use a unified ReplyCommand to handle the various events of the control. Here we use the simplest case with no arguments and no return values, which we write in ViewModel and XML in much the same way as the previous interface:

public final ReplyCommand onRefreshCommand = new ReplyCommand(() -> getPostData(true));

public final ReplyCommand onLoadCommand = new ReplyCommand(()->getPostData(true));Copy the code
<com.weapon.joker.lib.view.pullrefreshload.PullToRefreshLayout
    android:id="@+id/pull_refresh_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:onRefreshCommand="@{viewModel.onRefreshCommand}"
    app:onLoadCommand="@{viewModel.onLoadCommand}"/>Copy the code

In this way, our interfaces for all events are unified. ResponseCommand differs from ReplyCommand in that ResponseCommand defines a parameter that has a return value while ReplyCommand does not. For details on how to use ResponseCommand, see the link above. The author himself gives the most detail.

binding-collection-adapter

Binding-collection-adapter encapsulates all controls that require a Adapter, such as common ones: ListView, RecyclerView, ViewPager, etc., so by using this library, we don’t need to write adapter, we can bind some properties in XML by databinding, And processing these properties in ViewModel can complete the processing of these controls, logic is clear, the code is simple.

Making the address

Here’s an example of RecyclerView. We now define a RecyclerView control in XML.

<android.support.v7.widget.RecyclerView
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  app:layoutManager="@{LayoutManagers.linear()}"
  app:items="@{viewModel.items}"
  app:itemBinding="@{viewModel.itemBinding}"/>Copy the code

We see three special attributes: layoutManager, Items, and itemBinding. The layoutManager is familiar to everyone. The parameters are imported at the beginning of the import, passing in the associated class name.

<data>
    <variable
        name="viewModel"
        type="com.weaponzhi.test.ViewModel"/>
    <import type="com.weaponzhi.test.LayoutManagers"/>
</data>Copy the code

An itemBinding is used to handle the binding of each layout to the ViewModel of the corresponding item. The itemBinding is defined in the ViewModel of the above code

public final OnItemBindClass<Object> itemBinding =
    new OnItemBindClass<>
    .map(NoDataViewModel.class,BR.noData,R.layout.listitem_no_data)
    .map(ItemViewModel.class,BR.itemVM,R.layout.listitem_page);Copy the code

The map method takes three parameters. The first parameter is the ViewModel for the layout, the third parameter is the XML file for the layout, and the second parameter is the BR file ID of the ViewModel introduced in the XML. Now we have bound the multiple layout logic for the list control. A layout for empty data, a layout for normal return data.

How do we refresh our data using the items property above, which in our case is defined like this:

public final ObservableList<Object> viewModels = new ObservableArrayList<>();Copy the code

When the network request returns, we construct the ItemViewModel in the data callback by processing the data type. Finally, we just add the constructed objects one by one to the ObservableList data structure. The “ItemViewModel” is used to determine the data type of the ItemViewModel for which the updated data is to be added. Listitem_no_data for nodataViewModel. class, r.layout.listitem_page for itemViewModel. class. Of course, other data updates and deletes are flushed synchronously due to bidirectional binding.

We are completely free from the tedious Adapter!

Databinding support


,
,

,

and other tags are often used in Databind code. Just press ⌥+⏎(Alt+Enter for Windows) in the appropriate place and steal a few Gif images from the official website to give you a taste of it.


Wrap with <layout></layout>

Add <data> tag

Wrap with @ {}

Wrap with @ = {}

Switch @ {} and @ = {}

Add <import>

Add <variable>

MVVM automatic code generation

MVVM and MVP architectures don’t necessarily require less code. Each interface may have to create many classes in a fixed pattern, so why not use an automatic code generation tool to create these classes through simple configuration. Java can do all this. There are many ways to automate code generation in Java on the web, but everyone implements MVP and MVVM architecture differently, so the automated code will be different too. Let me show you the process I use here.

The main idea of the MVVM code generation tool I use is relatively simple, using an XML file to configure some properties, such as a name, set the path of the file output, and then generate template code in Java using string concatenation and file stream reading.


I am currently maintaining a project that uses MVVMLight and Binding-collection-Adapter for your reference. Making a link


I look forward to your attention on my public account: WeaponZhi