BaseQuickAdapter

159 阅读6分钟
package com.alibaba.genie.panel.basic.base.adapter;

import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.CallSuper;
import android.support.annotation.IdRes;
import android.support.annotation.IntRange;
import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.ItemAnimator;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;

import com.alibaba.genie.panel.basic.R;
import com.alibaba.genie.panel.basic.base.adapter.viewholder.StateLayoutVH;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import kotlin.jvm.internal.Intrinsics;

public abstract class BaseQuickAdapter<T extends Object, VH extends RecyclerView.ViewHolder>
        extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private List<T> items;
    private int mLastPosition = -1;

    private OnItemClickListener<T> mOnItemClickListener;
    private OnItemLongClickListener<T> mOnItemLongClickListener;
    private SparseArray<OnItemChildClickListener<T>> mOnItemChildClickArray;
    private SparseArray<OnItemChildLongClickListener<T>> mOnItemChildLongClickArray;
    private List<OnViewAttachStateChangeListener> mOnViewAttachStateChangeListeners;

    private RecyclerView _recyclerView;

    /**
     * Whether to use state layout.
     * 是否使用状态布局。
     */
    private boolean isStateViewEnable;

    /**
     * State view. Attention please: take effect when [items] is empty array.
     * 状态视图,注意:[items]为空数组才会生效
     */
    @Nullable
    private View stateView;

    /**
     * Whether enable animation.
     * 是否打开动画
     */
    private boolean animationEnable;

    /**
     * Whether the animation executed only the first time.
     * 动画是否仅第一次执行
     */
    private boolean isAnimationFirstOnly;

    /**
     * Set custom animation.
     * 设置自定义动画
     */
    @Nullable
    private ItemAnimator itemAnimation;

    protected Handler mHandler = new Handler(Looper.getMainLooper());

    private static final int EMPTY_VIEW = R.id.BaseQuickAdapter_empty_view;
    public static final int EMPTY_PAYLOAD = 0;

    public BaseQuickAdapter() {
        items = new ArrayList<>();
    }

    public BaseQuickAdapter(List<T> items) {
        if (items == null) {
            items = new ArrayList<>();
        }
        this.items = items;
    }

    public List<T> getItems() {
        return items;
    }

    public final RecyclerView getRecyclerView() {
        RecyclerView recyclerView = this._recyclerView;
        if (recyclerView == null) {
            throw new IllegalStateException("Please get it after onAttachedToRecyclerView()");
        } else {
            return recyclerView;
        }
    }

    public final Context getContext() {
        Context context = this.getRecyclerView().getContext();
        Intrinsics.checkNotNullExpressionValue(context, "recyclerView.context");
        return context;
    }

