(BR.item, R.layout.item_page)
39 |
40 | /**
41 | * Binds multiple items types to different layouts based on class. This could have also be
42 | * written manually as
43 | * `public final OnItemBind
*
54 | */
55 | val multipleItems = OnItemBindClass().apply {
56 | map(BR.item, R.layout.item_header_footer)
57 | map(BR.item, R.layout.item)
58 | }
59 |
60 | val multipleItems2 = itemBindingOf { itemBinding, _, item ->
61 | when (item::class) {
62 | String::class -> itemBinding.set(BR.item, R.layout.item_header_footer)
63 | MutableItem::class -> itemBinding.set(BR.item, R.layout.item)
64 | }
65 | }.bindExtra(BR.item, this)
66 |
67 | /**
68 | * Define stable item ids. These are just based on position because the items happen to not
69 | * ever move around.
70 | */
71 | val itemIds =
72 | BindingListViewAdapter.ItemIds { position, _ -> position.toLong() }
73 |
74 | /**
75 | * Define page titles for a ViewPager
76 | */
77 | val pageTitles =
78 | BindingViewPagerAdapter.PageTitles { _, item -> "Item ${item.index + 1}" }
79 |
80 | /**
81 | * Custom view holders for RecyclerView
82 | */
83 | val viewHolder =
84 | BindingRecyclerViewAdapter.ViewHolderFactory { binding -> MyAwesomeViewHolder(binding.root) }
85 |
86 | fun setCheckable(checkable: Boolean) {
87 | this.checkable = checkable
88 | for (item in items) {
89 | item.checkable = checkable
90 | }
91 | }
92 |
93 | private class MyAwesomeViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
94 |
95 | override fun onAddItem() {
96 | items.add(MutableItem(index = items.size, checkable = checkable))
97 | }
98 |
99 | override fun onRemoveItem() {
100 | if (items.size > 1) {
101 | items.removeAt(items.size - 1)
102 | }
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/bindingcollectionadapter-recyclerview/src/androidTest/java/me/tatarka/bindingcollectionadapter2/recyclerview/RecyclerViewInflationTest.java:
--------------------------------------------------------------------------------
1 | package me.tatarka.bindingcollectionadapter2.recyclerview;
2 |
3 | import androidx.databinding.DataBindingUtil;
4 | import androidx.databinding.ViewDataBinding;
5 | import androidx.test.annotation.UiThreadTest;
6 | import androidx.test.rule.ActivityTestRule;
7 | import androidx.test.runner.AndroidJUnit4;
8 | import androidx.recyclerview.widget.RecyclerView;
9 |
10 | import android.view.LayoutInflater;
11 |
12 | import me.tatarka.bindingcollectionadapter2.ItemBinding;
13 |
14 | import org.junit.Before;
15 | import org.junit.Rule;
16 | import org.junit.Test;
17 | import org.junit.runner.RunWith;
18 |
19 | import java.util.Arrays;
20 | import java.util.List;
21 |
22 | import me.tatarka.bindingcollectionadapter2.BindingRecyclerViewAdapter;
23 | import me.tatarka.bindingcollectionadapter2.recyclerview.test.R;
24 |
25 | import static org.assertj.core.api.Assertions.assertThat;
26 |
27 | @RunWith(AndroidJUnit4.class)
28 | public class RecyclerViewInflationTest {
29 |
30 | @Rule
31 | public ActivityTestRule activityTestRule = new ActivityTestRule<>(EmptyActivity.class);
32 |
33 | private LayoutInflater inflater;
34 |
35 | @Before
36 | public void setup() throws Exception {
37 | inflater = LayoutInflater.from(activityTestRule.getActivity());
38 | }
39 |
40 | @Test
41 | @UiThreadTest
42 | public void recyclerView() {
43 | List items = Arrays.asList("one", "two", "three");
44 | TestHelpers.ViewModel viewModel = new TestHelpers.ViewModel(items, ItemBinding.of(BR.item, R.layout.item));
45 | ViewDataBinding binding = DataBindingUtil.inflate(inflater, R.layout.recycler_view, null, false);
46 | binding.setVariable(BR.viewModel, viewModel);
47 | binding.executePendingBindings();
48 |
49 | RecyclerView recyclerView = (RecyclerView) binding.getRoot();
50 | @SuppressWarnings("unchecked")
51 | BindingRecyclerViewAdapter adapter = (BindingRecyclerViewAdapter) recyclerView.getAdapter();
52 |
53 | assertThat(TestHelpers.iterable(adapter)).containsExactlyElementsOf(items);
54 | }
55 |
56 | @Test
57 | @UiThreadTest
58 | public void recyclerViewAdapter() {
59 | List items = Arrays.asList("one", "two", "three");
60 | TestHelpers.ViewModel viewModel = new TestHelpers.ViewModel(items, ItemBinding.of(BR.item, R.layout.item));
61 | ViewDataBinding binding = DataBindingUtil.inflate(inflater, R.layout.recycler_view_adapter, null, false);
62 | binding.setVariable(BR.viewModel, viewModel);
63 | binding.executePendingBindings();
64 |
65 | RecyclerView recyclerView = (RecyclerView) binding.getRoot();
66 | @SuppressWarnings("unchecked")
67 | BindingRecyclerViewAdapter adapter = (BindingRecyclerViewAdapter) recyclerView.getAdapter();
68 |
69 | assertThat(adapter).isInstanceOf(TestHelpers.MyBindingRecyclerViewAdapter.class);
70 | }
71 |
72 | @Test
73 | @UiThreadTest
74 | public void recyclerViewDiff() {
75 | List items = Arrays.asList("one", "two", "three");
76 | TestHelpers.ViewModel viewModel = new TestHelpers.ViewModel(items, ItemBinding.of(BR.item, R.layout.item));
77 | ViewDataBinding binding = DataBindingUtil.inflate(inflater, R.layout.recycler_view_diff, null, false);
78 | binding.setVariable(BR.viewModel, viewModel);
79 | binding.executePendingBindings();
80 |
81 | RecyclerView recyclerView = (RecyclerView) binding.getRoot();
82 | @SuppressWarnings("unchecked")
83 | BindingRecyclerViewAdapter adapter = (BindingRecyclerViewAdapter) recyclerView.getAdapter();
84 |
85 | assertThat(TestHelpers.iterable(adapter)).containsExactlyElementsOf(items);
86 | }
87 | }
--------------------------------------------------------------------------------
/bindingcollectionadapter-recyclerview/src/androidTest/java/me/tatarka/bindingcollectionadapter2/recyclerview/TestHelpers.java:
--------------------------------------------------------------------------------
1 | package me.tatarka.bindingcollectionadapter2.recyclerview;
2 |
3 | import androidx.annotation.NonNull;
4 | import androidx.recyclerview.widget.DiffUtil;
5 | import androidx.test.espresso.core.internal.deps.guava.base.Joiner;
6 |
7 | import java.util.Iterator;
8 | import java.util.List;
9 |
10 | import me.tatarka.bindingcollectionadapter2.BindingListViewAdapter;
11 | import me.tatarka.bindingcollectionadapter2.BindingRecyclerViewAdapter;
12 | import me.tatarka.bindingcollectionadapter2.ItemBinding;
13 |
14 | public class TestHelpers {
15 |
16 | public static class ViewModel {
17 | public final List items;
18 | public final ItemBinding itemBinding;
19 | public final BindingListViewAdapter.ItemIds itemIds;
20 | public final MyBindingRecyclerViewAdapter adapter = new MyBindingRecyclerViewAdapter<>();
21 | public final DiffUtil.ItemCallback diff = new DiffUtil.ItemCallback() {
22 | @Override
23 | public boolean areItemsTheSame(@NonNull String oldItem, @NonNull String newItem) {
24 | return oldItem.equals(newItem);
25 | }
26 |
27 | @Override
28 | public boolean areContentsTheSame(@NonNull String oldItem, @NonNull String newItem) {
29 | return true;
30 | }
31 | };
32 |
33 | public ViewModel(List items, ItemBinding itemBinding) {
34 | this(items, itemBinding, null);
35 | }
36 |
37 | public ViewModel(List items, ItemBinding itemBinding, final List itemIds) {
38 | this.items = items;
39 | this.itemBinding = itemBinding;
40 | if (itemIds != null) {
41 | this.itemIds = new BindingListViewAdapter.ItemIds() {
42 | @Override
43 | public long getItemId(int position, String item) {
44 | return itemIds.get(position);
45 | }
46 | };
47 | } else {
48 | this.itemIds = null;
49 | }
50 | }
51 | }
52 |
53 | public static class MyBindingRecyclerViewAdapter extends BindingRecyclerViewAdapter {
54 | }
55 |
56 | public static Iterable iterable(final BindingRecyclerViewAdapter adapter) {
57 | if (adapter == null) return null;
58 | return new IndexIterable<>(new IndexIterable.Factory>() {
59 | @Override
60 | public IndexIterator get() {
61 | return new IndexIterator() {
62 | @Override
63 | int getCount() {
64 | return adapter.getItemCount();
65 | }
66 |
67 | @Override
68 | T get(int index) {
69 | return adapter.getAdapterItem(index);
70 | }
71 | };
72 | }
73 | });
74 | }
75 |
76 | private static class IndexIterable implements Iterable {
77 | private Factory> iteratorFactory;
78 |
79 | private IndexIterable(Factory> iteratorFactory) {
80 | this.iteratorFactory = iteratorFactory;
81 | }
82 |
83 | @Override
84 | public Iterator iterator() {
85 | return iteratorFactory.get();
86 | }
87 |
88 | @Override
89 | public String toString() {
90 | return iterator().toString();
91 | }
92 |
93 | interface Factory {
94 | T get();
95 | }
96 | }
97 |
98 | private static abstract class IndexIterator implements Iterator {
99 | private int index = 0;
100 |
101 | @Override
102 | public boolean hasNext() {
103 | return index < getCount();
104 | }
105 |
106 | @Override
107 | public T next() {
108 | T item = get(index);
109 | index += 1;
110 | return item;
111 | }
112 |
113 | @Override
114 | public void remove() {
115 | throw new UnsupportedOperationException();
116 | }
117 |
118 | @Override
119 | public String toString() {
120 | return "[" + Joiner.on(",").join(this) + "]";
121 | }
122 |
123 | abstract int getCount();
124 |
125 | abstract T get(int index);
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/bindingcollectionadapter-recyclerview/src/main/java/me/tatarka/bindingcollectionadapter2/collections/AsyncDiffObservableList.java:
--------------------------------------------------------------------------------
1 | package me.tatarka.bindingcollectionadapter2.collections;
2 |
3 | import java.util.AbstractList;
4 | import java.util.List;
5 | import java.util.ListIterator;
6 |
7 | import androidx.annotation.NonNull;
8 | import androidx.annotation.Nullable;
9 | import androidx.databinding.ListChangeRegistry;
10 | import androidx.databinding.ObservableList;
11 | import androidx.recyclerview.widget.AsyncDifferConfig;
12 | import androidx.recyclerview.widget.AsyncListDiffer;
13 | import androidx.recyclerview.widget.DiffUtil;
14 | import androidx.recyclerview.widget.ListUpdateCallback;
15 |
16 | public class AsyncDiffObservableList extends AbstractList implements ObservableList {
17 |
18 | private final AsyncListDiffer differ;
19 | private final ListChangeRegistry listeners = new ListChangeRegistry();
20 |
21 | /**
22 | * Creates a new DiffObservableList of type T, which runs the diff on a background thread using
23 | * {@link AsyncListDiffer}.
24 | *
25 | * @param callback The callback that controls the behavior of the DiffObservableList.
26 | */
27 | public AsyncDiffObservableList(@NonNull DiffUtil.ItemCallback callback) {
28 | this(new AsyncDifferConfig.Builder<>(callback).build());
29 | }
30 |
31 | /**
32 | * Creates a new AsyncDiffObservableList of type T, which runs the diff on a background thread using
33 | * {@link AsyncListDiffer}.
34 | *
35 | * @param config The config passed to {@code AsyncListDiffer}.
36 | */
37 | public AsyncDiffObservableList(@NonNull AsyncDifferConfig config) {
38 | differ = new AsyncListDiffer<>(new ObservableListUpdateCallback(), config);
39 | }
40 |
41 | /**
42 | * Updates the list to the given items. A diff will run in a background thread then this
43 | * collection will be updated.
44 | */
45 | public void update(@Nullable List newItems) {
46 | differ.submitList(newItems);
47 | }
48 |
49 | @Override
50 | public void addOnListChangedCallback(@NonNull OnListChangedCallback extends ObservableList> callback) {
51 | listeners.add(callback);
52 | }
53 |
54 | @Override
55 | public void removeOnListChangedCallback(@NonNull OnListChangedCallback extends ObservableList> callback) {
56 | listeners.remove(callback);
57 | }
58 |
59 | @Override
60 | public T get(int index) {
61 | return differ.getCurrentList().get(index);
62 | }
63 |
64 | @Override
65 | public int size() {
66 | return differ.getCurrentList().size();
67 | }
68 |
69 | @Override
70 | public int indexOf(Object o) {
71 | return differ.getCurrentList().indexOf(o);
72 | }
73 |
74 | @Override
75 | public int lastIndexOf(Object o) {
76 | return differ.getCurrentList().lastIndexOf(o);
77 | }
78 |
79 | @NonNull
80 | @Override
81 | public List subList(int fromIndex, int toIndex) {
82 | return differ.getCurrentList().subList(fromIndex, toIndex);
83 | }
84 |
85 | @Override
86 | public int hashCode() {
87 | return differ.getCurrentList().hashCode();
88 | }
89 |
90 | @Override
91 | public boolean equals(@Nullable Object o) {
92 | if (!(o instanceof AsyncDiffObservableList)) {
93 | return false;
94 | }
95 | return differ.getCurrentList().equals(((AsyncDiffObservableList) o).differ.getCurrentList());
96 | }
97 |
98 | @NonNull
99 | @Override
100 | public ListIterator listIterator(final int index) {
101 | return differ.getCurrentList().listIterator(index);
102 | }
103 |
104 | class ObservableListUpdateCallback implements ListUpdateCallback {
105 |
106 | @Override
107 | public void onChanged(int position, int count, Object payload) {
108 | listeners.notifyChanged(AsyncDiffObservableList.this, position, count);
109 | }
110 |
111 | @Override
112 | public void onInserted(int position, int count) {
113 | listeners.notifyInserted(AsyncDiffObservableList.this, position, count);
114 | }
115 |
116 | @Override
117 | public void onRemoved(int position, int count) {
118 | listeners.notifyRemoved(AsyncDiffObservableList.this, position, count);
119 | }
120 |
121 | @Override
122 | public void onMoved(int fromPosition, int toPosition) {
123 | listeners.notifyMoved(AsyncDiffObservableList.this, fromPosition, toPosition, 1);
124 | }
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/bindingcollectionadapter-paging/src/test/java/me/tatarka/bindingcollectionadapter2/DiffObservableListTest.java:
--------------------------------------------------------------------------------
1 | package me.tatarka.bindingcollectionadapter2;
2 |
3 | import androidx.databinding.ObservableList;
4 |
5 | import org.junit.Test;
6 | import org.junit.runner.RunWith;
7 | import org.junit.runners.JUnit4;
8 |
9 | import java.util.Arrays;
10 |
11 | import me.tatarka.bindingcollectionadapter2.collections.DiffObservableList;
12 |
13 | import static org.assertj.core.api.Assertions.assertThat;
14 | import static org.mockito.Mockito.mock;
15 | import static org.mockito.Mockito.verify;
16 |
17 | @RunWith(JUnit4.class)
18 | @SuppressWarnings("unchecked")
19 | public class DiffObservableListTest {
20 |
21 | @Test
22 | public void insetOneItem() {
23 | DiffObservableList- list = new DiffObservableList<>(Item.DIFF_CALLBACK);
24 | ObservableList.OnListChangedCallback callback = mock(ObservableList.OnListChangedCallback.class);
25 | list.addOnListChangedCallback(callback);
26 | list.update(Arrays.asList(new Item("1", "a")));
27 |
28 | assertThat(list)
29 | .hasSize(1)
30 | .containsExactly(new Item("1", "a"));
31 | verify(callback).onItemRangeInserted(list, 0, 1);
32 | }
33 |
34 | @Test
35 | public void removeOneItem() {
36 | DiffObservableList
- list = new DiffObservableList<>(Item.DIFF_CALLBACK);
37 | ObservableList.OnListChangedCallback callback = mock(ObservableList.OnListChangedCallback.class);
38 | list.addOnListChangedCallback(callback);
39 | list.update(Arrays.asList(new Item("1", "a")));
40 | list.update(Arrays.
- asList());
41 |
42 | assertThat(list).isEmpty();
43 | verify(callback).onItemRangeRemoved(list, 0, 1);
44 | }
45 |
46 | @Test
47 | public void moveOneItem() {
48 | DiffObservableList
- list = new DiffObservableList<>(Item.DIFF_CALLBACK);
49 | ObservableList.OnListChangedCallback callback = mock(ObservableList.OnListChangedCallback.class);
50 | list.addOnListChangedCallback(callback);
51 | list.update(Arrays.asList(new Item("1", "a"), new Item("2", "b")));
52 | list.update(Arrays.asList(new Item("2", "b"), new Item("1", "a")));
53 |
54 | assertThat(list)
55 | .hasSize(2)
56 | .containsExactly(new Item("2", "b"), new Item("1", "a"));
57 | verify(callback).onItemRangeMoved(list, 1, 0, 1);
58 | }
59 |
60 | @Test
61 | public void changeItem() {
62 | DiffObservableList
- list = new DiffObservableList<>(Item.DIFF_CALLBACK);
63 | ObservableList.OnListChangedCallback callback = mock(ObservableList.OnListChangedCallback.class);
64 | list.addOnListChangedCallback(callback);
65 | list.update(Arrays.asList(new Item("1", "a")));
66 | list.update(Arrays.asList(new Item("1", "b")));
67 |
68 | assertThat(list)
69 | .hasSize(1)
70 | .containsExactly(new Item("1", "b"));
71 | verify(callback).onItemRangeChanged(list, 0, 1);
72 | }
73 |
74 |
75 | static class Item {
76 |
77 | static final DiffObservableList.Callback
- DIFF_CALLBACK = new DiffObservableList.Callback
- () {
78 | @Override
79 | public boolean areItemsTheSame(Item oldItem, Item newItem) {
80 | return oldItem.id.equals(newItem.id);
81 | }
82 |
83 | @Override
84 | public boolean areContentsTheSame(Item oldItem, Item newItem) {
85 | return oldItem.value.equals(newItem.value);
86 | }
87 | };
88 |
89 | final String id;
90 | final String value;
91 |
92 | Item(String id, String value) {
93 | this.id = id;
94 | this.value = value;
95 | }
96 |
97 | @Override
98 | public boolean equals(Object o) {
99 | if (this == o) return true;
100 | if (o == null || getClass() != o.getClass()) return false;
101 | Item item = (Item) o;
102 | if (!id.equals(item.id)) return false;
103 | return value.equals(item.value);
104 | }
105 |
106 | @Override
107 | public int hashCode() {
108 | int result = id.hashCode();
109 | result = 31 * result + value.hashCode();
110 | return result;
111 | }
112 |
113 | @Override
114 | public String toString() {
115 | return "Test(" +
116 | "id='" + id + '\'' +
117 | ", value='" + value + '\'' +
118 | ')';
119 | }
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/bindingcollectionadapter-recyclerview/src/test/java/me/tatarka/bindingcollectionadapter2/DiffObservableListTest.java:
--------------------------------------------------------------------------------
1 | package me.tatarka.bindingcollectionadapter2;
2 |
3 | import androidx.databinding.ObservableList;
4 |
5 | import org.junit.Test;
6 | import org.junit.runner.RunWith;
7 | import org.junit.runners.JUnit4;
8 |
9 | import java.util.Arrays;
10 |
11 | import me.tatarka.bindingcollectionadapter2.collections.DiffObservableList;
12 |
13 | import static org.assertj.core.api.Assertions.assertThat;
14 | import static org.mockito.Mockito.mock;
15 | import static org.mockito.Mockito.verify;
16 |
17 | @RunWith(JUnit4.class)
18 | @SuppressWarnings("unchecked")
19 | public class DiffObservableListTest {
20 |
21 | @Test
22 | public void insetOneItem() {
23 | DiffObservableList
- list = new DiffObservableList<>(Item.DIFF_CALLBACK);
24 | ObservableList.OnListChangedCallback callback = mock(ObservableList.OnListChangedCallback.class);
25 | list.addOnListChangedCallback(callback);
26 | list.update(Arrays.asList(new Item("1", "a")));
27 |
28 | assertThat(list)
29 | .hasSize(1)
30 | .containsExactly(new Item("1", "a"));
31 | verify(callback).onItemRangeInserted(list, 0, 1);
32 | }
33 |
34 | @Test
35 | public void removeOneItem() {
36 | DiffObservableList
- list = new DiffObservableList<>(Item.DIFF_CALLBACK);
37 | ObservableList.OnListChangedCallback callback = mock(ObservableList.OnListChangedCallback.class);
38 | list.addOnListChangedCallback(callback);
39 | list.update(Arrays.asList(new Item("1", "a")));
40 | list.update(Arrays.
- asList());
41 |
42 | assertThat(list).isEmpty();
43 | verify(callback).onItemRangeRemoved(list, 0, 1);
44 | }
45 |
46 | @Test
47 | public void moveOneItem() {
48 | DiffObservableList
- list = new DiffObservableList<>(Item.DIFF_CALLBACK);
49 | ObservableList.OnListChangedCallback callback = mock(ObservableList.OnListChangedCallback.class);
50 | list.addOnListChangedCallback(callback);
51 | list.update(Arrays.asList(new Item("1", "a"), new Item("2", "b")));
52 | list.update(Arrays.asList(new Item("2", "b"), new Item("1", "a")));
53 |
54 | assertThat(list)
55 | .hasSize(2)
56 | .containsExactly(new Item("2", "b"), new Item("1", "a"));
57 | verify(callback).onItemRangeMoved(list, 1, 0, 1);
58 | }
59 |
60 | @Test
61 | public void changeItem() {
62 | DiffObservableList
- list = new DiffObservableList<>(Item.DIFF_CALLBACK);
63 | ObservableList.OnListChangedCallback callback = mock(ObservableList.OnListChangedCallback.class);
64 | list.addOnListChangedCallback(callback);
65 | list.update(Arrays.asList(new Item("1", "a")));
66 | list.update(Arrays.asList(new Item("1", "b")));
67 |
68 | assertThat(list)
69 | .hasSize(1)
70 | .containsExactly(new Item("1", "b"));
71 | verify(callback).onItemRangeChanged(list, 0, 1);
72 | }
73 |
74 |
75 | static class Item {
76 |
77 | static final DiffObservableList.Callback
- DIFF_CALLBACK = new DiffObservableList.Callback
- () {
78 | @Override
79 | public boolean areItemsTheSame(Item oldItem, Item newItem) {
80 | return oldItem.id.equals(newItem.id);
81 | }
82 |
83 | @Override
84 | public boolean areContentsTheSame(Item oldItem, Item newItem) {
85 | return oldItem.value.equals(newItem.value);
86 | }
87 | };
88 |
89 | final String id;
90 | final String value;
91 |
92 | Item(String id, String value) {
93 | this.id = id;
94 | this.value = value;
95 | }
96 |
97 | @Override
98 | public boolean equals(Object o) {
99 | if (this == o) return true;
100 | if (o == null || getClass() != o.getClass()) return false;
101 | Item item = (Item) o;
102 | if (!id.equals(item.id)) return false;
103 | return value.equals(item.value);
104 | }
105 |
106 | @Override
107 | public int hashCode() {
108 | int result = id.hashCode();
109 | result = 31 * result + value.hashCode();
110 | return result;
111 | }
112 |
113 | @Override
114 | public String toString() {
115 | return "Test(" +
116 | "id='" + id + '\'' +
117 | ", value='" + value + '\'' +
118 | ')';
119 | }
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/app/src/main/java/me/tatarka/bindingcollectionadapter/sample/ImmutableViewModel.kt:
--------------------------------------------------------------------------------
1 | package me.tatarka.bindingcollectionadapter.sample
2 |
3 | import androidx.lifecycle.LiveData
4 | import androidx.lifecycle.MutableLiveData
5 | import androidx.lifecycle.Transformations
6 | import androidx.lifecycle.ViewModel
7 | import androidx.paging.DataSource
8 | import androidx.paging.LivePagedListBuilder
9 | import androidx.paging.PagedList
10 | import androidx.paging.PositionalDataSource
11 | import androidx.recyclerview.widget.DiffUtil
12 | import me.tatarka.bindingcollectionadapter2.itemBindingOf
13 | import me.tatarka.bindingcollectionadapter2.itembindings.OnItemBindClass
14 | import me.tatarka.bindingcollectionadapter2.map
15 | import me.tatarka.bindingcollectionadapter2.toItemBinding
16 | import java.util.*
17 |
18 | class ImmutableViewModel : ViewModel(), ImmutableListeners {
19 |
20 | private val mutList = MutableLiveData
>().apply {
21 | value = (0 until 3).map { i -> ImmutableItem(index = i, checked = false) }
22 | }
23 | private val headerFooterList =
24 | Transformations.map, List>(mutList) { input ->
25 | val list = ArrayList(input.size + 2)
26 | list.add("Header")
27 | list.addAll(input)
28 | list.add("Footer")
29 | list
30 | }
31 | val list: LiveData> = headerFooterList
32 |
33 | val pagedList: LiveData> =
34 | LivePagedListBuilder(object : DataSource.Factory() {
35 | override fun create(): DataSource =
36 | object : PositionalDataSource() {
37 |
38 | override fun loadInitial(
39 | params: LoadInitialParams,
40 | callback: LoadInitialCallback
41 | ) {
42 | val list =
43 | (0 until params.requestedLoadSize).map {
44 | ImmutableItem(
45 | index = it + params.requestedStartPosition,
46 | checked = false
47 | )
48 | }
49 | // Pretend we are slow
50 | Thread.sleep(1000)
51 | callback.onResult(list, params.requestedStartPosition, 200)
52 | }
53 |
54 | override fun loadRange(
55 | params: LoadRangeParams,
56 | callback: LoadRangeCallback
57 | ) {
58 | val list =
59 | (0 until params.loadSize).map {
60 | ImmutableItem(
61 | index = it + params.startPosition,
62 | checked = false
63 | )
64 | }
65 | // Pretend we are slow
66 | Thread.sleep(1000)
67 | callback.onResult(list)
68 | }
69 | }
70 | }, 20).build()
71 |
72 | val items = itemBindingOf(BR.item, R.layout.item_immutable)
73 |
74 | val multipleItems = OnItemBindClass().apply {
75 | map(BR.item, R.layout.item_header_footer)
76 | map(BR.item, R.layout.item_immutable)
77 | }.toItemBinding().bindExtra(BR.listeners, this)
78 |
79 | val diff: DiffUtil.ItemCallback = object : DiffUtil.ItemCallback() {
80 | override fun areItemsTheSame(oldItem: Any, newItem: Any): Boolean {
81 | return if (oldItem is ImmutableItem && newItem is ImmutableItem) {
82 | oldItem.index == newItem.index
83 | } else oldItem == newItem
84 | }
85 |
86 | override fun areContentsTheSame(oldItem: Any, newItem: Any): Boolean {
87 | return oldItem == newItem
88 | }
89 | }
90 |
91 | override fun onToggleChecked(index: Int): Boolean {
92 | mutList.value = mutList.value!!.replaceAt(index) { item ->
93 | item.copy(checked = !item.checked)
94 | }
95 | return true
96 | }
97 |
98 | override fun onAddItem() {
99 | mutList.value = mutList.value!!.let { list ->
100 | list + ImmutableItem(index = list.size, checked = false)
101 | }
102 | }
103 |
104 | override fun onRemoveItem() {
105 | mutList.value!!.let { list ->
106 | if (list.size > 1) {
107 | mutList.value = list.dropLast(1)
108 | }
109 | }
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/bindingcollectionadapter-paging/src/main/java/me/tatarka/bindingcollectionadapter2/collections/AsyncDiffPagedObservableList.java:
--------------------------------------------------------------------------------
1 | package me.tatarka.bindingcollectionadapter2.collections;
2 |
3 | import java.util.AbstractList;
4 | import java.util.List;
5 | import java.util.ListIterator;
6 |
7 | import androidx.annotation.NonNull;
8 | import androidx.annotation.Nullable;
9 | import androidx.databinding.ListChangeRegistry;
10 | import androidx.databinding.ObservableList;
11 | import androidx.paging.AsyncPagedListDiffer;
12 | import androidx.paging.PagedList;
13 | import androidx.recyclerview.widget.AsyncDifferConfig;
14 | import androidx.recyclerview.widget.AsyncListDiffer;
15 | import androidx.recyclerview.widget.DiffUtil;
16 | import androidx.recyclerview.widget.ListUpdateCallback;
17 |
18 | public class AsyncDiffPagedObservableList extends AbstractList implements ObservableList {
19 |
20 | private final AsyncPagedListDiffer differ;
21 | private final ListChangeRegistry listeners = new ListChangeRegistry();
22 |
23 | /**
24 | * Creates a new DiffObservableList of type T, which runs the diff on a background thread using
25 | * {@link AsyncListDiffer}.
26 | *
27 | * @param callback The callback that controls the behavior of the DiffObservableList.
28 | */
29 | public AsyncDiffPagedObservableList(@NonNull DiffUtil.ItemCallback callback) {
30 | this(new AsyncDifferConfig.Builder<>(callback).build());
31 | }
32 |
33 | /**
34 | * Creates a new AsyncDiffObservableList of type T, which runs the diff on a background thread using
35 | * {@link AsyncListDiffer}.
36 | *
37 | * @param config The config passed to {@code AsyncListDiffer}.
38 | */
39 | public AsyncDiffPagedObservableList(@NonNull AsyncDifferConfig config) {
40 | differ = new AsyncPagedListDiffer<>(new ObservableListUpdateCallback(), config);
41 | }
42 |
43 | /**
44 | * Updates the list to the given items. A diff will run in a background thread then this
45 | * collection will be updated.
46 | */
47 | public void update(@Nullable PagedList newItems) {
48 | differ.submitList(newItems);
49 | }
50 |
51 | @Override
52 | public void addOnListChangedCallback(@NonNull OnListChangedCallback extends ObservableList> callback) {
53 | listeners.add(callback);
54 | }
55 |
56 | @Override
57 | public void removeOnListChangedCallback(@NonNull OnListChangedCallback extends ObservableList> callback) {
58 | listeners.remove(callback);
59 | }
60 |
61 | @Override
62 | public T get(int index) {
63 | return differ.getItem(index);
64 | }
65 |
66 | @Override
67 | public int size() {
68 | return differ.getItemCount();
69 | }
70 |
71 | @Override
72 | public int indexOf(Object o) {
73 | return differ.getCurrentList().indexOf(o);
74 | }
75 |
76 | @Override
77 | public int lastIndexOf(Object o) {
78 | return differ.getCurrentList().lastIndexOf(o);
79 | }
80 |
81 | @NonNull
82 | @Override
83 | public List subList(int fromIndex, int toIndex) {
84 | return differ.getCurrentList().subList(fromIndex, toIndex);
85 | }
86 |
87 | @Override
88 | public int hashCode() {
89 | return differ.getCurrentList().hashCode();
90 | }
91 |
92 | @Override
93 | public boolean equals(@Nullable Object o) {
94 | if (!(o instanceof AsyncDiffPagedObservableList)) {
95 | return false;
96 | }
97 | return differ.getCurrentList().equals(((AsyncDiffPagedObservableList) o).differ.getCurrentList());
98 | }
99 |
100 | @NonNull
101 | @Override
102 | public ListIterator listIterator(final int index) {
103 | return differ.getCurrentList().listIterator(index);
104 | }
105 |
106 | class ObservableListUpdateCallback implements ListUpdateCallback {
107 |
108 | @Override
109 | public void onChanged(int position, int count, Object payload) {
110 | listeners.notifyChanged(AsyncDiffPagedObservableList.this, position, count);
111 | }
112 |
113 | @Override
114 | public void onInserted(int position, int count) {
115 | listeners.notifyInserted(AsyncDiffPagedObservableList.this, position, count);
116 | }
117 |
118 | @Override
119 | public void onRemoved(int position, int count) {
120 | listeners.notifyRemoved(AsyncDiffPagedObservableList.this, position, count);
121 | }
122 |
123 | @Override
124 | public void onMoved(int fromPosition, int toPosition) {
125 | listeners.notifyMoved(AsyncDiffPagedObservableList.this, fromPosition, toPosition, 1);
126 | }
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/bindingcollectionadapter-recyclerview/src/androidTest/java/me/tatarka/bindingcollectionadapter2/recyclerview/DynamicItemViewTest.java:
--------------------------------------------------------------------------------
1 | package me.tatarka.bindingcollectionadapter2.recyclerview;
2 |
3 | import android.view.LayoutInflater;
4 |
5 | import org.junit.Before;
6 | import org.junit.Rule;
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import java.util.Arrays;
11 | import java.util.List;
12 |
13 | import androidx.annotation.NonNull;
14 | import androidx.databinding.DataBindingUtil;
15 | import androidx.databinding.ObservableArrayList;
16 | import androidx.databinding.ObservableList;
17 | import androidx.databinding.ViewDataBinding;
18 | import androidx.recyclerview.widget.RecyclerView;
19 | import androidx.test.annotation.UiThreadTest;
20 | import androidx.test.rule.ActivityTestRule;
21 | import androidx.test.runner.AndroidJUnit4;
22 | import me.tatarka.bindingcollectionadapter2.BR;
23 | import me.tatarka.bindingcollectionadapter2.BindingCollectionAdapter;
24 | import me.tatarka.bindingcollectionadapter2.ItemBinding;
25 | import me.tatarka.bindingcollectionadapter2.OnItemBind;
26 | import me.tatarka.bindingcollectionadapter2.recyclerview.test.R;
27 |
28 | import static me.tatarka.bindingcollectionadapter2.recyclerview.test.R.layout.item;
29 | import static org.assertj.core.api.Assertions.assertThat;
30 |
31 | @RunWith(AndroidJUnit4.class)
32 | public class DynamicItemViewTest {
33 |
34 | @Rule
35 | public ActivityTestRule activityTestRule = new ActivityTestRule<>(EmptyActivity.class);
36 |
37 | private LayoutInflater inflater;
38 |
39 | @Before
40 | public void setup() throws Exception {
41 | inflater = LayoutInflater.from(activityTestRule.getActivity());
42 | }
43 |
44 | @Test
45 | @UiThreadTest
46 | public void dynamicItemViewInRecyclerView() {
47 | List items = Arrays.asList("one", "two", "three");
48 | TestHelpers.ViewModel viewModel = new TestHelpers.ViewModel(items, ItemBinding.of(BR.item, item));
49 |
50 | ViewDataBinding binding = DataBindingUtil.inflate(inflater, R.layout.recycler_view, null, false);
51 | binding.setVariable(BR.viewModel, viewModel);
52 | binding.executePendingBindings();
53 |
54 | TestHelpers.ViewModel newViewModel = new TestHelpers.ViewModel(items, ItemBinding.of(BR.item, R.layout.item2));
55 | binding.setVariable(BR.viewModel, newViewModel);
56 | binding.executePendingBindings();
57 |
58 | RecyclerView recyclerView = (RecyclerView) binding.getRoot();
59 | @SuppressWarnings("unchecked")
60 | BindingCollectionAdapter adapter = (BindingCollectionAdapter) recyclerView.getAdapter();
61 |
62 | assertThat(adapter.getItemBinding().layoutRes()).isEqualTo(R.layout.item2);
63 | }
64 |
65 | @Test
66 | @UiThreadTest
67 | public void adapterDoesntChangeForSameItemBindingInRecylerView() {
68 | ObservableList items = new ObservableArrayList<>();
69 | items.addAll(Arrays.asList("one", "two", "three"));
70 | TestHelpers.ViewModel viewModel = new TestHelpers.ViewModel(items, ItemBinding.of(BR.item, item));
71 |
72 | ViewDataBinding binding = DataBindingUtil.inflate(inflater, R.layout.recycler_view, null, false);
73 | binding.setVariable(BR.viewModel, viewModel);
74 | binding.executePendingBindings();
75 |
76 | RecyclerView recyclerView1 = (RecyclerView) binding.getRoot();
77 | BindingCollectionAdapter adapter1 = (BindingCollectionAdapter) recyclerView1.getAdapter();
78 |
79 | items.add("four");
80 |
81 | RecyclerView recyclerView2 = (RecyclerView) binding.getRoot();
82 | @SuppressWarnings("unchecked")
83 | BindingCollectionAdapter adapter2 = (BindingCollectionAdapter) recyclerView2.getAdapter();
84 |
85 | assertThat(adapter1).isSameAs(adapter2);
86 | }
87 |
88 | @Test
89 | @UiThreadTest
90 | public void adapterDoesntChangeForSameItemBindingSelectorInRecyclerView() {
91 | ObservableList items = new ObservableArrayList<>();
92 | items.addAll(Arrays.asList("one", "two", "three"));
93 | ItemBinding itemBinding = ItemBinding.of(new OnItemBind() {
94 | @Override
95 | public void onItemBind(@NonNull ItemBinding itemBinding, int position, String item) {
96 | itemBinding.set(BR.item, position);
97 | }
98 | });
99 | TestHelpers.ViewModel viewModel = new TestHelpers.ViewModel(items, itemBinding);
100 |
101 | ViewDataBinding binding = DataBindingUtil.inflate(inflater, R.layout.recycler_view, null, false);
102 | binding.setVariable(BR.viewModel, viewModel);
103 | binding.executePendingBindings();
104 |
105 | RecyclerView recyclerView1 = (RecyclerView) binding.getRoot();
106 | BindingCollectionAdapter adapter1 = (BindingCollectionAdapter) recyclerView1.getAdapter();
107 |
108 | items.add("four");
109 |
110 | RecyclerView recyclerView2 = (RecyclerView) binding.getRoot();
111 | @SuppressWarnings("unchecked")
112 | BindingCollectionAdapter adapter2 = (BindingCollectionAdapter) recyclerView2.getAdapter();
113 |
114 | assertThat(adapter1).isSameAs(adapter2);
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/bindingcollectionadapter/src/androidTest/java/me/tatarka/bindingcollectionadapter2/DynamicItemViewTest.java:
--------------------------------------------------------------------------------
1 | package me.tatarka.bindingcollectionadapter2;
2 |
3 | import androidx.annotation.NonNull;
4 | import androidx.databinding.DataBindingUtil;
5 | import androidx.databinding.ObservableArrayList;
6 | import androidx.databinding.ObservableList;
7 | import androidx.databinding.ViewDataBinding;
8 | import androidx.test.annotation.UiThreadTest;
9 | import androidx.test.rule.ActivityTestRule;
10 | import androidx.test.runner.AndroidJUnit4;
11 | import androidx.viewpager.widget.ViewPager;
12 | import android.view.LayoutInflater;
13 | import android.widget.ListView;
14 |
15 | import org.junit.Before;
16 | import org.junit.Rule;
17 | import org.junit.Test;
18 | import org.junit.runner.RunWith;
19 |
20 | import java.util.Arrays;
21 | import java.util.List;
22 |
23 | import me.tatarka.bindingcollectionadapter2.test.R;
24 |
25 | import static me.tatarka.bindingcollectionadapter2.test.R.layout.item;
26 | import static org.assertj.core.api.Assertions.assertThat;
27 |
28 | @RunWith(AndroidJUnit4.class)
29 | public class DynamicItemViewTest {
30 |
31 | @Rule
32 | public ActivityTestRule activityTestRule = new ActivityTestRule<>(EmptyActivity.class);
33 |
34 | private LayoutInflater inflater;
35 |
36 | @Before
37 | public void setup() throws Exception {
38 | inflater = LayoutInflater.from(activityTestRule.getActivity());
39 | }
40 |
41 | @Test
42 | @UiThreadTest
43 | public void dynamicItemBindingInListView() {
44 | List items = Arrays.asList("one", "two", "three");
45 | TestHelpers.ViewModel viewModel = new TestHelpers.ViewModel.Builder(items, ItemBinding.of(BR.item, item)).build();
46 |
47 | ViewDataBinding binding = DataBindingUtil.inflate(inflater, R.layout.list_view, null, false);
48 | binding.setVariable(BR.viewModel, viewModel);
49 | binding.executePendingBindings();
50 |
51 | TestHelpers.ViewModel newViewModel = new TestHelpers.ViewModel.Builder(items, ItemBinding.of(BR.item, R.layout.item2)).build();
52 | binding.setVariable(BR.viewModel, newViewModel);
53 | binding.executePendingBindings();
54 |
55 | ListView listView = (ListView) binding.getRoot();
56 | @SuppressWarnings("unchecked")
57 | BindingCollectionAdapter adapter = (BindingCollectionAdapter) listView.getAdapter();
58 |
59 | assertThat(adapter.getItemBinding().layoutRes()).isEqualTo(R.layout.item2);
60 | }
61 |
62 | @Test
63 | @UiThreadTest
64 | public void adapterDoesntChangeForSameItemBindingInListView() {
65 | ObservableList items = new ObservableArrayList<>();
66 | items.addAll(Arrays.asList("one", "two", "three"));
67 | TestHelpers.ViewModel viewModel = new TestHelpers.ViewModel.Builder(items, ItemBinding.of(BR.item, item)).build();
68 |
69 | ViewDataBinding binding = DataBindingUtil.inflate(inflater, R.layout.list_view, null, false);
70 | binding.setVariable(BR.viewModel, viewModel);
71 | binding.executePendingBindings();
72 |
73 | ListView listView1 = (ListView) binding.getRoot();
74 | BindingCollectionAdapter adapter1 = (BindingCollectionAdapter) listView1.getAdapter();
75 |
76 | items.add("four");
77 |
78 | ListView listView2 = (ListView) binding.getRoot();
79 | @SuppressWarnings("unchecked")
80 | BindingCollectionAdapter adapter2 = (BindingCollectionAdapter) listView2.getAdapter();
81 |
82 | assertThat(adapter1).isSameAs(adapter2);
83 | }
84 |
85 | @Test
86 | @UiThreadTest
87 | public void adapterDoesntChangeForSameItemBindingSelectorInListView() {
88 | ObservableList items = new ObservableArrayList<>();
89 | items.addAll(Arrays.asList("one", "two", "three"));
90 | ItemBinding itemBinding = ItemBinding.of(new OnItemBind() {
91 | @Override
92 | public void onItemBind(@NonNull ItemBinding itemBinding, int position, String item) {
93 | itemBinding.set(me.tatarka.bindingcollectionadapter2.BR.item, position);
94 | }
95 | });
96 | TestHelpers.ViewModel viewModel = new TestHelpers.ViewModel.Builder(items, itemBinding).build();
97 |
98 | ViewDataBinding binding = DataBindingUtil.inflate(inflater, R.layout.list_view, null, false);
99 | binding.setVariable(me.tatarka.bindingcollectionadapter2.BR.viewModel, viewModel);
100 | binding.executePendingBindings();
101 |
102 | ListView listView1 = (ListView) binding.getRoot();
103 | BindingCollectionAdapter adapter1 = (BindingCollectionAdapter) listView1.getAdapter();
104 |
105 | items.add("four");
106 |
107 | ListView listView2 = (ListView) binding.getRoot();
108 | @SuppressWarnings("unchecked")
109 | BindingCollectionAdapter adapter2 = (BindingCollectionAdapter) listView2.getAdapter();
110 |
111 | assertThat(adapter1).isSameAs(adapter2);
112 | }
113 |
114 | @Test
115 | @UiThreadTest
116 | public void dynamicItemBindingInViewPager() {
117 | List items = Arrays.asList("one", "two", "three");
118 | TestHelpers.ViewModel viewModel = new TestHelpers.ViewModel.Builder(items, ItemBinding.of(me.tatarka.bindingcollectionadapter2.BR.item, item)).build();
119 |
120 | ViewDataBinding binding = DataBindingUtil.inflate(inflater, R.layout.view_pager_adapter, null, false);
121 | binding.setVariable(me.tatarka.bindingcollectionadapter2.BR.viewModel, viewModel);
122 | binding.executePendingBindings();
123 |
124 | TestHelpers.ViewModel newViewModel = new TestHelpers.ViewModel.Builder(items, ItemBinding.of(me.tatarka.bindingcollectionadapter2.BR.item, R.layout.item2)).build();
125 | binding.setVariable(me.tatarka.bindingcollectionadapter2.BR.viewModel, newViewModel);
126 | binding.executePendingBindings();
127 |
128 | ViewPager viewPager = (ViewPager) binding.getRoot();
129 | @SuppressWarnings("unchecked")
130 | BindingCollectionAdapter adapter = (BindingCollectionAdapter) viewPager.getAdapter();
131 |
132 | assertThat(adapter.getItemBinding().layoutRes()).isEqualTo(R.layout.item2);
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 | # Determine the Java command to use to start the JVM.
86 | if [ -n "$JAVA_HOME" ] ; then
87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
88 | # IBM's JDK on AIX uses strange locations for the executables
89 | JAVACMD="$JAVA_HOME/jre/sh/java"
90 | else
91 | JAVACMD="$JAVA_HOME/bin/java"
92 | fi
93 | if [ ! -x "$JAVACMD" ] ; then
94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
95 |
96 | Please set the JAVA_HOME variable in your environment to match the
97 | location of your Java installation."
98 | fi
99 | else
100 | JAVACMD="java"
101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
102 |
103 | Please set the JAVA_HOME variable in your environment to match the
104 | location of your Java installation."
105 | fi
106 |
107 | # Increase the maximum file descriptors if we can.
108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
109 | MAX_FD_LIMIT=`ulimit -H -n`
110 | if [ $? -eq 0 ] ; then
111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
112 | MAX_FD="$MAX_FD_LIMIT"
113 | fi
114 | ulimit -n $MAX_FD
115 | if [ $? -ne 0 ] ; then
116 | warn "Could not set maximum file descriptor limit: $MAX_FD"
117 | fi
118 | else
119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
120 | fi
121 | fi
122 |
123 | # For Darwin, add options to specify how the application appears in the dock
124 | if $darwin; then
125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
126 | fi
127 |
128 | # For Cygwin, switch paths to Windows format before running java
129 | if $cygwin ; then
130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
132 | JAVACMD=`cygpath --unix "$JAVACMD"`
133 |
134 | # We build the pattern for arguments to be converted via cygpath
135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
136 | SEP=""
137 | for dir in $ROOTDIRSRAW ; do
138 | ROOTDIRS="$ROOTDIRS$SEP$dir"
139 | SEP="|"
140 | done
141 | OURCYGPATTERN="(^($ROOTDIRS))"
142 | # Add a user-defined pattern to the cygpath arguments
143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
145 | fi
146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
147 | i=0
148 | for arg in "$@" ; do
149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
151 |
152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
154 | else
155 | eval `echo args$i`="\"$arg\""
156 | fi
157 | i=$((i+1))
158 | done
159 | case $i in
160 | (0) set -- ;;
161 | (1) set -- "$args0" ;;
162 | (2) set -- "$args0" "$args1" ;;
163 | (3) set -- "$args0" "$args1" "$args2" ;;
164 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
165 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
166 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
167 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
168 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
169 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
170 | esac
171 | fi
172 |
173 | # Escape application args
174 | save () {
175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
176 | echo " "
177 | }
178 | APP_ARGS=$(save "$@")
179 |
180 | # Collect all arguments for the java command, following the shell quoting and substitution rules
181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
182 |
183 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
184 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
185 | cd "$(dirname "$0")"
186 | fi
187 |
188 | exec "$JAVACMD" "$@"
189 |
--------------------------------------------------------------------------------
/bindingcollectionadapter/src/main/java/me/tatarka/bindingcollectionadapter2/ItemBinding.java:
--------------------------------------------------------------------------------
1 | package me.tatarka.bindingcollectionadapter2;
2 |
3 | import android.util.SparseArray;
4 |
5 | import androidx.annotation.LayoutRes;
6 | import androidx.annotation.NonNull;
7 | import androidx.annotation.Nullable;
8 | import androidx.databinding.ViewDataBinding;
9 |
10 | /**
11 | * Provides the necessary information to bind an item in a collection to a view. This includes the
12 | * variable id and the layout as well as any extra bindings you may want to provide.
13 | *
14 | * @param