Recycler上拉加载--适配GridLayoutManager和StaggeredGridLayoutManager

当Recycler中需要下拉刷新的时候。就想自己实现一下。

发现LinearLayoutManager时,体验完美。但是,到了GridLayoutManager和StaggeredGridLayoutManager问题就来了:


Recycler上拉加载--适配GridLayoutManager和StaggeredGridLayoutManager_第1张图片


在网上搜索了解决办法,还真有:

地址:http://blog.csdn.net/qibin0506/article/details/49716795


GridLayoutManager解决方法:

getSpanSize()  这个方法返回这个Item占几个空间,默认情况下占一个。

isFooter(position) 为true时,说明这是一个底部布局,返回gridManager.getSpanCount() 。标识占一整行。


    @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) {
                    return isFooter(position) ? gridManager.getSpanCount() : 1;
                }
            });
        }
    }


StaggeredGridLayoutManager解决方法:

StaggeredGridLayoutManager.LayoutParams为我们提供了一个setFullSpan方法来设置占领全部空间

 public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {
        super.onViewAttachedToWindow(holder);
        if (isStaggeredGridLayout(holder)) {
            handleLayoutIfStaggeredGridLayout(holder, holder.getLayoutPosition());
        }
    }

    private boolean isStaggeredGridLayout(RecyclerView.ViewHolder holder) {
        ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
        if (layoutParams != null && layoutParams instanceof StaggeredGridLayoutManager.LayoutParams) {
            return true;
        }
        return false;
    }

    protected void handleLayoutIfStaggeredGridLayout(RecyclerView.ViewHolder holder, int position) {
        if (isHeader(position) || isFooter(position)) {
            StaggeredGridLayoutManager.LayoutParams p = (StaggeredGridLayoutManager.LayoutParams) holder.itemView.getLayoutParams();
            p.setFullSpan(true);
        }
    }

解决后截图:

Recycler上拉加载--适配GridLayoutManager和StaggeredGridLayoutManager_第2张图片


============================================================================

下面看实现:

很多上拉加载,都封装进了RecyclerView和LayoutManager。

在XML中引用自定义的RecyclerView就可以得到上拉加载功能。

我这个是将上拉加载扩展到了Adapter 和  OnScrollListener。

这样,xml不需要修改,还是使用原生的控件,只需要设置Adapter和OnScrollListener

就可以实现上拉加载。RecyclerView和LayoutManager都使用系统默认的就ok!!!


自定义一个Adapter:ExAdapter,扩展上拉加载功能:

主要方法如下:

   @Override
    public final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (hasHeader() && viewType == TYPE_HEADER) {
            View v = LayoutInflater.from(parent.getContext()).inflate(getHeaderView(), parent, false);
            return new HeaderViewHolder(v);
        } else if (hasFooter() && viewType == TYPE_FOOTER) {
            View v = LayoutInflater.from(parent.getContext()).inflate(getFooterView(), parent, false);
            return new FooterViewHolder(v);
        } else {
            return onCreateHolder(parent, viewType);
        }
    }

    /**
     * 子类需要使用onCreateHolder 来替代onCreateViewHolder
     *
     * @param parent
     * @param viewType
     * @return
     */
    protected abstract RecyclerView.ViewHolder onCreateHolder(ViewGroup parent, int viewType);

    @Override
    public final void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        if (getItemViewType(position) == TYPE_HEADER) {
            HeaderViewHolder headerHolder = (HeaderViewHolder) holder;
            View headerView = headerHolder.itemView;
            onBindHeaderView(headerView);
        } else if (getItemViewType(position) == TYPE_FOOTER) {
            FooterViewHolder footerHolder = (FooterViewHolder) holder;
            View footerView = footerHolder.itemView;
            onBindFooterView(footerView);
        } else {
            onBindItemView(holder, hasHeader() ? position - 1 : position);
        }
    }

    /**
     * 子类需要使用onBindItemView 来替代onBindViewHolder
     */
    protected abstract void onBindItemView(RecyclerView.ViewHolder holder, int position);
  @Override
    public final int getItemViewType(int position) {
        int size = getCount();
        if (hasHeader()) {
            if (position == 0) {
                return TYPE_HEADER; // 有头布局,并且位置时0.返回头布局类型
            } else {
                if (position == size + 1) {
                    return TYPE_FOOTER;  // 当布局位置为  size + 1   说明有头布局,并且 这时的布局为底部布局
                } else {
                    return getViewType(position - 1); // 有头布局时,position自动减一
                }
            }
        } else {
            if (position == size) {  // 没有头布局时,size为position说明这时为底部布局
                return TYPE_FOOTER;
            } else {
                return getViewType(position);// 没有头布局时,position不变
            }
        }
    }

    /**
     * 子类需要使用getViewType 来替代getItemViewType
     * 默认情况下返回0.
     * -1  与  -2  已经被头布局 和 底部布局使用。不要返回-1  和  -2
     *
     * @param position
     * @return
     */
    protected int getViewType(int position) {
        return 0;
    }

 @Override
    public final int getItemCount() {
        if (visibleItemCount != -1 && visibleItemCount < getCount()) {
            // 不是初始化的时候   &&    Item数量要大于可见元素数量
            setShowFooterView(true);
        } else {
            setShowFooterView(false);
        }
        int count = 0;
        count += (hasHeader() ? 1 : 0);
        count += (hasFooter() ? 1 : 0);
        count += getCount();
        return count;
    }

    /**
     * 子类需要使用getCount 来替代getItemCount
     *
     * @return
     */
    protected abstract int getCount();


