Android DataBindingベースの汎用RecyclerView Adapter

22885 ワード

宣言:
この構造は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ダウンロード