24

This is my first attempt at data-binding with a RecyclerView but not my first use of RecyclerView itself.

For some reason none of the adapter methods are called - not even getItemCount(). It could be a stupid problem with my RecyclerView and nothing to do with the data binding at all but I can't see anything wrong.

       View rootview = inflater.inflate(R.layout.fragment_profile_first, container, false);
    // Initialize recycler view

    RecyclerView badgesRV = (RecyclerView) rootview.findViewById(R.id.badgesRV);
    LinearLayoutManager llm = new LinearLayoutManager(getActivity());
    llm.setOrientation(LinearLayoutManager.HORIZONTAL);
    badgesRV.setLayoutManager(llm);

    BadgeAdapter badgeAdapter = new BadgeAdapter(profileObject.badgesEntity.badges);
    badgesRV.setAdapter(badgeAdapter);

Adapter:

    public class BadgeAdapter extends RecyclerView.Adapter<BadgeAdapter.BadgeBindingHolder>{

    private static final int MAX_BADGES_TO_DISPLAY = 5;
    private BadgeObject[] badges;

    public BadgeAdapter(BadgeObject[] badges){
        this.badges = badges;
    }

    @Override
    public BadgeBindingHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.profile_badge_row, parent, false);
        BadgeBindingHolder holder = new BadgeBindingHolder(v);
        return holder;
    }

    @Override
    public void onBindViewHolder(BadgeBindingHolder holder, int position) {
        final BadgeObject badgeObject = badges[position];
        holder.getBinding().setVariable(BR.badge, badgeObject);
        holder.getBinding().executePendingBindings();

    }

    @Override
    public int getItemCount() {
        Log.d(TAG, "item count = " + Math.min(MAX_BADGES_TO_DISPLAY, badges.length));
        return Math.min(MAX_BADGES_TO_DISPLAY, badges.length);
    }

    public class BadgeBindingHolder extends RecyclerView.ViewHolder{
        private ViewDataBinding binding;

        public BadgeBindingHolder(View rowView) {
            super(rowView);
            binding = DataBindingUtil.bind(rowView);
        }
        public ViewDataBinding getBinding() {
            return binding;
        }
    }
}

profile_badge_row.xml

<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">

<data>
    <variable name="badge" type="parseJsonEntities.requestObjects.BadgeObject"/>
</data>

<LinearLayout
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@{badge.badgeImage}"/>

</LinearLayout>

I have checked and there is definitely data there. What have I missed?

=====================

Update:

From what I can tell RecyclerView simply doesn't work inside a Data Binding layout. I created a separate layout with just my RV and it worked perfectly. As soon as I included it with my main layout it ceased to work.

Not sure if this is a bug or a feature.

So, I thought that maybe if I make it a custom view maybe it will work and it does. My problem is I don't know how to pass a value into my custom view.

I looked here but couldn't figure out exactly what he means. This is my code in the custom view.

    LayoutInflater inflater = (LayoutInflater)
            context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    View view = inflater.inflate(R.layout.profile_badges_layout, this, true);
    ProfileBadgesLayoutBinding binding = ProfileBadgesLayoutBinding.inflate(inflater);

    RecyclerView badgesRV = (RecyclerView) view.findViewById(R.id.badgesRV);

    LinearLayoutManager llm = new LinearLayoutManager(context);
    llm.setOrientation(LinearLayoutManager.HORIZONTAL);
    badgesRV.setLayoutManager(llm);

    BadgeAdapter badgeAdapter = new BadgeAdapter(null);
    badgesRV.setAdapter(badgeAdapter);

This gives a message that ProfileBadgesLayoutBinding is not found.

3
  • where is your fragment xml ?? Commented Jul 16, 2016 at 5:36
  • 1
    RecyclerView works perfectly with DataBinding, since many others are using it without problems. Can you post your layout where your RecyclerView is located? Maybe some strange behaviour with other scrolling Views. Commented Jul 16, 2016 at 9:22
  • 1
    Also, what is badge.badgeImage? A Resource id, a Drawable..? Commented Jul 18, 2016 at 6:25

2 Answers 2

23
+25

In your onCreateViewHolder:

@Override
public BadgeBindingHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    ProfileBadgeRowBinding binding = DataBindingUtil.inflate(inflater, R.layout.profile_badge_row, parent, false);
    BadgeBindingHolder holder = new BadgeBindingHolder(binding.getRoot(), binding);
    return holder;
}

