This is the 26th day of my participation in the August Text Challenge.More challenges in August

Android DataBinding Literacy Part 2

The previous article briefly explained the basic operations of data binding, but the previous article was one-way binding. This article explains how to do two-way binding and how to use more complex business scenarios.

I. Bidirectional binding

What is called bidirectional binding?

Data changes, synchronized updates to the view; The view changes and also synchronously changes the data.

Data change synchronous view is easy to understand, we get the server data will be encapsulated into objects, objects mapped to the view directly display can be; The view changes and synchronizes the data. Our data is already present in the view. Why synchronize the data again

In MVVM thinking, it goes like this.

graph LR
A[model] --> B[viewModel]
B --> A

B --> C[view]
C --> B

So how do you implement bidirectional binding in your code?

CheckBox One-way binding data

<CheckBox
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:checked="@{person.isSelect}"/>
Copy the code

CheckBox Binds data in both directions

<CheckBox
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:checked="@={person.isSelect}"/>
Copy the code

Just from @{person.isselect} -> @={person.isselect}.

EditText bidirectional bindings are the same. When the EditText changes, the object properties change as well.

eg

 <EditText
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@={person.name}" />
Copy the code

Second, advanced usage

2.1 Loading Images

Layout file

<ImageView    android:layout_width="wrap_content"    android:layout_height="wrap_content"    app:url="@{person.photo}" />
Copy the code

Bean file

@BindingAdapter("url")
    public static void setURL(ImageView imageView, String photo) {
        Glide.with(imageView.getContext()).load(photo).into(imageView);
    }
Copy the code

Note app in the layout file: The URL is a custom attribute that needs to be imported into XMLNS and must correspond to the url of the @BindingAdapter(” URL “) in the Bean file in order to be identified.

I’ve seen some people write it this way @BindingAdapter(“bind: URL “), you can write it that way.

The setURL in the Bean file can be named at will because it is identified by the annotations above.

2.2 ListView Loads the list

1. Prepare the interface

As usual, write a ListView in the layout

<? The XML version = "1.0" encoding = "utf-8"? > <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <ListView android:id="@+id/list_view" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout> </LinearLayout>Copy the code

2. Prepare the ViewModel class

Different from ordinary encapsulated objects, this object class inherits from BaseObservable to bind data and add some methods, such as displaying images and setting click events.

public class Person extends BaseObservable { private int id; private String name; private String photo; public Person() { } public Person(int id, String name, String photo) { this.id = id; this.name = name; this.photo = photo; } @Bindable public int getId() { return id; } public void setId(int id) { this.id = id; notifyPropertyChanged(BR.id); } @Bindable public String getName() { return name; } public void setName(String name) { this.name = name; notifyPropertyChanged(BR.name); } @Bindable public String getPhoto() { return photo; } public void setPhoto(String photo) { this.photo = photo; notifyPropertyChanged(BR.photo); } @BindingAdapter("url") public static void setURL(ImageView imageView, String photo) { Glide.with(imageView.getContext()).load(photo).into(imageView); } public void itemClick(View view) { Log.d("Person", "item click"); }}Copy the code

3. Prepare the layout of items

The @{} link to the data in the layout of the item, enabling them to be bound together. If you want to execute the click event, you add android:onClick=”@{person.itemclick}”

<? The XML version = "1.0" encoding = "utf-8"? > <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <data> <variable name="person" type="com.demo.mvvmdemo.Person" /> </data> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" android:onClick="@{person.itemClick}"> <ImageView android:layout_width="100dp" android:layout_height="100dp" app:url="@{person.photo}" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{String.valueOf(person.id)}" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{person.name}" /> </LinearLayout> </LinearLayout> </layout>Copy the code

4. Prepare adapters

Here I imitate the general adapter written by others, ordinary effects can be satisfied. Let’s see how the adapter is implemented.

public class CommonAdapter<T> extends BaseAdapter { private final String TAG = "CommonAdapter"; LayoutInflater mLayoutInflater; int mLayoutId; int mVariableId; List<T> mList; public CommonAdapter(LayoutInflater layoutInflater, int layoutId, int variableId, List<T> list) { this.mLayoutInflater = layoutInflater; this.mLayoutId = layoutId; this.mVariableId = variableId; this.mList = list; } @Override public int getCount() { return mList.size(); } @Override public Object getItem(int position) { return mList.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewDataBinding binding; {binding = databindingutil.inflate (mLayoutInflater, mLayoutId, parent, false); convertView = binding.getRoot(); convertView.setTag(binding); } else { binding = (ViewDataBinding) convertView.getTag(); } binding.setVariable(mVariableId, mList.get(position)); return binding.getRoot(); }}Copy the code

5. Bind the ListView to the view

After emulating some data, the ListView binds to the adapter.

public class MainActivity extends AppCompatActivity { List<Person> mPersonList = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initData(); ListView listView = findViewById(R.id.list_view); CommonAdapter<Person> personCommonAdapter = new CommonAdapter<>(getLayoutInflater(), R.layout.item, BR.person, mPersonList); listView.setAdapter(personCommonAdapter); } private void initData() { for (int i = 0; i < 10; I ++) {mpersonlist. add(new Person(I, "name" + I, "https://c-ssl.duitang.com/uploads/blog/202107/12/20210712132638_bd95f.thumb.1000_0.jpg")); }}}Copy the code

2.3 RecyclerView Loading list

In addition to the different adapter and binding way is different, the rest is still in accordance with the ListView, there is no change, the following code is RecyclerView adapter.

public class CommonAdapter<T> extends RecyclerView.Adapter<CommonAdapter.CommonViewHolder> { private final String TAG = "CommonAdapter"; LayoutInflater mLayoutInflater; int mLayoutId; int mVariableId; List<T> mList; public CommonAdapter(LayoutInflater layoutInflater, int layoutId, int variableId, List<T> list) { this.mLayoutInflater = layoutInflater; this.mLayoutId = layoutId; this.mVariableId = variableId; this.mList = list; } @NonNull @Override public CommonViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int i) { ViewDataBinding viewDataBinding = DataBindingUtil.inflate(mLayoutInflater,mLayoutId, parent, false); CommonViewHolder commonViewHolder = new CommonViewHolder(viewDataBinding.getRoot().getRootView()); commonViewHolder.setViewDataBinding(viewDataBinding); return commonViewHolder; } @Override public void onBindViewHolder(@NonNull CommonAdapter.CommonViewHolder commonViewHolder, Int position) {/ / update the view commonViewHolder. SetContent (mList. Get (position)); } @Override public long getItemId(int position) { return position; } @Override public int getItemCount() { return mList.size(); } public class CommonViewHolder extends RecyclerView.ViewHolder{ ViewDataBinding viewDataBinding; public CommonViewHolder(@NonNull View itemView) { super(itemView); } public ViewDataBinding getViewDataBinding() { return viewDataBinding; } public void setViewDataBinding(ViewDataBinding viewDataBinding) { this.viewDataBinding = viewDataBinding; } public void setContent(T t) { viewDataBinding.setVariable(mVariableId, t); viewDataBinding.executePendingBindings(); }}}Copy the code