//    /**
//     * Function to judge if the viewHolder is EmptyLayoutVH.
//     * 判断 ViewHolder 是否是 [StateLayoutVH]
//     *
//     * @receiver RecyclerView.ViewHolder
//     * @return Boolean
//     */
//    public final boolean isEmptyViewHolder(@NonNull RecyclerView.ViewHolder viewHolder) {
//        Intrinsics.checkNotNullParameter(viewHolder, "$this$isEmptyViewHolder");
//        return viewHolder instanceof StateLayoutVH;
//    }

    public final boolean isStateViewEnable() {
        return this.isStateViewEnable;
    }


    public final void setStateViewEnable(boolean value) {
        boolean oldDisplayEmptyLayout = displayEmptyView();
        this.isStateViewEnable = value;
        boolean newDisplayEmptyLayout = displayEmptyView();
        if (oldDisplayEmptyLayout && !newDisplayEmptyLayout) {
            this.notifyItemRemoved(0);
        } else if (newDisplayEmptyLayout && !oldDisplayEmptyLayout) {
            this.notifyItemInserted(0);
        } else if (oldDisplayEmptyLayout && newDisplayEmptyLayout) {
            this.notifyItemChanged(0, 0);
        }

    }

    @Nullable
    public View getStateView() {
        return stateView;
    }

    public final void setStateView(@Nullable View stateView) {
        boolean oldDisplayEmptyLayout = displayEmptyView();
        this.stateView = stateView;
        boolean newDisplayEmptyLayout = displayEmptyView();
        if (oldDisplayEmptyLayout && !newDisplayEmptyLayout) {
            this.notifyItemRemoved(0);
        } else if (newDisplayEmptyLayout && !oldDisplayEmptyLayout) {
            this.notifyItemInserted(0);
        } else if (oldDisplayEmptyLayout && newDisplayEmptyLayout) {
            this.notifyItemChanged(0, 0);
        }

    }

    public final boolean getAnimationEnable() {
        return this.animationEnable;
    }

    public final void setAnimationEnable(boolean var1) {
        this.animationEnable = var1;
    }

    public final boolean isAnimationFirstOnly() {
        return this.isAnimationFirstOnly;
    }

    public final void setAnimationFirstOnly(boolean var1) {
        this.isAnimationFirstOnly = var1;
    }

    @Nullable
    public final ItemAnimator getItemAnimation() {
        return this.itemAnimation;
    }

    public final void setItemAnimation(@Nullable ItemAnimator value) {
        this.animationEnable = true;
        this.itemAnimation = value;
    }

    @NonNull
    protected abstract VH onCreateViewHolder(@NonNull Context context, @NonNull ViewGroup parent, int viewType);

    /**
     * Implement this method and use the helper to adapt the view to the given item.
     * <p>
     * 实现此方法,并使用 [holder] 完成 item 视图的操作
     *
     * @param holder A fully initialized helper.
     * @param item   The item that needs to be displayed.
     */
    protected abstract void onBindViewHolder(@NonNull VH holder, int position, @Nullable T item);

    /**
     * Optional implementation this method and use the helper to adapt the view to the given item.
     * If use [payloads], will perform this method, Please implement this method for partial refresh.
     * If use [RecyclerView.Adapter.notifyItemChanged(Int, Object)] with payload,
     * Will execute this method.
     * <p>
     * 可选实现,如果你是用了[payloads]刷新item,请实现此方法,进行局部刷新
     *
     * @param holder   A fully initialized helper.
     * @param item     The item that needs to be displayed.
     * @param payloads payload info.
     */
    protected void onBindViewHolder(@NonNull VH holder, int position, @Nullable T item, @NonNull List payloads) {
        this.onBindViewHolder(holder, position, item);
    }

    /**
     * Override this method and return your item size.
     * 重写此方法,返回你的item数量。
     */
    protected int getItemCount(@NonNull List<T> items) {
        return items != null ? items.size() : 0;
    }

    /**
     * Override this method and return your ViewType.
     * 重写此方法,返回你的ViewType。
     */
    protected int getItemViewType(int position, @NonNull List<T> list) {
        Intrinsics.checkNotNullParameter(list, "list");
        return 0;
    }

    /**
     * Don't override this method.
     * 不要重写此方法
     *
     * @return Int
     */
    public final int getItemCount() {
        return displayEmptyView() ? 1 : this.getItemCount(this.getItems());
    }


    /**
     * Don't override this method.
     * 不要重写此方法
     *
     * @param position Int
     * @return Int
     */
    public final int getItemViewType(int position) {
        return displayEmptyView() ? EMPTY_VIEW : this.getItemViewType(position, this.getItems());
    }

    @NonNull
    public final RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        Intrinsics.checkNotNullParameter(parent, "parent");
        if (viewType == EMPTY_VIEW) {
            return new StateLayoutVH(parent, this.stateView, null);
        } else {
            Context context = parent.getContext();
            VH holder = this.onCreateViewHolder(context, parent, viewType);
            this.bindViewClickListener(holder, viewType);
            return holder;
        }
    }

    @Override
    public final void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        Intrinsics.checkNotNullParameter(holder, "holder");
        if (holder instanceof StateLayoutVH) {
            ((StateLayoutVH) holder).changeStateView(this.stateView);
        } else {
            this.onBindViewHolder((VH) holder, position, this.getItem(position));
        }
    }

    @Override
    public final void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position, @NonNull List payloads) {
        Intrinsics.checkNotNullParameter(holder, "holder");
        Intrinsics.checkNotNullParameter(payloads, "payloads");
        if (payloads.isEmpty()) {
            this.onBindViewHolder(holder, position);
        } else if (holder instanceof StateLayoutVH) {
            ((StateLayoutVH) holder).changeStateView(this.stateView);
        } else {
            this.onBindViewHolder((VH) holder, position, this.getItem(position), payloads);
        }
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    /**
     * If the hold view use StaggeredGridLayoutManager they should using all span area.
     * 如果 ViewHolder 使用 StaggeredGridLayoutManager 布局,则铺满一行。
     */
    private void asStaggeredGridFullSpan(RecyclerView.ViewHolder holder) {
        ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
        if (layoutParams instanceof StaggeredGridLayoutManager.LayoutParams) {
            ((StaggeredGridLayoutManager.LayoutParams) layoutParams).setFullSpan(true);
        }
    }

    /**
     * 当 ViewHolder 视图已附加到窗口时调用。
     * Called when a view created by this holder has been attached to a window.
     * simple to solve item will layout using all
     * [asStaggeredGridFullSpan]
     *
     * @param holder
     */
    @CallSuper
    public void onViewAttachedToWindow(@NonNull RecyclerView.ViewHolder holder) {
        Intrinsics.checkNotNullParameter(holder, "holder");
        super.onViewAttachedToWindow(holder);
        if ((holder instanceof StateLayoutVH) || this.isFullSpanItem(this.getItemViewType(holder.getAdapterPosition()))) {
            asStaggeredGridFullSpan(holder);
        } else {
            runAnimator(holder);
        }

        List<OnViewAttachStateChangeListener> onViewAttachStateChangeListeners = this.mOnViewAttachStateChangeListeners;
        int size;
        if (onViewAttachStateChangeListeners != null && (size = onViewAttachStateChangeListeners.size()) > 0) {
            for (int i = 0; i < size; i++) {
                onViewAttachStateChangeListeners.get(i).onViewAttachedToWindow(holder);
            }
        }

    }

    @CallSuper
    public void onViewDetachedFromWindow(@NonNull RecyclerView.ViewHolder holder) {

        List<OnViewAttachStateChangeListener> onViewAttachStateChangeListeners = this.mOnViewAttachStateChangeListeners;
        int size;
        if (onViewAttachStateChangeListeners != null && (size = onViewAttachStateChangeListeners.size()) > 0) {
            for (int i = 0; i < size; i++) {
                onViewAttachStateChangeListeners.get(i).onViewDetachedFromWindow(holder);
            }
        }

    }

    @CallSuper
    public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
        Intrinsics.checkNotNullParameter(recyclerView, "recyclerView");
        this._recyclerView = recyclerView;
    }

    @CallSuper
    public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
        Intrinsics.checkNotNullParameter(recyclerView, "recyclerView");
        this._recyclerView = null;
    }

    protected void bindViewClickListener(VH viewHolder, int viewType) {
        if (mOnItemClickListener != null) {
            viewHolder.itemView.setOnClickListener(v -> {
                int position = viewHolder.getAdapterPosition();
                if (position == RecyclerView.NO_POSITION) {
                    return;
                }
                onItemClick(v, position);
            });
        }

        if (this.mOnItemLongClickListener != null) {
            viewHolder.itemView.setOnLongClickListener(v -> {
                int position = viewHolder.getAdapterPosition();
                if (position == RecyclerView.NO_POSITION) {
                    return false;
                }
                return onItemLongClick(v, position);
            });
        }
        int childClickSize;
        if (mOnItemChildClickArray != null && (childClickSize = mOnItemChildClickArray.size()) > 0) {
            int id;
            View childView;
            for (int i = 0; i < childClickSize; i++) {
                id = mOnItemChildClickArray.keyAt(i);
                childView = viewHolder.itemView.findViewById(id);
                if (childView != null) {
                    childView.setOnClickListener(v -> {
                        int position = viewHolder.getAdapterPosition();
                        if (position == RecyclerView.NO_POSITION) {
                            return;
                        }
                        onItemChildClick(v, position);
                    });
                }
            }
        }

        if (mOnItemChildLongClickArray != null && (childClickSize = mOnItemChildLongClickArray.size()) > 0) {
            int id;
            View childView;
            for (int i = 0; i < childClickSize; i++) {
                id = mOnItemChildLongClickArray.keyAt(i);
                childView = viewHolder.itemView.findViewById(id);
                if (childView != null) {
                    childView.setOnLongClickListener(v -> {
                        int position = viewHolder.getAdapterPosition();
                        if (position == RecyclerView.NO_POSITION) {
                            return false;
                        }
                        return onItemChildLongClick(v, position);
                    });
                }
            }
        }
    }

    /**
     * override this method if you want to override click event logic
     * <p>
     * 如果你想重新实现 item 点击事件逻辑,请重写此方法
     *
     * @param v
     * @param position
     */
    protected void onItemClick(@NonNull View v, int position) {
        Intrinsics.checkNotNullParameter(v, "v");
        OnItemClickListener<T> onItemClickListener = this.mOnItemClickListener;
        if (onItemClickListener != null) {
            onItemClickListener.onClick(this, v, position);
        }

    }

    /**
     * override this method if you want to override longClick event logic
     * <p>
     * 如果你想重新实现 item 长按事件逻辑,请重写此方法
     *
     * @param v
     * @param position
     * @return
     */
    protected boolean onItemLongClick(@NonNull View v, int position) {
        Intrinsics.checkNotNullParameter(v, "v");
        OnItemLongClickListener<T> onItemLongClickListener = this.mOnItemLongClickListener;
        return onItemLongClickListener != null && onItemLongClickListener.onLongClick(this, v, position);
    }

    protected void onItemChildClick(@NonNull View v, int position) {
        Intrinsics.checkNotNullParameter(v, "v");
        SparseArray<OnItemChildClickListener<T>> onItemChildClickArray = this.mOnItemChildClickArray;
        if (onItemChildClickArray != null) {
            OnItemChildClickListener<T> onItemChildClickListener = onItemChildClickArray.get(v.getId());
            if (onItemChildClickListener != null) {
                onItemChildClickListener.onItemClick(this, v, position);
            }
        }

    }

    protected boolean onItemChildLongClick(@NonNull View v, int position) {
        Intrinsics.checkNotNullParameter(v, "v");
        SparseArray<OnItemChildLongClickListener<T>> onItemChildLongClickArray = this.mOnItemChildLongClickArray;
        boolean consumed = false;
        if (onItemChildLongClickArray != null) {
            OnItemChildLongClickListener<T> onItemChildLongClickListener = onItemChildLongClickArray.get(v.getId());
            if (onItemChildLongClickListener != null) {
                consumed = onItemChildLongClickListener.onItemLongClick(this, v, position);
            }
        }

        return consumed;
    }

    /**
     * Is full span item
     * 是否是完整跨度的item
     *
     * @param itemType
     * @return
     */
    public boolean isFullSpanItem(int itemType) {
        return itemType == EMPTY_VIEW;
    }

    public final T getItem(@IntRange(from = 0L) int position) {
        if (position >= 0 && position < items.size()) {
            return items.get(position);
        }
        return null;
    }

    public final void setStateViewLayout(@NonNull Context context, @LayoutRes int layoutResId) {
        Intrinsics.checkNotNullParameter(context, "context");
        this.setStateView(LayoutInflater.from(context).inflate(layoutResId, (ViewGroup) (new FrameLayout(context)), false));
    }

    public boolean displayEmptyView() {
        return displayEmptyView(items);
    }

    public boolean displayEmptyView(List<T> items) {
        if (this.stateView == null || !isStateViewEnable) return false;
        return items == null || items.isEmpty();
    }

    private final void runAnimator(RecyclerView.ViewHolder holder) {
//        if (this.animationEnable && (!this.isAnimationFirstOnly || holder.getLayoutPosition() > this.mLastPosition)) {
//            ItemAnimator animation = this.itemAnimation;
//            if (animation == null) {
//                animation = (ItemAnimator)(new AlphaInAnimation(0L, 0.0F));
//            }
//
//            View itemView = holder.itemView;
//            Animator var3 = animation.animator(itemView);
//            this.startItemAnimator(var3, holder);
//            this.mLastPosition = holder.getLayoutPosition();
//        }
    }

