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>
This commit is contained in:
parent
fb919a7226
commit
353028420b
@ -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;
|
||||
}
|
||||
|
126
app/src/main/java/com/wireguard/android/ItemChangeListener.java
Normal file
126
app/src/main/java/com/wireguard/android/ItemChangeListener.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
4
app/src/main/res/values/ids.xml
Normal file
4
app/src/main/res/values/ids.xml
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<item name="item_change_listener" type="id" />
|
||||
</resources>
|
Loading…
Reference in New Issue
Block a user