Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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:

enter image description here

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.

like image 288
kirtan403 Avatar asked Sep 05 '16 07:09

kirtan403


2 Answers

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.

like image 97
tynn Avatar answered Sep 30 '22 13:09

tynn


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);
like image 27
Long Ranger Avatar answered Sep 30 '22 15:09

Long Ranger