【问题标题】:Android data binding to listview like WPF?Android数据绑定到像WPF这样的listview?
【发布时间】:2021-02-25 13:06:06
【问题描述】:

我正在尝试进入 android 中的数据绑定。 因为我对 WPF 中的数据绑定有更多的经验,所以我很困惑。

尝试了什么:

在 WPF 中,很容易将对象列表绑定到带有自定义项的列表视图。这是一个私人项目的例子:

就是这样。不需要胶水代码或适配器。

我的问题:

  • 是否可以在没有任何代码的情况下将项目绑定到列表视图 背景?
  • 我可以在列表视图的布局中定义“列表视图项”吗?

因为我很困惑,关于适配器/膨胀的东西等。 有一个很好的关于绑定到列表的教程,但是我仍然需要为绑定编写代码。

http://blog.trsquarelab.com/2016/01/data-binding-in-android-listview.html

【问题讨论】:

  • 请不要将源代码发布为截图。并且,在未来,请提供实际症状的详细信息(编译错误、运行时崩溃等),而不仅仅是说“不,不工作”。 “是否可以在没有任何后台代码的情况下将项目绑定到列表视图?” - AFAIK,不,尽管其他人可能已经编写了一个库来支持这一点。 “我可以在列表视图的布局中定义一个“列表视图项”吗?” -- 没有。
  • 抱歉,这更像是一个概念问题,而不是特定于错误的问题。但我以后会以文本形式提供代码!

标签: android listview data-binding


【解决方案1】:

Android 中的东西是不同的

是否可以在没有任何后台代码的情况下将项目绑定到列表视图?

您必须在列表视图的适配器类中使用数据绑定代码。

我可以在列表视图的布局中定义“列表视图项”吗?

不,你不能!列表视图项必须有自己的布局。

由于您对适配器有混淆,这里有几点可以让您更清楚:

将适配器视为管理数据模型并将其调整到列表视图的各个条目的管理器。适配器将为每一行填充布局并将数据分配给行中的各个视图。

如果没有数据绑定,适配器类可以包含很多代码,具体取决于您的行 UI 的复杂程度。因此,使用数据绑定将有助于从适配器类中删除所有不必要的代码,只需几行绑定代码。

您发布的链接足以开始使用,但我建议改用 Recycler 视图。以下是您可以查看的其他链接

  1. https://medium.com/google-developers/android-data-binding-recyclerview-db7c40d9f0e4
  2. https://android.jlelse.eu/recyclerview-with-endlessscroll-2c503008522f

【讨论】:

  • 谢谢!适配器的事情现在更清楚了。我会看看你的链接。
【解决方案2】:

Android DataBinding 需要一些代码来完成这项工作

前面的步骤 1 可以被你所有的项目复用,后面的步骤 2 和 3 都是布局资源文件,就像 WPF 做的一样

步骤1.为所有AbsListView定义一个BindingAdapter类,这个类可以被你的其他项目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();
        }
    }
}

步骤 2. 像 WPF 一样声明你自己的项目模板,并且你必须声明你的 viewModel 数据类型,因为 Android 总是使用静态类型绑定,但 WPF 使用动态/反射类型绑定,请确保为方便起见,变量 viewModel(= WPF 的 DataContext)在数据部分声明

例如: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>

步骤 3. 将您的项目模板添加到您的 ListView 布局文件中,此示例列出了外部存储目录中的所有文件

<?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>

完整源代码:sample3

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-01-01
    • 2015-04-12
    • 1970-01-01
    • 1970-01-01
    • 2012-05-14
    • 2010-11-01
    • 2011-01-14
    • 2011-11-13
    相关资源
    最近更新 更多