summaryrefslogtreecommitdiffhomepage
path: root/app/src
diff options
context:
space:
mode:
authorSamuel Holland <samuel@sholland.org>2017-08-22 21:49:55 -0500
committerSamuel Holland <samuel@sholland.org>2017-08-22 23:10:35 -0500
commit353028420b92774d9a6ccdfe9318481de00b17e7 (patch)
treef29a4a2b8a89053b41759f0496da7dca60c92b84 /app/src
parentfb919a72262b43ffa3d93239afa6fb1621341f28 (diff)
Add an adapter for binding an ObservableList to a LinearLayout
EditTexts do not work in ListViews. Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
Diffstat (limited to 'app/src')
-rw-r--r--app/src/main/java/com/wireguard/android/BindingAdapters.java45
-rw-r--r--app/src/main/java/com/wireguard/android/ItemChangeListener.java126
-rw-r--r--app/src/main/java/com/wireguard/android/ObservableListAdapter.java4
-rw-r--r--app/src/main/java/com/wireguard/android/ObservableMapAdapter.java4
-rw-r--r--app/src/main/res/values/ids.xml4
5 files changed, 169 insertions, 14 deletions
diff --git a/app/src/main/java/com/wireguard/android/BindingAdapters.java b/app/src/main/java/com/wireguard/android/BindingAdapters.java
index ec465930..d5c07ed6 100644
--- a/app/src/main/java/com/wireguard/android/BindingAdapters.java
+++ b/app/src/main/java/com/wireguard/android/BindingAdapters.java
@@ -1,10 +1,11 @@
package com.wireguard.android;
import android.databinding.BindingAdapter;
-import android.databinding.ObservableArrayMap;
import android.databinding.ObservableList;
+import android.databinding.adapters.ListenerUtil;
import android.graphics.Typeface;
import android.text.InputFilter;
+import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
@@ -15,11 +16,34 @@ import android.widget.TextView;
@SuppressWarnings("unused")
public final class BindingAdapters {
@BindingAdapter({"items", "layout"})
- public static <T> void listBinding(final ListView view,
- final ObservableList<T> oldList, final int oldLayoutId,
- final ObservableList<T> newList, final int newLayoutId) {
- // Remove any existing binding when there is no new list.
- if (newList == null) {
+ public static <T> void setItems(final LinearLayout view,
+ final ObservableList<T> oldList, final int oldLayoutId,
+ final ObservableList<T> newList, final int newLayoutId) {
+ if (oldList == newList && oldLayoutId == newLayoutId)
+ return;
+ ItemChangeListener<T> listener = ListenerUtil.getListener(view, R.id.item_change_listener);
+ // If the layout changes, any existing listener must be replaced.
+ if (listener != null && oldList != null && oldLayoutId != newLayoutId) {
+ listener.setList(null);
+ listener = null;
+ }
+ // Avoid adding a listener when there is no new list or layout.
+ if (newList == null || newLayoutId == 0)
+ return;
+ if (listener == null) {
+ listener = new ItemChangeListener<>(view, newLayoutId);
+ ListenerUtil.trackListener(view, listener, R.id.item_change_listener);
+ }
+ // Either the list changed, or this is an entirely new listener because the layout changed.
+ listener.setList(newList);
+ }
+
+ @BindingAdapter({"items", "layout"})
+ public static <T> void setItems(final ListView view,
+ final ObservableList<T> oldList, final int oldLayoutId,
+ final ObservableList<T> newList, final int newLayoutId) {
+ // Remove any existing binding when there is no new list or layout.
+ if (newList == null || newLayoutId == 0) {
view.setAdapter(null);
return;
}
@@ -39,11 +63,12 @@ public final class BindingAdapters {
}
@BindingAdapter({"items", "layout"})
- public static <K extends Comparable<K>, V> void sortedMapBinding(
- final ListView view, final ObservableSortedMap<K, V> oldMap, final int oldLayoutId,
+ public static <K extends Comparable<K>, V> void setItems(
+ final ListView view,
+ final ObservableSortedMap<K, V> oldMap, final int oldLayoutId,
final ObservableSortedMap<K, V> newMap, final int newLayoutId) {
- // Remove any existing binding when there is no new map.
- if (newMap == null) {
+ // Remove any existing binding when there is no new map or layout.
+ if (newMap == null || newLayoutId == 0) {
view.setAdapter(null);
return;
}
diff --git a/app/src/main/java/com/wireguard/android/ItemChangeListener.java b/app/src/main/java/com/wireguard/android/ItemChangeListener.java
new file mode 100644
index 00000000..e3f34019
--- /dev/null
+++ b/app/src/main/java/com/wireguard/android/ItemChangeListener.java
@@ -0,0 +1,126 @@
+package com.wireguard.android;
+
+import android.databinding.DataBindingUtil;
+import android.databinding.ObservableList;
+import android.databinding.ViewDataBinding;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Helper class for binding an ObservableList to the children of a ViewGroup.
+ */
+
+class ItemChangeListener<T> {
+ private final OnListChangedCallback<T> callback = new OnListChangedCallback<>(this);
+ private final ViewGroup container;
+ private final int layoutId;
+ private final LayoutInflater layoutInflater;
+ private ObservableList<T> list;
+
+ ItemChangeListener(final ViewGroup container, final int layoutId) {
+ this.container = container;
+ this.layoutId = layoutId;
+ layoutInflater = LayoutInflater.from(container.getContext());
+ }
+
+ private View getView(final int position, final View convertView) {
+ ViewDataBinding binding = DataBindingUtil.getBinding(convertView);
+ if (binding == null)
+ binding = DataBindingUtil.inflate(layoutInflater, layoutId, container, false);
+ binding.setVariable(BR.item, list.get(position));
+ binding.executePendingBindings();
+ return binding.getRoot();
+ }
+
+ public void setList(final ObservableList<T> newList) {
+ if (list != null)
+ list.removeOnListChangedCallback(callback);
+ list = newList;
+ if (list != null) {
+ list.addOnListChangedCallback(callback);
+ callback.onChanged(list);
+ } else {
+ container.removeAllViews();
+ }
+ }
+
+ private static class OnListChangedCallback<T>
+ extends ObservableList.OnListChangedCallback<ObservableList<T>> {
+
+ private final WeakReference<ItemChangeListener<T>> weakListener;
+
+ private OnListChangedCallback(final ItemChangeListener<T> listener) {
+ weakListener = new WeakReference<>(listener);
+ }
+
+ @Override
+ public void onChanged(final ObservableList<T> sender) {
+ final ItemChangeListener<T> listener = weakListener.get();
+ if (listener != null) {
+ // TODO: recycle views
+ listener.container.removeAllViews();
+ for (int i = 0; i < sender.size(); ++i)
+ listener.container.addView(listener.getView(i, null));
+ } else {
+ sender.removeOnListChangedCallback(this);
+ }
+ }
+
+ @Override
+ public void onItemRangeChanged(final ObservableList<T> sender, final int positionStart,
+ final int itemCount) {
+ final ItemChangeListener<T> listener = weakListener.get();
+ if (listener != null) {
+ for (int i = positionStart; i < positionStart + itemCount; ++i) {
+ final View child = listener.container.getChildAt(i);
+ listener.container.removeViewAt(i);
+ listener.container.addView(listener.getView(i, child));
+ }
+ } else {
+ sender.removeOnListChangedCallback(this);
+ }
+ }
+
+ @Override
+ public void onItemRangeInserted(final ObservableList<T> sender, final int positionStart,
+ final int itemCount) {
+ final ItemChangeListener<T> listener = weakListener.get();
+ if (listener != null) {
+ for (int i = positionStart; i < positionStart + itemCount; ++i)
+ listener.container.addView(listener.getView(i, null));
+ } else {
+ sender.removeOnListChangedCallback(this);
+ }
+ }
+
+ @Override
+ public void onItemRangeMoved(final ObservableList<T> sender, final int fromPosition,
+ final int toPosition, final int itemCount) {
+ final ItemChangeListener<T> listener = weakListener.get();
+ if (listener != null) {
+ final View[] views = new View[itemCount];
+ for (int i = 0; i < itemCount; ++i)
+ views[i] = listener.container.getChildAt(fromPosition + i);
+ listener.container.removeViews(fromPosition, itemCount);
+ for (int i = 0; i < itemCount; ++i)
+ listener.container.addView(views[i], toPosition + i);
+ } else {
+ sender.removeOnListChangedCallback(this);
+ }
+ }
+
+ @Override
+ public void onItemRangeRemoved(final ObservableList<T> sender, final int positionStart,
+ final int itemCount) {
+ final ItemChangeListener<T> listener = weakListener.get();
+ if (listener != null) {
+ listener.container.removeViews(positionStart, itemCount);
+ } else {
+ sender.removeOnListChangedCallback(this);
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/wireguard/android/ObservableListAdapter.java b/app/src/main/java/com/wireguard/android/ObservableListAdapter.java
index c69af4b5..8e240b71 100644
--- a/app/src/main/java/com/wireguard/android/ObservableListAdapter.java
+++ b/app/src/main/java/com/wireguard/android/ObservableListAdapter.java
@@ -17,14 +17,14 @@ import java.lang.ref.WeakReference;
*/
class ObservableListAdapter<T> extends BaseAdapter implements ListAdapter {
+ private final OnListChangedCallback<T> callback = new OnListChangedCallback<>(this);
private final int layoutId;
private final LayoutInflater layoutInflater;
private ObservableList<T> list;
- private final OnListChangedCallback<T> callback = new OnListChangedCallback<>(this);
ObservableListAdapter(final Context context, final int layoutId, final ObservableList<T> list) {
- layoutInflater = LayoutInflater.from(context);
this.layoutId = layoutId;
+ layoutInflater = LayoutInflater.from(context);
setList(list);
}
diff --git a/app/src/main/java/com/wireguard/android/ObservableMapAdapter.java b/app/src/main/java/com/wireguard/android/ObservableMapAdapter.java
index 3090ed5e..308c818d 100644
--- a/app/src/main/java/com/wireguard/android/ObservableMapAdapter.java
+++ b/app/src/main/java/com/wireguard/android/ObservableMapAdapter.java
@@ -19,16 +19,16 @@ import java.util.Collections;
*/
class ObservableMapAdapter<K extends Comparable<K>, V> extends BaseAdapter implements ListAdapter {
+ private final OnMapChangedCallback<K, V> callback = new OnMapChangedCallback<>(this);
private ArrayList<K> keys;
private final int layoutId;
private final LayoutInflater layoutInflater;
private ObservableSortedMap<K, V> map;
- private final OnMapChangedCallback<K, V> callback = new OnMapChangedCallback<>(this);
ObservableMapAdapter(final Context context, final int layoutId,
final ObservableSortedMap<K, V> map) {
- layoutInflater = LayoutInflater.from(context);
this.layoutId = layoutId;
+ layoutInflater = LayoutInflater.from(context);
setMap(map);
}
diff --git a/app/src/main/res/values/ids.xml b/app/src/main/res/values/ids.xml
new file mode 100644
index 00000000..7f34f808
--- /dev/null
+++ b/app/src/main/res/values/ids.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <item name="item_change_listener" type="id" />
+</resources>