Android DataBindingベースの汎用RecyclerView Adapter
この構造はBRVAHフレームワークの修正から来ており、興味のある方はBRVAHアダプタフレームワークを優先的に知ることができます.
サポート:
原生のRecyclerViewをほぼサポートしています.Adapterが実現できるすべての効果.
次の操作を行います.
/**
*
* :
* layoutItem item variable
*
* @param recyclerView
* @param layoutItem
* @param
*/
private void singleAdapter(RecyclerView recyclerView, @LayoutRes int layoutItem) {
BaseHxAdapter adapter = new BaseHxAdapter<>(layoutItem);
// RecyclerView
adapter.bindToRecyclerView(recyclerView);
// , bindToRecyclerView , , adapter.bindToRecyclerView(recyclerView);
adapter.setOnLoadMoreListener(() -> {
}, recyclerView);
// bindToRecyclerView() , adapter.setOnLoadMoreListener , RecyclerView Adapter
// , , ,
adapter.disableLoadMoreIfNotFullPage();
// ; adapter.disableLoadMoreIfNotFullPage() , disableLoadMoreIfNotFullPage ,
adapter.setEnableLoadMore(true);
//
adapter.loadMoreComplete();
// , true or false;true , “ ”,false
adapter.loadMoreEnd(true);
//
adapter.loadMoreFail();
// ,
adapter.setNewData(null);
// , ,
adapter.addData((ArrayList) null);
//
adapter.getItem(0);
//
List list = adapter.getData();
//
adapter.remove(0);
// ; , ; , setNewData();
adapter.replaceData((ArrayList) null);
}
/**
* ;
* :
* 1. IMultiItemType , layoutItems
* 2.layoutItems item variable
*
* @param recyclerView
* @param layoutItems
*/
private void multipleAdapter(RecyclerView recyclerView, @LayoutRes int... layoutItems) {
BaseMultiItemHxAdapter adapter = new BaseMultiItemHxAdapter() {
@Override
protected void bindTypeAndLayout() {
for (int i = 0; i < layoutItems.length; i++) {
addItemType(i, layoutItems[0]);
}
}
};
// , singleAdapter
}
キーコード:
/**
* function:RecyclerView
* author: frj
* modify date: 2018/6/7
*/
public class BaseHxAdapter extends RecyclerView.Adapter {
/**
*
*/
private static final int TYPE_LOAD_MORE = 0x111;
/**
*
*/
private static final int TYPE_ITEM = 0;
/**
*
*/
private LoadMoreView mLoadMoreView;
//
private boolean mNextLoadEnable = false;
/**
*
*/
private boolean mLoadMoreEnable = false;
/**
*
*/
private boolean mLoading = false;
private int mLastPosition = -1;
private RecyclerView mRecyclerView;
/**
*
*/
private OnLoadMoreListener onLoadMoreListener;
/**
* id
*/
private @LayoutRes
int mItemLayoutId;
/**
*
*/
protected List mData;
protected Context mContext;
protected LayoutInflater mLayoutInflater;
public BaseHxAdapter(@LayoutRes int layoutResId) {
mData = new ArrayList<>();
this.mItemLayoutId = layoutResId;
this.mLoadMoreView = new SimpleLoadMoreView();
}
public BaseHxAdapter(@LayoutRes int layoutResId, List data) {
this(layoutResId);
setNewData(data);
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
RecyclerView.ViewHolder viewHolder = null;
this.mContext = parent.getContext();
this.mLayoutInflater = LayoutInflater.from(mContext);
//
if (viewType == TYPE_LOAD_MORE) {
viewHolder = new LoadMoreViewHolder(getItemView(getLoadMoreView().getLayoutId(), parent));
} else {
viewHolder = V.create(parent, getItemLayoutId(viewType));
}
return viewHolder;
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
int itemType = getItemViewType(position);
// LoadMore ViewHolder ,
if (itemType == TYPE_LOAD_MORE) {
if (mLoadMoreView != null) {
autoLoadMore(position);
mLoadMoreView.convert((LoadMoreViewHolder) holder);
}
} else {
bindDataItem(holder, position);
}
}
@Override
public int getItemCount() {
return mData.size() + getLoadMoreViewCount();
}
@Override
public int getItemViewType(int position) {
if (mLoadMoreEnable) {
if (position == mData.size()) {
return TYPE_LOAD_MORE;
}
}
return getDefItemViewType(position);
}
/**
* View Window
*
* @param holder
*/
@Override
public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {
super.onViewAttachedToWindow(holder);
int type = holder.getItemViewType();
if (TYPE_LOAD_MORE == type) {
setFullSpan(holder);
}
}
/**
* Item
*
* @return
*/
protected int getItemLayoutId(int viewType) {
return mItemLayoutId;
}
/**
*
*
* @param position
* @return
*/
protected int getDefItemViewType(int position) {
return TYPE_ITEM;
}
/**
*
*
* @param holder
* @param position
*/
protected void bindDataItem(RecyclerView.ViewHolder holder, int position) {
if (holder != null) {
ItemViewHolder itemViewHolder = (ItemViewHolder) holder;
itemViewHolder.bindItem(getItem(position));
}
}
/**
*
*
* @param data
*/
public void setNewData(@Nullable List data) {
this.mData = data == null ? new ArrayList() : data;
if (onLoadMoreListener != null) {
mNextLoadEnable = true;
mLoadMoreEnable = true;
mLoading = false;
mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_DEFAULT);
}
mLastPosition = -1;
notifyDataSetChanged();
}
/**
*
*
* @param position
* @param item
* @deprecated {@link #addData(int, Object)}
*/
@Deprecated
public void add(@IntRange(from = 0) int position, @NonNull K item) {
addData(position, item);
}
/**
*
*
* @param position
*/
public void addData(@IntRange(from = 0) int position, @NonNull K data) {
mData.add(position, data);
notifyItemInserted(position);
compatibilityDataSizeChanged(1);
}
/**
* add one new data
*/
public void addData(@NonNull K data) {
mData.add(data);
notifyItemInserted(mData.size());
compatibilityDataSizeChanged(1);
}
/**
*
*
* @param position
*/
public void remove(@IntRange(from = 0) int position) {
if (mData == null
|| position < 0
|| position >= mData.size()) {
return;
}
mData.remove(position);
int internalPosition = position;
notifyItemRemoved(internalPosition);
compatibilityDataSizeChanged(0);
notifyItemRangeChanged(internalPosition, mData.size() - internalPosition);
}
/**
*
*/
public void setData(@IntRange(from = 0) int index, @NonNull K data) {
mData.set(index, data);
notifyItemChanged(index);
}
/**
*
*
* @param position
* @param newData
*/
public void addData(@IntRange(from = 0) int position, @NonNull Collection extends K> newData) {
mData.addAll(position, newData);
notifyItemRangeInserted(position + 0, newData.size());
compatibilityDataSizeChanged(newData.size());
}
/**
*
*
* @param newData
*/
public void addData(@NonNull Collection extends K> newData) {
mData.addAll(newData);
notifyItemRangeInserted(mData.size() - newData.size(), newData.size());
compatibilityDataSizeChanged(newData.size());
}
/**
* use data to replace all item in mData. this method is different {@link #setNewData(List)},
* it doesn't change the mData reference
*
* @param data data collection
*/
public void replaceData(@NonNull Collection extends K> data) {
//
if (data != mData) {
mData.clear();
mData.addAll(data);
}
notifyDataSetChanged();
}
/**
*
*
* @param size
*/
private void compatibilityDataSizeChanged(int size) {
final int dataSize = mData == null ? 0 : mData.size();
if (dataSize == size) {
notifyDataSetChanged();
}
}
/**
*
*
* @return
*/
@NonNull
public List getData() {
return mData;
}
/**
*
*
* @param position
* @return
*/
@Nullable
public K getItem(@IntRange(from = 0) int position) {
if (position >= 0 && position < mData.size()) {
return mData.get(position);
} else {
return null;
}
}
/**
*
*
* @param position
*/
public final void refreshNotifyItemChanged(int position) {
notifyItemChanged(position);
}
/**
* RecyclerView
*/
private void checkNotNull() {
if (mRecyclerView == null) {
throw new RuntimeException("please bind recyclerView first!");
}
}
protected RecyclerView getRecyclerView() {
return mRecyclerView;
}
private void setRecyclerView(RecyclerView recyclerView) {
mRecyclerView = recyclerView;
}
/**
* RecyclerView , ,
*/
public void bindToRecyclerView(RecyclerView recyclerView) {
if (getRecyclerView() != null) {
throw new RuntimeException("Don't bind twice");
}
setRecyclerView(recyclerView);
if (getRecyclerView().getAdapter() == null) {
getRecyclerView().setAdapter(this);
}
}
/**
*
*
* @return
*/
protected LoadMoreView getLoadMoreView() {
if (mLoadMoreView == null) {
mLoadMoreView = new SimpleLoadMoreView();
}
return mLoadMoreView;
}
/**
*
*
* @param loadMoreView
*/
public void setLoadMoreView(LoadMoreView loadMoreView) {
if (loadMoreView == null) {
return;
}
this.mLoadMoreView = loadMoreView;
}
/**
*
*
* @param onLoadMoreListener
*/
private void openLoadMore(OnLoadMoreListener onLoadMoreListener) {
this.onLoadMoreListener = onLoadMoreListener;
mNextLoadEnable = true;
mLoadMoreEnable = true;
mLoading = false;
}
/**
*
*
* @param onLoadMoreListener
* @param recyclerView
*/
public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener, RecyclerView recyclerView) {
openLoadMore(onLoadMoreListener);
bindToRecyclerView(recyclerView);
}
/**
*
*
* @return true ,false
*/
public boolean isLoadMoreEnable() {
return mLoadMoreEnable;
}
/**
*
*
* @return 0 or 1
*/
public int getLoadMoreViewCount() {
if (onLoadMoreListener == null || !mLoadMoreEnable) {
return 0;
}
if (!mNextLoadEnable && mLoadMoreView.isLoadEndMoreGone()) {
return 0;
}
if (mData.size() == 0) {
return 0;
}
return 1;
}
/**
* Gets to load more locations
*
* @return
*/
public int getLoadMoreViewPosition() {
return mData.size();
}
/**
*
*
* @param enable true ;false
*/
public void setEnableLoadMore(boolean enable) {
int oldLoadMoreCount = getLoadMoreViewCount();
mLoadMoreEnable = enable;
int newLoadMoreCount = getLoadMoreViewCount();
if (oldLoadMoreCount == 1) {
if (newLoadMoreCount == 0) {
notifyItemRemoved(getLoadMoreViewPosition());
}
} else {
if (newLoadMoreCount == 1) {
mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_DEFAULT);
notifyItemInserted(getLoadMoreViewPosition());
}
}
}
/**
* @return
*/
public boolean isLoading() {
return mLoading;
}
/**
* ,
*/
public void loadMoreEnd() {
loadMoreEnd(false);
}
/**
*
*
* @param gone true
*/
public void loadMoreEnd(boolean gone) {
if (getLoadMoreViewCount() == 0) {
return;
}
mLoading = false;
mNextLoadEnable = false;
mLoadMoreView.setLoadMoreEndGone(gone);
if (gone) {
notifyItemRemoved(getLoadMoreViewPosition());
} else {
mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_END);
notifyItemChanged(getLoadMoreViewPosition());
}
}
/**
*
*/
public void loadMoreComplete() {
if (getLoadMoreViewCount() == 0) {
return;
}
mLoading = false;
mNextLoadEnable = true;
mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_DEFAULT);
notifyItemChanged(getLoadMoreViewPosition());
}
/**
*
*/
public void loadMoreFail() {
if (getLoadMoreViewCount() == 0) {
return;
}
mLoading = false;
mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_FAIL);
notifyItemChanged(getLoadMoreViewPosition());
}
/**
*
*/
public void notifyLoadMoreToLoading() {
if (mLoadMoreView.getLoadMoreStatus() == LoadMoreView.STATUS_LOADING) {
return;
}
mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_DEFAULT);
notifyItemChanged(getItemCount() - 1);
}
/**
* , ;
* RecyclerView
*
* @see #disableLoadMoreIfNotFullPage(RecyclerView)
*/
public void disableLoadMoreIfNotFullPage() {
checkNotNull();
disableLoadMoreIfNotFullPage(getRecyclerView());
}
/**
*
* !!
*
* , {@link #setNewData(List)}
* , load more,
*
* !!
*
* @param recyclerView your recyclerView
* @see #setNewData(List)
*/
private void disableLoadMoreIfNotFullPage(RecyclerView recyclerView) {
setEnableLoadMore(false);
if (recyclerView == null) {
return;
}
RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
if (manager == null) {
return;
}
if (manager instanceof LinearLayoutManager) {
final LinearLayoutManager linearLayoutManager = (LinearLayoutManager) manager;
recyclerView.postDelayed(new Runnable() {
@Override
public void run() {
if (isFullScreen(linearLayoutManager)) {
setEnableLoadMore(true);
}
}
}, 50);
} else if (manager instanceof StaggeredGridLayoutManager) {
final StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) manager;
recyclerView.postDelayed(new Runnable() {
@Override
public void run() {
final int[] positions = new int[staggeredGridLayoutManager.getSpanCount()];
staggeredGridLayoutManager.findLastCompletelyVisibleItemPositions(positions);
//
int pos = getTheBiggestNumber(positions) + 1;
if (pos != getItemCount()) {
setEnableLoadMore(true);
}
}
}, 50);
}
}
/**
* , View
*
* @param layoutResId id
* @param parent
* @return
*/
protected View getItemView(@LayoutRes int layoutResId, ViewGroup parent) {
return mLayoutInflater.inflate(layoutResId, parent, false);
}
/**
*
*
* @param numbers
* @return
*/
private int getTheBiggestNumber(int[] numbers) {
int tmp = -1;
if (numbers == null || numbers.length == 0) {
return tmp;
}
for (int num : numbers) {
if (num > tmp) {
tmp = num;
}
}
return tmp;
}
/**
* holder
*
* @param holder Holde
*/
protected void setFullSpan(RecyclerView.ViewHolder holder) {
if (holder.itemView.getLayoutParams() instanceof StaggeredGridLayoutManager.LayoutParams) {
StaggeredGridLayoutManager.LayoutParams params = (StaggeredGridLayoutManager.LayoutParams) holder
.itemView.getLayoutParams();
params.setFullSpan(true);
}
}
/**
*
*
* @param llm
* @return
*/
private boolean isFullScreen(LinearLayoutManager llm) {
return (llm.findLastCompletelyVisibleItemPosition() + 1) != getItemCount() ||
llm.findFirstCompletelyVisibleItemPosition() != 0;
}
/**
*
*
* @param position
*/
private void autoLoadMore(int position) {
if (TYPE_LOAD_MORE != getItemViewType(position)) {
return;
}
if (mLoadMoreView.getLoadMoreStatus() != LoadMoreView.STATUS_DEFAULT) {
return;
}
mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_LOADING);
if (!mLoading) {
mLoading = true;
if (mRecyclerView != null) {
mRecyclerView.post(new Runnable() {
@Override
public void run() {
if (onLoadMoreListener != null) {
onLoadMoreListener.onLoadMore();
}
}
});
} else {
if (onLoadMoreListener != null) {
onLoadMoreListener.onLoadMore();
}
}
}
}
/**
*
*/
public interface OnLoadMoreListener {
/**
*
*/
void onLoadMore();
}
}
/**
* function:RecyclerView
* author: frj
* modify date: 2018/6/7
*/
public abstract class BaseMultiItemHxAdapter extends BaseHxAdapter {
/**
*
*/
private SparseIntArray layouts;
/**
*
*/
private static final int DEFAULT_VIEW_TYPE = -0xff;
/**
*
*/
public static final int TYPE_NOT_FOUND = -404;
public BaseMultiItemHxAdapter() {
this(null);
}
public BaseMultiItemHxAdapter(List data) {
super(0, data);
bindTypeAndLayout();
}
@Override
protected int getDefItemViewType(int position) {
T item = mData.get(position);
if (item != null) {
return item.getItemType();
}
return DEFAULT_VIEW_TYPE;
}
@Override
protected void bindDataItem(RecyclerView.ViewHolder holder, int position) {
if (holder != null) {
ItemViewHolder itemViewHolder = (ItemViewHolder) holder;
itemViewHolder.bindItem(getItem(position));
}
}
/**
*
*/
protected abstract void bindTypeAndLayout();
/**
* id
*
* @param viewType
* @return
*/
private int getLayoutId(int viewType) {
return layouts.get(viewType, TYPE_NOT_FOUND);
}
/**
* id
*
* @param type
* @param layoutResId id
*/
protected void addItemType(int type, @LayoutRes int layoutResId) {
if (layouts == null) {
layouts = new SparseIntArray();
}
layouts.put(type, layoutResId);
}
/**
* id
*
* @param viewType
* @return
*/
@Override
protected int getItemLayoutId(int viewType) {
return getLayoutId(viewType);
}
}
public class ItemViewHolder extends RecyclerView.ViewHolder {
public final T binding;
/**
* ItemViewHolder
*
* @param parent
* @param layoutRes
* @return
*/
public static ItemViewHolder create(@NonNull ViewGroup parent, @LayoutRes int layoutRes) {
return new ItemViewHolder(DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), layoutRes, parent, false));
}
public ItemViewHolder(T binding) {
super(binding.getRoot());
this.binding = binding;
}
/**
*
*
* @param item
* @param
*/
public void bindItem(K item) {
binding.setVariable(BR.item, item);
binding.executePendingBindings();
}
}
/**
* function:
* author: frj
* modify date: 2018/6/11
*/
public interface IMultiItemType {
/**
*
*
* @return
*/
int getItemType();
}
説明:
ItemViewHolderのbindItem()メソッドでは、固定名BRがバインドする.item、プロジェクトにitemというvariableがない場合、プロジェクトコンパイルは通過しません.解決策はレイアウトファイルでitemというvariableを定義することです.各Itemのレイアウトファイルにはitemというvariableが含まれています.
Demoダウンロード