And add the binding in your Item View

public class BadgeBindingHolder extends RecyclerView.ViewHolder{
    private ProfileBadgeRowBinding binding;

    public BadgeBindingHolder(View rowView, ProfileBadgeRowBinding binding) {
        super(rowView);
        this.binding = binding;
    }

    ...

}

And in the bindToViewHolder set the variable

@Override
public void bindToViewHolder(RecyclerView.ViewHolder viewHolder) {
    ProfileBadgeRowBinding binding = ((BadgeBindingHolder) viewHolder).binding;
    binding.setVariable(BR.badge, badge);
    binding.executePendingBindings(); // This line is important, it will force to load the variable in a custom view
}

If you want to access a view, add an id to a view in your xml.

<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>
        <variable name="badge" type="parseJsonEntities.requestObjects.BadgeObject"/>
    </data>

    <LinearLayout
        android:orientation="vertical" android:layout_width="match_parent"
        android:layout_height="match_parent">

        <ImageView
            android:id="@+id/image"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

    </LinearLayout>
</layout>

And then you can access the id's in your bindToViewHolder

@Override
public void bindToViewHolder(RecyclerView.ViewHolder viewHolder) {
    ProfileBadgeRowBinding binding = ((BadgeBindingHolder) viewHolder).binding;
    binding.setVariable(BR.badge, badge);
    binding.executePendingBindings();

    binding.image.setImage(...);
}

More about custom views https://developer.android.com/topic/libraries/data-binding/index.html#dynamic_variables

Sign up to request clarification or add additional context in comments.

3 Comments

Thank you ! My problem was that I didn't use binding.executePendingBindings(); It didn't look that obvious to me. Thank you very much !!!!
I have so many issues in all adapters due to data binding. you solved it. Thanks for answer
stackoverflow.com/questions/49726385/… Check this out if you are using diff utils and still facing issues. The stupid submitList has a weird logic
0

It is much easier to do it today, follow this example:

public class LanguageRecyclerAdapter extends RecyclerView.Adapter<LanguageRecyclerAdapter.LanguageViewHolder> {

    private List<Language> languages;

    public class LanguageViewHolder extends RecyclerView.ViewHolder {

        final LanguageSetItemBinding item;

        public void bind(final Language language) {
            item.setLanguage(language.getName());
            item.setFlag(language.getFlagRes());
            item.getRoot().setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
//do somthing if you need
                }
            });
            item.executePendingBindings();
        }

        public LanguageViewHolder(LanguageSetItemBinding itemView) {
            super(itemView.getRoot());
            item =itemView;
        }
    }

    public LanguageRecyclerAdapter(List<Language> languages) {
        this.languages = languages;
    }

    public void onBindViewHolder(LanguageViewHolder holder, int position) {
        Language item = languages.get(position);
        holder.bind(item);
    }

    public LanguageViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
        LanguageSetItemBinding item = LanguageSetItemBinding.inflate(layoutInflater, parent,false);
        return new LanguageViewHolder(item);
    }

    @Override
    public int getItemCount() {
        return languages.size();
    }

    @Override
    public int getItemViewType(int position) {
        return super.getItemViewType(position);
    }
}

the LanguageSetItemBinding xml looks like this, it has inside a TextView and ImageView:

<?xml version="1.0" encoding="utf-8"?>
<layout >
    <data>
        <variable name="language" type="String" />
        <variable name="flag" type="int" />
    </data>

    <android.widget.LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="166dp"
        android:layout_height="166dp"
        android:background="@color/colorWhite"
        android:orientation="vertical"
        android:layout_margin="20dp"
        android:gravity="center">
        <TextView
            android:layout_marginTop="20dp"
            android:gravity="center"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="20sp"
            android:text="@{language}"
            android:textColor="@color/colorBlack"
            android:textAlignment="center"/>
        <ImageView
            android:textColor="?attr/errorColor"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:src="@{context.getDrawable(flag)}"
            android:gravity="center"
            android:paddingTop="3dp"/>
    </android.widget.LinearLayout>
</layout>

I have an activity with a recyclerview inside, and in the activity OmCreateView I attached this recycler to the adapter I created above:

final ArrayList<Language> lang = new ArrayList(languageList.values());
final LanguageRecyclerAdapter adapter = new LanguageRecyclerAdapter(lang);
mBinding.languageRecyclerView.setAdapter(adapter);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.