动态添加元素的数据绑定

Posted

技术标签:

【中文标题】动态添加元素的数据绑定【英文标题】:Data-binding with dynamically added elements 【发布时间】:2017-01-12 13:24:06 【问题描述】:

我已经开始在我的新应用中实现 android 数据绑定库。但是,我在使用数据动态添加元素时遇到了一些困难。在我的POJO 中,它包含<String,Double> 的地图。这里,String 是用户的 id,Double 是数量。我有一个用于显示单个条目的布局文件。因此,如果 map 包含 2 个元素,它将如下所示:

以前,我通过在循环内膨胀布局并为地图的每个项目添加到LinearLayout 来做到这一点。但是,现在我想通过数据绑定来做到这一点。

由于Map 中的元素个数可以是1 到20 之间的任意值,我默认不能在布局文件中添加。我必须根据地图中的条目充气。我已经成功实现了与POJO 的数据绑定,而没有Map。但是,无法理解如何通过数据绑定轻松做到这一点。

那么,有没有办法直接在数据绑定中(在布局文件中)或在 java 代码中使用尽可能少的代码?

编辑: 对于所有人来说,谁看到它是一个简单的RecyclerView,它不是。我同意RecyclerView 是一个非常好的选择,当你有一个包含一些元素的列表并且滚动视图将提高性能时。在这里,我在此列表上方和下方有多个不同类型的元素。我也将RecyclerView 与数据绑定一起使用,并且我知道它是如何工作的,但这里并非如此。我只想使用数据绑定在布局 (LinearLayout) 中填充多行。

【问题讨论】:

你试过RecyclerView吗? 我不需要 RecyclerView,大多数时候最多有 5-7 个元素。 RecyclerView 在有很多滚动元素时会很有用 在你说的1到20的问题中。如果以防万一,元素数量增加到> 7,为了安全起见,我建议使用RecyclerView。您的绑定问题也将得到解决。如果您以编程方式添加LinearLayout 中的所有元素,并且在屏幕旋转时,您必须滚动才能看到所有元素,这将导致此处出现内存问题。 当然你应该使用RecyclerView 【参考方案1】:

由于您更喜欢自己动态创建布局,我建议采用以下方法。目前这完全是理论上的,但我看不出为什么这不适合你。

考虑到您使用自定义属性设置Map 进行绑定。

<LinearLayout ...
    app:inflateData="@dataMap" />

然后您必须创建一个@BindingAdapter 来处理适配器将为您执行的操作。

@BindingAdapter("inflateData")
public static void inflateData(LinearLayout layout, Map<String, Double> data) 
    LayoutInflater inflater = LayoutInflater.from(layout.getContext());
    for (Entry<String, Double> entry : data.entrySet()) 
        MyItem myItem = inflater.inflate(R.layout.my_item, layout, true);
        myItem.setKey(entry.getKey);
        myItem.setValue(entry.getValue);
    

在这里,您为项目扩展布局并将其添加到父布局。您可以通过向布局和绑定适配器添加更多属性来进一步概括它。

【讨论】:

太棒了!这应该有效。我要试一试。谢谢! 如何将 myItem 添加到父布局?我知道 addView() 方法,但问题是如何访问父布局以调用 addView() 方法? 嗨,它的答案非常好,但我有一些疑问 inflate 返回视图的对象,我无法找到 setKey 和 setValue 方法,例如:myItem.setKey【参考方案2】:

这不是数据绑定的用途。这些用于将数据绑定到静态定义的布局。创建或管理视图层次结构可能是可能的,但也比仅使用 RecyclerView 及其适配器复杂得多。

如果您更喜欢只设置布局中的数据,您可以为数据绑定库编写Custom Setters,并在此范围内创建适配器。

@BindingAdapter("adapter")
public static void bindAdapter(RecyclerView view, Map<String, Double> data) 
   RecyclerView.Adapter adapter = ...;
   view.setAdapter(adapter);

【讨论】:

是的,我知道它用于静态定义的布局。在我的布局中,我使用数据绑定在这个小列表的上方和下方绑定了许多属性。但是,就这一部分我被卡住了。并没有找到任何方法。但是,我认为这里不需要回收站视图。【参考方案3】:

要以动态方式添加/删除数据,您应该使用Adapter 而不是使用您的方式添加它。正如谷歌建议的那样,RecycelerView 在 UI 和内存控制方面有更好的表现。我已经编写了一些可能适合您的简单代码。

对于Adapter,以及绑定方法

