package com.dl7.recycler.adapter; import android.content.Context; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.support.annotation.LayoutRes; import android.support.v4.content.ContextCompat; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.StaggeredGridLayoutManager; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import com.dl7.recycler.R; import com.dl7.recycler.helper.ItemTouchHelperAdapter; import com.dl7.recycler.helper.OnStartDragListener; import com.dl7.recycler.helper.SimpleItemTouchHelperCallback; import com.dl7.recycler.listener.OnItemMoveListener; import com.dl7.recycler.listener.OnRecyclerViewItemClickListener; import com.dl7.recycler.listener.OnRecyclerViewItemLongClickListener; import com.dl7.recycler.listener.OnRemoveDataListener; import com.dl7.recycler.listener.OnRequestDataListener; import com.github.ybq.android.spinkit.SpinKitView; import com.github.ybq.android.spinkit.SpriteFactory; import com.github.ybq.android.spinkit.Style; import com.github.ybq.android.spinkit.sprite.Sprite; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * Created by long on 2016/4/21. * 适配器基类 */ public abstract class BaseQuickAdapter<T> extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements ItemTouchHelperAdapter { public static final int HEADER_VIEW = 0x00000111; public static final int LOADING_VIEW = 0x00000222; public static final int FOOTER_VIEW = 0x00000333; public static final int EMPTY_VIEW = 0x00000555; public static final int FULL_VIEW = 0x00000666; public static final int FULL_VIEW_2 = 0x00000777; public static final int FULL_VIEW_3 = 0x00000888; protected Context mContext; protected int mLayoutResId; protected LayoutInflater mLayoutInflater; protected List<T> mData; private View mParentView; // head and footer private View mHeaderView; private View mFooterView; // listener private OnRecyclerViewItemClickListener mItemClickListener; private OnRecyclerViewItemLongClickListener mItemLongClickListener; private OnRequestDataListener onRequestDataListener; private OnRemoveDataListener mRemoveDataListener; // drag and swipe private OnStartDragListener mDragStartListener; private OnItemMoveListener mItemMoveListener; private SimpleItemTouchHelperCallback mDragCallback; private int mDragFixCount = 0; // 固定数量,从0开始算 private Drawable mDragFixDrawable; // load more private boolean mIsLoadMoreEnable; private boolean mIsLoadingNow; private boolean mIsNoMoreData; private String mLoadingStr; private View mLoadingView; private TextView mLoadingDesc; private SpinKitView mLoadingIcon; // empty private View mEmptyView; public BaseQuickAdapter(Context context) { this(context, null); } public BaseQuickAdapter(Context context, List<T> data) { mLayoutResId = attachLayoutRes(); if (mLayoutResId == 0) { throw new IllegalAccessError("Layout resource ID must be valid!"); } if (data == null) { mData = new ArrayList<>(); } else { this.mData = data; } this.mContext = context; this.mLayoutInflater = LayoutInflater.from(mContext); } /** * 绑定布局 * * @return */ @LayoutRes protected abstract int attachLayoutRes(); /** * Implement this method and use the helper to adapt the view to the given item. * * @param holder A fully initialized helper. * @param item The item that needs to be displayed. */ protected abstract void convert(BaseViewHolder holder, T item); @Override public int getItemCount() { int count = mData.size() + getHeaderViewsCount() + getFooterViewsCount(); if (count == 0 && mEmptyView != null) { return 1; } if (mIsLoadMoreEnable && mData.size() != 0) { count++; } return count; } @Override public long getItemId(int position) { return position; } @Override public int getItemViewType(int position) { if (mHeaderView != null && position == 0) { return HEADER_VIEW; } else if ((mData.size() + getHeaderViewsCount() + getFooterViewsCount()) == 0 && mEmptyView != null) { return EMPTY_VIEW; } else if (mIsLoadMoreEnable) { if (position == (getItemCount() - 1)) { return LOADING_VIEW; } else if (mFooterView != null && position == (getItemCount() - 2)) { return FOOTER_VIEW; } } else if (mFooterView != null && position == (getItemCount() - 1)) { return FOOTER_VIEW; } return getDefItemViewType(position - getHeaderViewsCount()); } /** * 获取 ItemView 类型,对于多种布局的 RecyclerView 有用 * @param position * @return */ protected int getDefItemViewType(int position) { return super.getItemViewType(position); } /** * Called when a view created by this adapter has been attached to a window. * simple to solve item will layout using all * {@link #_setFullSpan(RecyclerView.ViewHolder)} * * @param holder */ @Override public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) { super.onViewAttachedToWindow(holder); int type = holder.getItemViewType(); if (type == EMPTY_VIEW || type == HEADER_VIEW || type == FOOTER_VIEW || type == LOADING_VIEW || type == FULL_VIEW || type == FULL_VIEW_2 || type == FULL_VIEW_3) { _setFullSpan(holder); } } /** * When set to true, the item will layout using all span area. That means, if orientation * is vertical, the view will have full width; if orientation is horizontal, the view will * have full height. * if the hold view use StaggeredGridLayoutManager they should using all span area * * @param holder True if this item should traverse all spans. */ protected void _setFullSpan(RecyclerView.ViewHolder holder) { if (holder.itemView.getLayoutParams() instanceof StaggeredGridLayoutManager.LayoutParams) { StaggeredGridLayoutManager.LayoutParams params = (StaggeredGridLayoutManager.LayoutParams) holder.itemView.getLayoutParams(); params.setFullSpan(true); } } @Override public void onAttachedToRecyclerView(RecyclerView recyclerView) { super.onAttachedToRecyclerView(recyclerView); RecyclerView.LayoutManager manager = recyclerView.getLayoutManager(); if (manager instanceof GridLayoutManager) { final GridLayoutManager gridManager = ((GridLayoutManager) manager); gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { @Override public int getSpanSize(int position) { int type = getItemViewType(position); return (type == EMPTY_VIEW || type == HEADER_VIEW || type == FOOTER_VIEW || type == LOADING_VIEW || type == FULL_VIEW || type == FULL_VIEW_2 || type == FULL_VIEW_3) ? gridManager.getSpanCount() : 1; } }); } } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (mParentView == null) { mParentView = parent; } BaseViewHolder baseViewHolder; switch (viewType) { case LOADING_VIEW: baseViewHolder = new BaseViewHolder(mLoadingView); break; case HEADER_VIEW: baseViewHolder = new BaseViewHolder(mHeaderView); break; case EMPTY_VIEW: baseViewHolder = new BaseViewHolder(mEmptyView); break; case FOOTER_VIEW: baseViewHolder = new BaseViewHolder(mFooterView); break; default: baseViewHolder = onCreateDefViewHolder(parent, viewType); // 设置用于单项刷新的tag标识 baseViewHolder.itemView.setTag(R.id.view_holder_tag, baseViewHolder); _initItemClickListener(baseViewHolder); break; } return baseViewHolder; } /** * 创建 ViewHolder * @param parent parent * @param viewType ItemView 类型,对于多种布局的 RecyclerView 有用 * @return BaseViewHolder */ protected BaseViewHolder onCreateDefViewHolder(ViewGroup parent, int viewType) { return createBaseViewHolder(parent, mLayoutResId); } protected BaseViewHolder createBaseViewHolder(ViewGroup parent, int layoutResId) { View view = mLayoutInflater.inflate(layoutResId, parent, false); return new BaseViewHolder(view); } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { switch (holder.getItemViewType()) { case LOADING_VIEW: _loadMore(); break; case HEADER_VIEW: break; case EMPTY_VIEW: break; case FOOTER_VIEW: break; default: convert((BaseViewHolder) holder, mData.get(holder.getLayoutPosition() - getHeaderViewsCount())); _setDragFixBackground(holder, position); break; } } /************************************* 加载更多 ****************************************/ public void setRequestDataListener(OnRequestDataListener listener) { this.onRequestDataListener = listener; if (!mIsLoadMoreEnable) { this.enableLoadMore(true); } } public void enableLoadMore(boolean isEnable) { this.mIsLoadMoreEnable = isEnable; _initLoadingView(); } public void setLoadStyle(Style style) { Sprite sprite = SpriteFactory.create(style); _initLoadingView(); mLoadingIcon.setIndeterminateDrawable(sprite); } public void setLoadDesc(String desc) { _initLoadingView(); mLoadingStr = desc; mLoadingDesc.setText(mLoadingStr); } public void setLoadColor(int color) { mLoadingDesc.setTextColor(color); mLoadingIcon.getIndeterminateDrawable().setColor(color); } /** * 加载完成 */ public void loadComplete() { mIsLoadingNow = false; } /** * 没有更多数据,后面不再加载数据 */ public void noMoreData() { mIsLoadingNow = false; mIsNoMoreData = true; mLoadingIcon.setVisibility(View.GONE); mLoadingDesc.setText(R.string.no_more_data); } /** * 加载数据异常,重新进入可再加载数据 */ public void loadAbnormal() { mIsLoadingNow = false; mLoadingIcon.setVisibility(View.GONE); mLoadingDesc.setText(R.string.load_abnormal); } private void _initLoadingView() { if (mLoadingView == null) { mLoadingView = mLayoutInflater.inflate(R.layout.layout_load_more, null); mLoadingView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); mLoadingDesc = (TextView) mLoadingView.findViewById(R.id.tv_loading_desc); mLoadingIcon = (SpinKitView) mLoadingView.findViewById(R.id.iv_loading_icon); mLoadingStr = mContext.getResources().getString(R.string.loading_desc); mLoadingDesc.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (!mIsLoadingNow && !mIsNoMoreData) { _loadMore(); } } }); } } private void _loadMore() { if (!mIsLoadingNow && onRequestDataListener != null && !mIsNoMoreData) { if (mLoadingIcon.getVisibility() == View.GONE) { mLoadingIcon.setVisibility(View.VISIBLE); mLoadingDesc.setText(mLoadingStr); } mIsLoadingNow = true; onRequestDataListener.onLoadMore(); } } /************************************* 头尾视图 ****************************************/ public View getHeaderView() { return mHeaderView; } public void addHeaderView(View headerView) { headerView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); mHeaderView = headerView; notifyDataSetChanged(); } public View getFooterView() { return mFooterView; } public void addFooterView(View footerView) { footerView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); mFooterView = footerView; notifyDataSetChanged(); } public int getHeaderViewsCount() { return mHeaderView == null ? 0 : 1; } public int getFooterViewsCount() { return mFooterView == null ? 0 : 1; } /************************************空数据****************************************/ public View getEmptyView() { return mEmptyView; } public void setEmptyView(View emptyView) { mEmptyView = emptyView; // mEmptyView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, // ViewGroup.LayoutParams.MATCH_PARENT)); } public int getEmptyViewCount() { return mEmptyView == null ? 0 : 1; } /************************************数据操作****************************************/ /** * Get the data item associated with the specified position in the data set. * * @param position Position of the item whose data we want within the adapter's * data set. * @return The data at the specified position. */ public T getItem(int position) { return mData.get(position); } /** * Get the data of list * * @return */ public List<T> getData() { return mData; } /** * 更新数据,替换原有数据 * * @param items */ public void updateItems(List<T> items) { mData = new ArrayList<>(items); notifyDataSetChanged(); mIsNoMoreData = false; } private void _addItem(int position, T item) { if (mData == null || mData.size() == 0) { mData = new ArrayList<>(); mData.add(item); } else { mData.add(position, item); } } private void _addItemList(int position, List<T> items) { if (mData == null || mData.size() == 0) { mData = new ArrayList<>(); } mData.addAll(position, items); } /** * 首部插入一条数据 * * @param item 数据 */ public void addItem(T item) { _addItem(0, item); notifyItemInserted(0); } /** * 插入一条数据 * * @param item 数据 * @param position 插入位置 */ public void addItem(T item, int position) { position = Math.min(position, mData.size()); _addItem(position, item); notifyItemInserted(_calcPosition(position)); } /** * 尾部插入一条数据 * @param item 数据 */ public void addLastItem(T item) { _addItem(mData.size(), item); notifyItemInserted(_calcPosition(mData.size())); } /** * 在列表尾添加一串数据 * * @param items */ public void addItems(List<T> items) { _addItemList(mData.size(), items); int position = _calcPosition(mData.size()); for (T item : items) { notifyItemInserted(position++); } } /** * 在列表尾添加一串数据 * * @param items */ public void addItems(List<T> items, int position) { position = Math.min(position, mData.size()); _addItemList(position, items); int pos = _calcPosition(position); for (T item : items) { notifyItemInserted(pos++); } } /** * 移除一条数据 * * @param position 位置 */ public void removeItem(int position) { if (position > mData.size() - 1) { return; } int pos = _calcPosition(position); if (mRemoveDataListener != null) { // 放在 mData.remove(pos) 前,不然外面获取不到数据 mRemoveDataListener.onRemove(pos); } mData.remove(position); notifyItemRemoved(pos); } /** * 移除一条数据 * * @param item 数据 */ public void removeItem(T item) { int pos = 0; for (T info : mData) { if (item.hashCode() == info.hashCode()) { removeItem(pos); break; } pos++; } } /** * 清除所有数据 */ public void cleanItems() { mData.clear(); notifyDataSetChanged(); } /** * 计算位置,算上头部 * @param position * @return */ private int _calcPosition(int position) { if (mHeaderView != null) { position++; } return position; } /************************************监听****************************************/ /** * Register a callback to be invoked when an item in this AdapterView has * been clicked. * * @param listener The callback that will be invoked. */ public void setOnItemClickListener(OnRecyclerViewItemClickListener listener) { this.mItemClickListener = listener; } /** * Register a callback to be invoked when an item in this AdapterView has * been clicked and held * * @param listener The callback that will run */ public void setOnItemLongClickListener(OnRecyclerViewItemLongClickListener listener) { this.mItemLongClickListener = listener; } /** * 设置移除监听 * @param removeDataListener */ public void setRemoveDataListener(OnRemoveDataListener removeDataListener) { mRemoveDataListener = removeDataListener; } /** * init the baseViewHolder to register onRecyclerViewItemClickListener and onRecyclerViewItemLongClickListener * * @param baseViewHolder */ private void _initItemClickListener(final BaseViewHolder baseViewHolder) { if (mItemClickListener != null) { baseViewHolder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mItemClickListener.onItemClick(v, baseViewHolder.getLayoutPosition()); } }); } if (mItemLongClickListener != null) { baseViewHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { return mItemLongClickListener.onItemLongClick(v, baseViewHolder.getLayoutPosition()); } }); } if (mDragCallback != null && mDragFixCount > 0) { baseViewHolder.itemView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (baseViewHolder.getLayoutPosition() < mDragFixCount) { mDragCallback.setEnable(false); } else { mDragCallback.setEnable(true); } return false; } }); } } /************************************拖拽滑动****************************************/ @Override public boolean onItemMove(int fromPosition, int toPosition) { if (fromPosition >= mDragFixCount && toPosition >= mDragFixCount) { Collections.swap(mData, fromPosition, toPosition); notifyItemMoved(fromPosition, toPosition); if (mItemMoveListener != null) { mItemMoveListener.onItemMove(fromPosition, toPosition); } return true; } return false; } @Override public void onItemDismiss(int position) { removeItem(position); } public void setDragStartListener(OnStartDragListener dragStartListener) { mDragStartListener = dragStartListener; } public void setDragCallback(SimpleItemTouchHelperCallback dragCallback) { mDragCallback = dragCallback; } protected void startDrag(RecyclerView.ViewHolder viewHolder) { if (mDragStartListener != null) { mDragStartListener.onStartDrag(viewHolder); } } public void setItemMoveListener(OnItemMoveListener itemMoveListener) { mItemMoveListener = itemMoveListener; } /** * 该方法在添加列表数据前调用 * @param dragFixCount 固定的数量 */ public void setDragFixCount(int dragFixCount) { mDragFixCount = dragFixCount; if (mDragFixDrawable == null) { mDragFixDrawable = ContextCompat.getDrawable(mContext, R.drawable.shape_drag_default); } } public void setDragFixDrawable(int fixColor) { mDragFixDrawable = new ColorDrawable(fixColor); } public void setDragFixDrawable(Drawable drawable) { mDragFixDrawable = drawable; } public void setDragColor(int dragColor) { BaseViewHolder.setDragColor(dragColor); } public void setDragDrawable(Drawable drawable) { BaseViewHolder.setDragDrawable(drawable); } /** * 设置固定项的背景色 * @param holder * @param position */ private void _setDragFixBackground(RecyclerView.ViewHolder holder, int position) { if (position < mDragFixCount) { holder.itemView.setBackgroundDrawable(mDragFixDrawable); } } /** * 设置拖拽控制标志位 * eg: ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | * ItemTouchHelper.RIGHT | ItemTouchHelper.START | ItemTouchHelper.END * @param dragFlags */ public void setDragFlags(int dragFlags) { if (mDragCallback != null) { mDragCallback.setDragFlags(dragFlags); } } /** * 设置滑动控制标志位 * eg: ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | * ItemTouchHelper.RIGHT | ItemTouchHelper.START | ItemTouchHelper.END * @param swipeFlags */ public void setSwipeFlags(int swipeFlags) { if (mDragCallback != null) { mDragCallback.setSwipeFlags(swipeFlags); } } /************************************* Tag标志 ****************************************/ /** * 给BaseViewHolder设置Tag * @param viewHolder 目标BaseViewHolder * @param tag tag标志 */ public void setTag(BaseViewHolder viewHolder, Object tag) { viewHolder.itemView.setTag(tag); } /** * 根据tag标志获取BaseViewHolder * @param tag tag标志 * @return 目标BaseViewHolder */ public BaseViewHolder getTag(Object tag) { View view = mParentView.findViewWithTag(tag); if (view == null) { return null; } return (BaseViewHolder) view.getTag(R.id.view_holder_tag); } }