//    /**
//     * start executing animation
//     * override this method to execute more actions
//     * 开始执行动画方法
//     * 可以重写此方法,实行更多行为
//     *
//     * @param anim
//     * @param holder
//     */
//    protected void startItemAnimator(Animator anim , RecyclerView.ViewHolder holder) {
//        anim.start();
//    }

    public void submitList(List<T> list) {
        List<T> newList;
        if (list == null) {
            newList = new ArrayList<>();
        } else {
            newList = list;
        }
        mLastPosition = -1;

        boolean oldDisplayEmptyLayout = displayEmptyView();
        boolean newDisplayEmptyLayout = displayEmptyView(newList);
        if (oldDisplayEmptyLayout && !newDisplayEmptyLayout) {
            items = newList;
            notifyItemRemoved(0);
            notifyItemRangeInserted(0, newList.size());
        } else if (newDisplayEmptyLayout && !oldDisplayEmptyLayout) {
            notifyItemRangeRemoved(0, items.size());
            items = newList;
            notifyItemInserted(0);
        } else if (oldDisplayEmptyLayout && newDisplayEmptyLayout) {
            items = newList;
            notifyItemChanged(0, EMPTY_PAYLOAD);
        } else {
            items = newList;
            notifyDataSetChanged();
        }
    }

    /**
     * change data
     * 改变某一位置数据
     */
    public void set(@IntRange(from = 0) int position, T data) {
        if (position >= items.size()) {
            throw new IndexOutOfBoundsException(String.format("position: %d. size:$%d", position, items.size()));
        }
        items.set(position, data);
        notifyItemChanged(position);
    }

    /**
     * add one new data in to certain location
     * 在指定位置添加一条新数据
     *
     * @param position
     */
    public void add(@IntRange(from = 0) int position, T data) {
        if (position > items.size() || position < 0) {
            throw new IndexOutOfBoundsException(String.format("position: %d. size:$%d", position, items.size()));
        }

        if (displayEmptyView()) {
            // 如果之前在显示空布局,需要先移除
            notifyItemRemoved(0);
        }
        items.add(position, data);
        notifyItemInserted(position);
    }

    /**
     * add one new data,not null.
     * 添加一条新数据,不可为 null。
     */
    public void add(T data) {
        if (displayEmptyView()) {
            // 如果之前在显示空布局,需要先移除
            notifyItemRemoved(0);
        }
        if (items.add(data)) {
            notifyItemInserted(items.size() - 1);
        }
    }

    /**
     * add new data in to certain location
     * 在指定位置添加数据
     *
     * @param position   the insert position
     * @param collection the new data collection
     */
    public void addAll(@IntRange(from = 0) int position, Collection<T> collection) {
        if (collection == null || collection.isEmpty()) return;

        if (position > items.size() || position < 0) {
            throw new IndexOutOfBoundsException(String.format("position: %d. size:$%d", position, items.size()));
        }

        if (displayEmptyView()) {
            // 如果之前在显示空布局,需要先移除
            notifyItemRemoved(0);
        }
        if (items.addAll(position, collection)) {
            notifyItemRangeInserted(position, collection.size());
        }
    }

    /**
     * 添加一组数据,不可为 null。
     */
    public void addAll(Collection<T> collection) {
        if (collection == null || collection.isEmpty()) return;

        if (displayEmptyView()) {
            // 如果之前在显示空布局,需要先移除
            notifyItemRemoved(0);
        }

        int oldSize = items.size();
        if (items.addAll(collection)) {
            notifyItemRangeInserted(oldSize, collection.size());
        }
    }

    /**
     * remove the item associated with the specified position of adapter
     * 删除指定位置的数据
     *
     * @param position
     */
    public T removeAt(@IntRange(from = 0) int position) {
        if (position >= items.size()) {
            throw new IndexOutOfBoundsException(String.format("position: %d. size:$%d", position, items.size()));
        }
        T removed = items.remove(position);
        notifyItemRemoved(position);

        // 处理空视图的情况
        if (displayEmptyView()) {
            notifyItemInserted(0);
        }
        return removed;
    }

    /**
     * 删除数据
     *
     * @param data
     */
    public void remove(T data) {
        int index = items.indexOf(data);
        if (index == -1) return;
        removeAt(index);
    }

    /**
     * Item swap
     * 数据位置交换。这里单纯的只是两个数据交换位置。(注意⚠️,这里移动后的数据顺序与 [move] 不同)
     *
     * @param fromPosition
     * @param toPosition
     */
    public void swap(int fromPosition, int toPosition) {
        int size;
        if (items == null || (size = items.size()) == 0) {
            return;
        }
        if (fromPosition < 0 || fromPosition >= size || toPosition < 0 || toPosition >= size) {
            return;
        }
        Collections.swap(items, fromPosition, toPosition);
        notifyItemChanged(fromPosition);
        notifyItemChanged(toPosition);
    }

    /**
     * Move Item
     * item 位置的移动。(注意⚠️,这里移动后的数据顺序与 [swap] 不同)
     *
     * @param fromPosition
     * @param toPosition
     */
    public void move(int fromPosition, int toPosition) {
        int size;
        if (items == null || (size = items.size()) == 0) {
            return;
        }
        if (fromPosition < 0 || fromPosition >= size || toPosition < 0 || toPosition >= size) {
            return;
        }
        T e = items.remove(fromPosition);
        items.add(toPosition, e);
        notifyItemMoved(fromPosition, toPosition);
    }

    /************************************** Set Listener ****************************************/

    public void setOnItemClickListener(OnItemClickListener<T> listener) {
        this.mOnItemClickListener = listener;
    }

    public OnItemClickListener<T> getOnItemClickListener() {
        return mOnItemClickListener;
    }

    public void setOnItemLongClickListener(OnItemLongClickListener<T> listener) {
        this.mOnItemLongClickListener = listener;
    }

    public OnItemLongClickListener<T> getOnItemLongClickListener() {
        return mOnItemLongClickListener;
    }

    public void addOnItemChildClickListener(@IdRes int id, OnItemChildClickListener<T> listener) {
        if (mOnItemChildClickArray == null) {
            mOnItemChildClickArray = new SparseArray<>(2);
        }
        mOnItemChildClickArray.put(id, listener);
    }

    public void removeOnItemChildClickListener(@IdRes int id) {
        if (mOnItemChildClickArray != null) {
            mOnItemChildClickArray.remove(id);
        }
    }

    public void addOnItemChildLongClickListener(@IdRes int id, OnItemChildLongClickListener<T> listener) {
        if (mOnItemChildLongClickArray == null) {
            mOnItemChildLongClickArray = new SparseArray<>(2);
        }
        mOnItemChildLongClickArray.put(id, listener);
    }

    public void removeOnItemChildLongClickListener(@IdRes int id) {
        if (mOnItemChildLongClickArray != null) {
            mOnItemChildLongClickArray.remove(id);
        }
    }

    public void addOnViewAttachStateChangeListener(OnViewAttachStateChangeListener listener) {
        if (mOnViewAttachStateChangeListeners == null) {
            mOnViewAttachStateChangeListeners = new ArrayList<>();
        }
        if (!mOnViewAttachStateChangeListeners.contains(listener)) {
            mOnViewAttachStateChangeListeners.add(listener);
        }
    }

    public void removeOnViewAttachStateChangeListener(OnViewAttachStateChangeListener listener) {
        if (mOnViewAttachStateChangeListeners != null) {
            mOnViewAttachStateChangeListeners.remove(listener);
        }
    }

    public void clearOnViewAttachStateChangeListener() {
        if (mOnViewAttachStateChangeListeners != null) {
            mOnViewAttachStateChangeListeners.clear();
        }
    }


    interface OnViewAttachStateChangeListener {

        /**
         * Called when a view is attached to the RecyclerView.
         */
        void onViewAttachedToWindow(RecyclerView.ViewHolder holder);

        /**
         * Called when a view is detached from RecyclerView.
         */
        void onViewDetachedFromWindow(RecyclerView.ViewHolder holder);
    }

    public interface OnItemClickListener<T extends Object> {
        void onClick(BaseQuickAdapter<T, ?> adapter, View view, int position);
    }

    public interface OnItemLongClickListener<T extends Object> {
        boolean onLongClick(BaseQuickAdapter<T, ?> adapter, View view, int position);
    }

    public interface OnItemChildClickListener<T extends Object> {
        void onItemClick(@NonNull BaseQuickAdapter<T, ?> adapter, @NonNull View view, int position);
    }

    public interface OnItemChildLongClickListener<T extends Object> {
        boolean onItemLongClick(BaseQuickAdapter<T, ?> adapter, View view, int position);
    }

}