1

It seems to me that Android Data Binding is an interesting tool, but it doesn't tie very well with the (overly) complex architecture of Android. Many example or tutorials show only some basic scenario that obviously works, but when things become harder then problems arise.

For example: many views (like RecyclerView or ViewPager) require adapters or decorators that need Context and it seems wrong to pass the Context to every ViewModel because it breaks the separation of layers.

ViewFlipper: how do you show next or previous by just binding a property of the ViewModel?

How would you bind a RecyclerView with a LinearLayout, an ItemAnimation and an ItemDecoration? Can you show some real-world complex example of Android Data Binding at work?

Alessandro
  • 3,666
  • 2
  • 28
  • 41
  • 1
    why would you need to pass a `Context` when using `RecyclerView`? – pskink Apr 05 '17 at 16:51
  • Your question is much to general. Could you specify it a little better to not require an entire essay to answer? – tynn Apr 05 '17 at 22:22
  • [This](http://saulmm.github.io/from-design-to-android-part1) may be an example that covers more than simple bindings. It even has a `ViewFlipper`. – yennsarah Apr 06 '17 at 05:37
  • @pskink: I need to pass the context to access local resources (like strings or dimensions) in my adapter or decoration – Alessandro Apr 06 '17 at 06:02

1 Answers1

5

You might be interested in these two articles:

On using data binding with RecyclerView: https://medium.com/google-developers/android-data-binding-recyclerview-db7c40d9f0e4

On using data binding with lists without RecyclerView (e.g. LinearLayout): https://medium.com/google-developers/android-data-binding-list-tricks-ef3d5630555e

With ItemDecoration, you must add your own BindingAdapter because RecyclerView allows multiple item decorations. Something like this should work:

@BindingAdapter("itemDecoration")
public static void setItemDecoration(RecyclerView view, ItemDecoration old,
        ItemDecoration newVal) {
    if (old != null) {
        view.removeItemDecoration(old);
    }
    if (newVal != null) {
        view.addItemDecoration(newVal);
    }
}

Your question about Context is a little confusing. I'm trying to imagine how you would need Context in data binding. Data binding expressions do not allow new so you wouldn't be able to create one that way. Perhaps you're thinking of using some representation instead:

@BindingAdapter("dividerDirection")
public static void setItemDecoration(RecyclerView view, int oldDirection, int newDirection) {
    if (oldDirection != newDirection) {
        ItemDecoration decoration =
                new DividerItemDecoration(view.getContext(), newDirection);
        ItemDecoration old = ListenerUtil.trackListener(view, decoration, R.id.decoration);
        if (old != null) {
            view.removeItemDecoration(old);
        }
        view.addItemDecoration(decoration);
    }
}

and it would be bound like this:

<android.support.v7.widget.RecyclerView
     app:dividerDirection="@{DividerItemDecoration.HORIZONTAL}" .../>

For other uses, you are automatically granted a built-in "context" variable in your layout and you can pass it to any methods you call. It is the Context of the root View of the bound view hierarchy and should work for most of your needs. You should not need to pass the context in the model for most uses.

I expect that should also answer your question about ItemAnimator, though you don't need a special BindingAdapter to use an attribute since it has a setter already:

<android.support.v7.widget.RecyclerView
     app:itemAnimator="@{model.animator}" .../>
George Mount
  • 20,708
  • 2
  • 73
  • 61
  • In your ItemDecoration example, what should I do if my ItemDecoration needs a Context to access some dimen resource? How could I instantiate it from the ViewModel without having a Context passed from the View? – Alessandro Apr 06 '17 at 06:19
  • The easy way is to pass the dimension as an attribute `app:dividerWidth="@{@dim/thickDivider}"`. You'll need a multi-attribute BindingAdapter. – George Mount Apr 06 '17 at 13:09
  • Just a piece of information if you weren't aware. You can get the context from the passed view into the binding adapter, so no need to pass the context. Example. Glide.with(view.getContext()) .load(imageUrl) – Sam Aug 03 '18 at 15:16