简体   繁体   中英

Data-binding with dynamically added elements

I have started implementing Android data-binding library to my new app. But, I am having some difficulties with data dynamically added elements. In my POJO it contains a map of <String,Double> . Here, String is the id of a user and Double is the amount. I have a layout file for displaying single entry. So, if map contains 2 elements, it will looks like this:

在此处输入图片说明

Previously, I have done this by inflating layouts inside loop and adding to LinearLayout for each item of a map. But, now I want to do this with data-binding.

As, the number of elements in the Map can be anything from 1 to 20, I can not add in the layout file by default. I have to inflate as per the entries in the map. I have successfully implemented data-binding with the POJO without a Map . But, unable to understand how this can be done with data-binding easily.

So, Is there any way to do this directly in the data binding (in layout file) or with as little code possible in java code?

Edit: For all, who sees that it is a simple RecyclerView , it is not. I agree RecyclerView is very good choice when you have a list with some elements and scrolling that views will be performance benefit. Here I have multiple elements of different types above and below this list. I have used RecyclerView with data-binding also and I know how it works, but here it is not the case. I just want to inflate multiple rows inside a layout (LinearLayout) using data-binding.

Since you prefer to create the layout dynamically yourself, I'd propose the following approach. It's completely theoretical at the moment, but I don't see a reason, why this should not work for you.

Considering you set the Map with a custom attribute for binding.

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

You then have to create a @BindingAdapter to handle what the adapter would do for you.

@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);
    }
}

Here you inflate the layout for the item and add it to the parent layout. You could generalize it even more by adding more attributes to the layout and binding adapter.

That's not what data bindings are used for. These are there to bind data to a statically defined layout. Creating or managing view hierarchies might be possible, but also a lot more complicated than just using RecyclerView with its adapters.

If you like it better to just set the data from the layout, you can write Custom Setters for the Data Binding Library and create the adapter in this scope instead.

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

To make the data to be added/removed in dynamic way, you should use the Adapter instead of add it using your way. As google suggested, RecycelerView have better performance in UI and memory control. I have made some simple code which might suit to you.

For the Adapter , and the binding method .

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);
    }
}

There is some problem if you insist to use Map instead of list for the data since Map is not indexed but the adapter gets Data based on the index of the dataset .

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?

In your xml, you can set your map to the recyclerview and your dataset can be set in 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_width="match_parent"
        android:layout_height="match_parent"
        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_width="match_parent"
            android:layout_height="wrap_content"
            data:map="@{map}"/>

    </LinearLayout>
</layout>

In your activity code,

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

This is actually a query to the accepted answer. I do not have enough reputation to add a comment. What is 'MyItem' in the answer? Is it the variable that is defined in the data tag of the layout?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM