Reputation: 109
I'm trying to get into the data binding in android. Because I'm much more experienced with data binding in WPF I'm very confused.
In WPF it is ridiculous easy to bind a list of objects to list view with custom items. Here is a example from a private project:
That's it. There is no need for a glue code or adapter.
My Question:
Because im quite confused, about adapters / inflating things, etc. There is a nice tutorial about binding to lists, but there i still have to write code for the binding.
http://blog.trsquarelab.com/2016/01/data-binding-in-android-listview.html
Upvotes: 0
Views: 1201
Reputation: 1352
Android DataBinding require a little code to accomplish that
The preceding step 1 can be reused by all your projects, the following step 2 & 3 are all layout resource file, just like what WPF do
Step 1. define a BindingAdapter class for ALL AbsListView, this class can be reused by your other project AbsListView
public class AbsListViewBindingAdapter {
@BindingAdapter(value = {"android:items", "android:itemTemplate", "android:dropDownItemTemplate"}, requireAll = false)
public static <T> void setListAdapter(AbsListView view, List<T> items, @LayoutRes int itemTemplateLayout, @LayoutRes int dropDownItemTemplateLayout) {
final ListAdapter oldAdapter = view.getAdapter();
if (oldAdapter instanceof ObservableListAdapter) {
((ObservableListAdapter<T>) oldAdapter).setParams(items, itemTemplateLayout, dropDownItemTemplateLayout);
} else {
view.setAdapter(new ObservableListAdapter<>(view.getContext(), items, itemTemplateLayout, dropDownItemTemplateLayout));
}
}
@BindingAdapter(value = {"android:items", "android:itemTemplate", "android:dropDownItemTemplate"}, requireAll = false)
public static <T> void setListAdapter(AbsListView view, T[] items, @LayoutRes int itemTemplateLayout, @LayoutRes int dropDownItemTemplateLayout) {
setListAdapter(view, items != null ? Arrays.asList(items) : null, itemTemplateLayout, dropDownItemTemplateLayout);
}
@BindingAdapter(value = {"android:items", "android:itemTemplate", "android:dropDownItemTemplate"}, requireAll = false)
public static <T> void setListAdapter(AbsListView view, int[] items, @LayoutRes int itemTemplateLayout, @LayoutRes int dropDownItemTemplateLayout) {
setListAdapter(view, items != null ? IntStream.of(items).boxed().collect(Collectors.toList()) : null, itemTemplateLayout, dropDownItemTemplateLayout);
}
static class ObservableListAdapter<T> extends BaseAdapter {
private List<T> mList;
private int mDropDownResourceId = 0;
private int mResourceId = 0;
private final LayoutInflater mLayoutInflater;
final ObservableList.OnListChangedCallback mListChangedCallback = new ObservableList.OnListChangedCallback() {
@Override
public void onChanged(ObservableList observableList) {
notifyDataSetChanged();
}
@Override
public void onItemRangeChanged(ObservableList observableList, int i, int i1) {
notifyDataSetChanged();
}
@Override
public void onItemRangeInserted(ObservableList observableList, int i, int i1) {
notifyDataSetChanged();
}
@Override
public void onItemRangeMoved(ObservableList observableList, int i, int i1, int i2) {
notifyDataSetChanged();
}
@Override
public void onItemRangeRemoved(ObservableList observableList, int i, int i1) {
notifyDataSetChanged();
}
};
public ObservableListAdapter(Context context, List<T> list, @LayoutRes int itemTemplate, @LayoutRes int dropDownItemTemplate) {
mLayoutInflater = LayoutInflater.from(context);
setParams(list, itemTemplate, dropDownItemTemplate);
}
public void setParams(List<T> list, @LayoutRes int itemTemplate, @LayoutRes int dropDownItemTemplate) {
boolean requireNotifyChange = mResourceId != itemTemplate || mDropDownResourceId != dropDownItemTemplate || !Objects.equals(list, mList);
mResourceId = itemTemplate;
mDropDownResourceId = dropDownItemTemplate;
if (!Objects.equals(list, mList)) {
if (mList instanceof ObservableList) {
((ObservableList) mList).removeOnListChangedCallback(mListChangedCallback);
}
mList = list;
if (mList instanceof ObservableList) {
((ObservableList) mList).addOnListChangedCallback(mListChangedCallback);
}
}
if (requireNotifyChange) {
notifyDataSetChanged();
}
}
@Override
public int getCount() {
return (mList != null) ? mList.size() : 0;
}
@Override
public T getItem(int position) {
return mList != null ? mList.get(position) : null;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return getViewForResource(mResourceId, position, convertView, parent);
}
@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
return getViewForResource(mDropDownResourceId, position, convertView, parent);
}
public View getViewForResource(int resourceId, int position, View convertView, ViewGroup parent) {
final ViewDataBinding binding = (convertView != null)
? DataBindingUtil.getBinding(convertView)
: DataBindingUtil.inflate(mLayoutInflater, resourceId, parent, false);
binding.setVariable(BR.viewModel, getItem(position));
return binding.getRoot();
}
}
}
Step 2. declare your own item template like WPF, and you must declare your viewModel data type, because Android always use static type binding, but WPF use dynamic/reflection type binding, make sure a variable viewModel (= DataContext of WPF) declare in data section for convenience
for example: java.io.File
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="viewModel"
type="java.io.File" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="0px"
android:layout_height="wrap_content"
android:layout_weight="0.2"
android:text='@{viewModel.directory ? "Folder" : "File"}'
android:textSize="20sp" />
<TextView
android:layout_width="0px"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@{viewModel.name}"
android:textSize="20sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text='@{viewModel.length()+ ""}'
android:textSize="20sp" />
</LinearLayout>
</layout>
Step 3. add your item template to your ListView layout file, this example list all the file in the external storage directory
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<import type="android.os.Environment" />
<variable
name="viewModel"
type="com.mycompany.databindingtest.MainActivity.ViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
android:dropDownItemTemplate="@{@layout/file_list_item_template}"
android:itemTemplate="@{@layout/file_list_item_template}"
android:items="@{Environment.externalStorageDirectory.listFiles()}" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
full source code: sample3
Upvotes: 0
Reputation: 1842
Things in Android are different
Is it possible to bind items to a list view without any code in the background?
You have to use data binding code within the adapter class for the list view.
Can i define a "list view item" within the layout of a listview?
No you cannot! A list view item must have a layout of its own.
Since you have confusion about adapters, here are a few points that can make it more clear:
Think of an adapter as a manager that manages the data model and adapts it to the individual entries of the list view. The adapter will populate the layout for each row and assign the data to the individual view in the row.
Without data binding, adapter class can contain a lot code depending on how complex your row UI is. So using data binding will help to remove all the unnecessary code from your adapter class with just a few lines of binding code.
The link you posted is good enough to get started, but I would suggest using a Recycler view instead. Here are other links that you can look at
Upvotes: 1