Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android Data Binding on Individual Views Added to LinearLayout Programmatically?

I am able to get "activity-wide" data binding to work without any difficulties whatsoever. However, when I try to setup bindings for specific Views, say inflated Views that I'm programmatically adding to a LinearLayout, nothing I've tried works.

For example, binding data to an Activity's view is trivial. Typically in the onCreate() method one just has to add:

MainActivityBinding binding = DataBindingUtil.setContentView(this, R.layout.main_activity);
User user = new User("Test", "User");
binding.setUser(user);

That's it! The view main_activity.xml will be "bound" to the binding through some magic (this is my problem) and variable substitution will work as expected in the main_activity.xml.

I consulted Google's documentation regarding RecyclerView use and attempted to utilize this method in my inflated Views. I did this by creating a ListItemBinding (my .xml is just called list_item.xml and this file is auto-generated off of the .xml layout) for each ViewHolder, the ListItemBinding is passed in to the ViewHolder to be held as a reference through the Constructor of each ViewHolder.

Now of course this does not work, and I am guessing that the reason for this is that there's no explicit tie between my View and the Binding, however, there's no tie between the Activity above and the Binding either, so why does that work?

I'm digging into the source code to see how the binding works on Activities, I'm sure that will have my answer, but if anyone else can chime in before I spend multiple hours, I would appreciate the head-start!

I will report all findings here, thank you very much

like image 817
AutoM8R Avatar asked Apr 14 '16 21:04

AutoM8R


1 Answers

You can add dynamically-inflated data bound views to a LinearLayout. You will have to programmatically set the data for the data bound view.

void addViewToViewGroup(ViewGroup viewGroup, User user) {
  LayoutInflater inflater = LayoutInflater.from(viewGroup.getContext());
  MyLayoutBinding binding = MyLayoutBinding.inflate(inflater, viewGroup,true);
  binding.setUser(user);
}

If there is a known Binding that you're attaching to, you can pull the data from there instead. Here, I assume the containing ViewGroup is in a layout outer.xml and generated binding OuterBinding.

void addViewToViewGroup(ViewGroup viewGroup) {
  OuterBinding outer = DataBindingUtil.findBinding(viewGroup);
  LayoutInflater inflater = LayoutInflater.from(viewGroup.getContext());
  MyLayoutBinding binding = MyLayoutBinding.inflate(inflater, viewGroup, true);
  binding.setUser(outer.getUser());
}

You can see that there is no direct binding between the OuterBinding's user and MyLayoutBinding's user. That means that if you update it, you won't see it reflected. With included layouts, the framework does work to bind it that you have to do manually. To get dynamic updates, you can do it this way:

outer.addOnPropertyChangedCallback(new OnPropertyChangedCallback() {
   @Override
   public void onPropertyChanged(Observable sender, int propertyId) {
       if (propertyId == BR.user) {
           binding.setUser(outer.getUser());
           break;
       }
   }
});
like image 195
George Mount Avatar answered Oct 07 '22 01:10

George Mount