添加了GridLayoutManager的上拉加载:

/**
 * 网格布局时,使用这个类来替代Adapter
 * Created by wp on 16-3-10.
 */
public abstract class ExAdapterGridLayout extends ExAdapter {
    /**
     * 定义一个内部类GridSpanSizeLookup继承GridLayoutManager.SpanSizeLookup,
     * 调用父类isHeader和isFooter方法判断是否是头或者尾,
     * 如果是则返回gridManager.getSpanCount();
     * 即一个item占据一行的span数,否则就返回1
     *
     * @param recyclerView
     */
    @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) {
                    return isFooter(position) ? gridManager.getSpanCount() : 1;
                }
            });
        }
    }
}

添加StaggeredGridLayout 的上拉加载:


/**
 * 瀑布流时使用这个类来代替Adapter类
 * Created by wp on 16-3-10.
 */
public abstract class ExAdapterStaggeredGridLayout extends ExAdapter {

    /**
     * StaggeredGridLayoutManager中没有setSpanSizeLookup方法,
     * 庆幸的是StaggeredGridLayoutManager.LayoutParams中有setFullSpan方法可以达到同样的效果。
     * 这时候重写的不再是onAttachedToRecyclerView方法而是onViewAttachedToWindow方法
     *
     * @param holder
     */
    public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {
        super.onViewAttachedToWindow(holder);
        if (isStaggeredGridLayout(holder)) {
            handleLayoutIfStaggeredGridLayout(holder, holder.getLayoutPosition());
        }
    }

    private boolean isStaggeredGridLayout(RecyclerView.ViewHolder holder) {
        ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
        if (layoutParams != null && layoutParams instanceof StaggeredGridLayoutManager.LayoutParams) {
            return true;
        }
        return false;
    }

    protected void handleLayoutIfStaggeredGridLayout(RecyclerView.ViewHolder holder, int position) {
        if (isHeader(position) || isFooter(position)) {
            StaggeredGridLayoutManager.LayoutParams p = (StaggeredGridLayoutManager.LayoutParams) holder.itemView.getLayoutParams();
            p.setFullSpan(true);
        }
    }
}


继承RecyclerView.OnScrollListener的类:ExScrollListener

