package me.tatarka.bindingcollectionadapter2; import android.databinding.DataBindingUtil; import android.databinding.ObservableList; import android.databinding.ViewDataBinding; import android.support.annotation.LayoutRes; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import java.lang.ref.WeakReference; import java.util.List; /** * A {@link BaseAdapter} that binds items to layouts using the given {@link ItemBinding} If you give * it an {@link ObservableList} it will also updated itself based on changes to that list. */ public class BindingListViewAdapter<T> extends BaseAdapter implements BindingCollectionAdapter<T> { private final int itemTypeCount; private ItemBinding<T> itemBinding; @LayoutRes private int dropDownItemLayout; @NonNull private final WeakReferenceOnListChangedCallback<T> callback = new WeakReferenceOnListChangedCallback<>(this); private List<T> items; private int[] layouts; private LayoutInflater inflater; private ItemIds<? super T> itemIds; private ItemIsEnabled<? super T> itemIsEnabled; /** * Constructs a new instance with the given item count. */ public BindingListViewAdapter(int itemTypeCount) { this.itemTypeCount = itemTypeCount; } @Override public void setItemBinding(ItemBinding<T> itemBinding) { this.itemBinding = itemBinding; } @Override public ItemBinding<T> getItemBinding() { return itemBinding; } /** * Set a different layout to show for {@link #getDropDownView(int, View, ViewGroup)}. If this is * null, it will default to {@link #getView(int, View, ViewGroup)}. */ public void setDropDownItemLayout(@LayoutRes int layoutRes) { dropDownItemLayout = layoutRes; } @Override public void setItems(@Nullable List<T> items) { if (this.items == items) { return; } if (this.items instanceof ObservableList) { ((ObservableList<T>) this.items).removeOnListChangedCallback(callback); } if (items instanceof ObservableList) { ((ObservableList<T>) items).addOnListChangedCallback(callback); } this.items = items; notifyDataSetChanged(); } @Override public T getAdapterItem(int position) { return items.get(position); } @Override public ViewDataBinding onCreateBinding(LayoutInflater inflater, @LayoutRes int layoutRes, ViewGroup viewGroup) { return DataBindingUtil.inflate(inflater, layoutRes, viewGroup, false); } @Override public void onBindBinding(ViewDataBinding binding, int variableId, @LayoutRes int layoutRes, int position, T item) { if (itemBinding.bind(binding, item)) { binding.executePendingBindings(); } } /** * Set the item id's for the items. If not null, this will make {@link #hasStableIds()} return * true. */ public void setItemIds(@Nullable ItemIds<? super T> itemIds) { this.itemIds = itemIds; } /** * Sets {@link #isEnabled(int)} for the items. */ public void setItemIsEnabled(@Nullable ItemIsEnabled<? super T> itemIsEnabled) { this.itemIsEnabled = itemIsEnabled; } @Override public int getCount() { return items == null ? 0 : items.size(); } @Override public T getItem(int position) { return items.get(position); } @Override public long getItemId(int position) { return itemIds == null ? position : itemIds.getItemId(position, items.get(position)); } @Override public boolean isEnabled(int position) { return itemIsEnabled == null || itemIsEnabled.isEnabled(position, items.get(position)); } @Override public final View getView(int position, View convertView, @NonNull ViewGroup parent) { if (inflater == null) { inflater = LayoutInflater.from(parent.getContext()); } int viewType = getItemViewType(position); int layoutRes = layouts[viewType]; ViewDataBinding binding; if (convertView == null) { binding = onCreateBinding(inflater, layoutRes, parent); } else { binding = DataBindingUtil.getBinding(convertView); } T item = items.get(position); onBindBinding(binding, itemBinding.variableId(), layoutRes, position, item); return binding.getRoot(); } @Override public final View getDropDownView(int position, View convertView, ViewGroup parent) { if (inflater == null) { inflater = LayoutInflater.from(parent.getContext()); } if (dropDownItemLayout == 0) { return super.getDropDownView(position, convertView, parent); } else { int layoutRes = dropDownItemLayout; ViewDataBinding binding; if (convertView == null) { binding = onCreateBinding(inflater, layoutRes, parent); } else { binding = DataBindingUtil.getBinding(convertView); } T item = items.get(position); onBindBinding(binding, itemBinding.variableId(), layoutRes, position, item); return binding.getRoot(); } } @Override public int getItemViewType(int position) { ensureLayoutsInit(); T item = items.get(position); itemBinding.onItemBind(position, item); int firstEmpty = 0; for (int i = 0; i < layouts.length; i++) { if (itemBinding.layoutRes() == layouts[i]) { return i; } if (layouts[i] == 0) { firstEmpty = i; } } layouts[firstEmpty] = itemBinding.layoutRes(); return firstEmpty; } @Override public boolean hasStableIds() { return itemIds != null; } @Override public int getViewTypeCount() { return ensureLayoutsInit(); } private int ensureLayoutsInit() { int count = itemTypeCount; if (layouts == null) { layouts = new int[count]; } return count; } private static class WeakReferenceOnListChangedCallback<T> extends ObservableList.OnListChangedCallback<ObservableList<T>> { final WeakReference<BindingListViewAdapter<T>> adapterRef; WeakReferenceOnListChangedCallback(BindingListViewAdapter<T> adapter) { this.adapterRef = new WeakReference<>(adapter); } @Override public void onChanged(ObservableList sender) { BindingListViewAdapter<T> adapter = adapterRef.get(); if (adapter == null) { return; } Utils.ensureChangeOnMainThread(); adapter.notifyDataSetChanged(); } @Override public void onItemRangeChanged(ObservableList sender, int positionStart, int itemCount) { onChanged(sender); } @Override public void onItemRangeInserted(ObservableList sender, int positionStart, int itemCount) { onChanged(sender); } @Override public void onItemRangeMoved(ObservableList sender, int fromPosition, int toPosition, int itemCount) { onChanged(sender); } @Override public void onItemRangeRemoved(ObservableList sender, int positionStart, int itemCount) { onChanged(sender); } } public interface ItemIds<T> { long getItemId(int position, T item); } public interface ItemIsEnabled<T> { boolean isEnabled(int position, T item); } }