public class MyOwnBindingUtil 
    public static class Holder extends RecyclerView.ViewHolder 
        private ItemBinding mItemBinding;
        public Holder(ItemBinding itemView) 
            super(itemView.getRoot());
            mItemBinding = itemView;
        
    
    public static class OwnAdapter extends RecyclerView.Adapter<Holder> 
        private Map<String, String> mMap;
        private List<String> keys;
        private List<Double> values;
        public OwnAdapter() 
            keys = new ArrayList<>();
            values = new ArrayList<>();
        
        public void add(String key, Double value) 
            keys.add(key);
            values.add(value);
        
        @Override
        public Holder onCreateViewHolder(ViewGroup parent, int viewType) 
            ItemBinding binding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.item, parent, false);
            return new MyOwnBindingUtil.Holder(binding);
        
        @Override
        public void onBindViewHolder(Holder holder, int position) 
            holder.mItemBinding.setKey(keys.get(position));
            holder.mItemBinding.setValue(String.valueOf(values.get(position)));
        
        @Override
        public int getItemCount() 
            return keys.size();
        
    
    @BindingAdapter("data:map")
    public static void bindMap(RecyclerView pRecyclerView, Map<String, Double> pStringStringMap) 
        pRecyclerView.setLayoutManager(new LinearLayoutManager(pRecyclerView.getContext()));
        OwnAdapter lAdapter = new OwnAdapter();
        for (Map.Entry<String, Double> lStringStringEntry : pStringStringMap.entrySet()) 
            lAdapter.add(lStringStringEntry.getKey(), lStringStringEntry.getValue());
        
        pRecyclerView.setAdapter(lAdapter);
    

如果您坚持使用 Map 而不是列表来获取数据,则会出现一些问题,因为 Map 没有索引,但 适配器会根据数据集的索引获取 Data .

https://www.mkyong.com/java8/java-8-convert-map-to-list/

How to convert a Map to List in Java?

How does one convert a HashMap to a List in Java?

在您的xml中,您可以将您的地图设置为recyclerview,您的数据集可以在xml中设置。

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:data="http://schemas.android.com/tools"
    >


    <data>

        <import type="java.lang.String"/>

        <import type="java.util.Map"/>

        <variable
            name="map"
            type="Map&lt;String, String&gt;"/>
    </data>

    <LinearLayout

        android:layout_
        android:layout_
        android:orientation="vertical"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        >

        <android.support.v7.widget.RecyclerView
            android:layout_
            android:layout_
            data:map="@map"/>

    </LinearLayout>
</layout>

在您的活动代码中,

    Map<String, String> lStringStringMap = new HashMap<>();
    lStringStringMap.put("A", "A1");
    lStringStringMap.put("B", "B1");
    lStringStringMap.put("C", "C1");
    mBinding.setMap(lStringStringMap);

【讨论】:

感谢您的建议。我同意 RecyclerView 是一个非常好的选择,当你有一个包含一些元素的列表并且滚动视图将提高性能时。但是,在这里使用 RecyclerView 并不是一个好的选择,因为我想要一些视图(通常在 5-7 左右)与此列表上方和下方的许多不同视图内联。我已经将数据绑定与回收器视图一起使用,它工作得很好。但在这里,它不是必需的。 这意味着你有 binding1,binding2,binding3... 但不超过 7 个。而 7 个绑定(如果是 7 )是完全不同的布局。您想将其添加到单个容器中(可能是 LinearLayout)。现在您想找到一种更简单的方法来在单个容器内添加 7 种不同的布局,而不是使用数据绑定逐个添加? 不,不是那样的。让我解释。假设我有一个包含这么多元素的 POJO:标题、描述、所有者、created_at、created_by、modified_at、modified_by、图标、类别、标签、位置、参与者。所以,有了这个 POJO 和数据绑定,我可以轻松设置所有内容。但是,“参与者”部分是一张棘手的地图。对于那张地图,我想为每个地图显示一个相当小的行。【参考方案4】:

这实际上是对已接受答案的查询。我没有足够的声誉来添加评论。 答案中的“MyItem”是什么?是不是布局的data标签中定义的变量?

【讨论】:

以上是关于动态添加元素的数据绑定的主要内容,如果未能解决你的问题,请参考以下文章

在js中怎么给动态生成的元素绑定事件

jQuery动态创建的元素为啥不能绑定事件

js 为动态添加的元素绑定事件

vue动态绑定class

动态生成元素的淘汰数据绑定

05《Vue 入门教程》Vue 动态样式绑定