主要方法如下:

  /**
     * 当recyclerView滚动时调用
     *
     * @param recyclerView
     * @param dx
     * @param dy
     */
    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);
        layoutManager = recyclerView.getLayoutManager();
        if (mLayoutManagerType == null) {
            if (layoutManager instanceof LinearLayoutManager) {
                mLayoutManagerType = layoutManagerType.LINEAR_LAYOUT;
            } else if (layoutManager instanceof GridLayoutManager) {
                mLayoutManagerType = layoutManagerType.GRID_LAYOUT;
            } else if (layoutManager instanceof StaggeredGridLayoutManager) {
                mLayoutManagerType = layoutManagerType.STAGGERED_GRID_LAYOUT;
            } else {
                throw new RuntimeException("不支持的layoutManager");
            }
        }
        switch (mLayoutManagerType) {
            case LINEAR_LAYOUT:
                lastVisibleItemPosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();
                break;
            case GRID_LAYOUT:
                lastVisibleItemPosition = ((GridLayoutManager) layoutManager).findLastVisibleItemPosition();
                break;
            case STAGGERED_GRID_LAYOUT:
                StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager;
                if (lastPositions == null) {
                    lastPositions = new int[staggeredGridLayoutManager.getSpanCount()];
                }
                staggeredGridLayoutManager.findLastVisibleItemPositions(lastPositions);
                lastVisibleItemPosition = findMax(lastPositions);
                break;
            default:
                break;
        }
        if (exAdapter == null && recyclerView.getAdapter() instanceof ExAdapter) {
            exAdapter = (ExAdapter) recyclerView.getAdapter();
        }
        if (exAdapter != null && layoutManager != null)
            exAdapter.setVisibleItemCount(layoutManager.getChildCount());
    }

    /**
     * 滚动状态改变时调用
     *
     * @param recyclerView
     * @param newState
     */
    @Override
    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
        super.onScrollStateChanged(recyclerView, newState);
        //正在滚动时回调,回调2-3次,手指没抛则回调2次。scrollState = 2的这次不回调
        //回调顺序如下
        //第1次:scrollState = SCROLL_STATE_TOUCH_SCROLL(1) 正在滚动
        //第2次:scrollState = SCROLL_STATE_FLING(2) 手指做了抛的动作(手指离开屏幕前,用力滑了一下)
        //第3次:scrollState = SCROLL_STATE_IDLE(0) 停止滚动
        //当屏幕停止滚动时为0;当屏幕滚动且用户使用的触碰或手指还在屏幕上时为1;
        //由于用户的操作,屏幕产生惯性滑动时为2

        if (layoutManager == null)
            return;

        // 没有处于加载状态
        if (newState == RecyclerView.SCROLL_STATE_IDLE//当滚动停止时。判断
                && layoutManager.getChildCount() > 0 // 界面上可见的数量  > 0
                && lastVisibleItemPosition >= layoutManager.getItemCount() - 1 // 到达了最后一个条目
                && !noMoreData // 有更多数据
                && !loading // 没有正在加载数据
                ) {
            loading = true;
            tempTotalItemCount = layoutManager.getItemCount(); //  将Item的总数 赋值给previousTotal。用来判断是否请求到数据。
            onLoadMore();
            // Logger.w("可见的Item数量是:" + layoutManager.getChildCount() + "\n当前的Item数量时:" + layoutManager.getItemCount());
        }
    }

再设置两个方法,用来控制数据加载完毕,或者没有更多数据后的重置操作:

    /**
     * 上拉加载完成,调用此方法,
     * 会通知ScrollListener上拉加载完成,可以进行下一次的上拉加载。
     */
    public void loadMoreComplete() {
        loading = false;
        if (layoutManager == null)
            return;
        int count = layoutManager.getItemCount();
        if (count == tempTotalItemCount) {
            noMoreData = true; // 当加载后的数据 等于 之前数据时,证明什么页没有加载出来
        } else {
            noMoreData = false;
        }
        if (exAdapter != null)
            exAdapter.setNoMoreData(noMoreData);
    }

    /**
     * 重置没有数据状态。将字段变为true。
     * 当没有数据时,就算上拉也不会进行加载。
     * 这个方法可以清楚这个标识,让上拉加载重新启用
     */
    public void resetLoadMore() {
        noMoreData = false;
        tempTotalItemCount = -1;
        if (exAdapter != null)
            exAdapter.setNoMoreData(noMoreData);
    }


// ==================================================

在Activity中使用:


    private void initRecycler(Activity activity) {
        adapter = new ItemAdapter(activity); //ItemAdapter继承了 ExAdapter
        adapter.showLoadMore(); // 设置显示“上拉加载”功能
        scrollListener = new ExScrollListener() {
            @Override
            protected void onLoadMore() {
                // 上拉加载回调
            }
        };
        // setLayoutManager() 方法要在setAdapter()方法之前调用。不然会出问题
        viewHolder.tab1Recycler.setLayoutManager(new LinearLayoutManager(activity));
        viewHolder.tab1Recycler.setAdapter(adapter);
        viewHolder.tab1Recycler.addOnScrollListener(scrollListener);
    }









你可能感兴趣的:(教程,android,上拉加载,RecyclerView)