layoutmanager怎么判断可见item数

 我来答
灬盖一板混朵1X
推荐于2018-03-13 · TA获得超过1305个赞
知道小有建树答主
回答量:789
采纳率:12%
帮助的人:292万
展开全部
RecyclerView不断的普及,越来越多的人使用来代替传统的ListView,GridView等,为了跟进时代也要不断的学习RecyclerView的相关知识,下面就来了解一下RecyclerView的LayoutManger。
Recycler
RecyclerView内部有一个Recycler,顾名思义它就是一个回收的工具,当定义LayoutManager时,它可以访问到一个Recycler的实例,从而用于来回收或者获取View。当需要一个新的view时,调用getViewForPosition()这个方法,它会返回一个View,这个View可能是之前Recycler回收的View再利用,也可能是一个新的View。
两级缓存机制
Scrap和Recycle
在RecyclerView中有两级缓存机制:Scrap和Recycle。
Scrap Heap(垃圾堆)是一个轻量的集合,View不会经过适配器而是直接返回给LayoutManager,当需要一个View时首先回去Scrap缓存里面找有没有所需要的View,而这里面的View已经绑定了需要的数据所以无需适配直接使用。
Recycle Pool(回收池)这里面回收的View如果再次使用需要重新经过适配器绑定数据,即调用onBindViewHolder()进行绑定数据,当然如果Recycle Pool里面也没有View就只有重新创建View。
Detach和Remove
我们可以通过Detach和Remove决定把View缓存在Recycle或者Scrap。
使用Detach是把View缓存在Scrap,这种缓存方式可以方便如果还需要把缓存的View添加进来的场景,可以明显提高效率,可以调用detachAndScrapView()方法来实现。
Remove就是把View移除掉,放到Recycle里面,以备后面的再次利用,调用方法removeAndRecycleView()实现。
具体采取何种方法还是要根据你具体的需求来调用。

下面写个具体例子来实现如下效果





当然做法就是写一个类来继承RecyclerView.LayoutManager
首先看看几个重要的方法


generateDefaultLayoutParams()
这是一个必须重写的方法,当然仅仅实现这个方法不行,虽然能编译通过。这个方法是给RecyclerView的子View创建一个默认的LayoutParams,实现起来也十分简单。

onLayoutChildren
这个方法显然是用于放置子view的位置,十分重要的一个方法。


canScrollVertically()和canScrollHorizontally()
若想要RecyclerView能水平或者竖直滚动这两个方法需要重写返回true


scrollVerticallyBy()和scrollHorizontallyBy()
在水平或者竖直滚动时会分别调用这两个方法,dx,dy代表每次的增长值,返回值是真实移动的距离

下面贴出代码

[java] view plain copy 
package com.lzy.lzy_layoutmanager;  
  
import android.graphics.Rect;  
import android.support.v4.util.SparseArrayCompat;  
import android.support.v7.widget.RecyclerView;  
import android.view.View;  
import android.view.ViewGroup;  
  
/** 
 * Created by lzy on 2016/10/18. 
 */  
public class NyLayoutManager extends RecyclerView.LayoutManager {  
  
    private static final String TAG = "lzy";  
    //保存所有item的偏移信息  
    private SparseArrayCompat<Rect> itemFrames = new SparseArrayCompat<>();  
    //总的高度和宽度  
    private int mTotalHeight;  
    private int mTotalWidth;  
  
    private int verticalOffset;//竖直方向的偏移  
    private int horizontalOffset;//水平方向的偏移  
  
    @Override  
    public RecyclerView.LayoutParams generateDefaultLayoutParams() {  
        return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,  
                ViewGroup.LayoutParams.WRAP_CONTENT);  
    }  
  
  
    @Override  
    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {  
        if (getItemCount() <= 0) {  
            return;  
        }  
        detachAndScrapAttachedViews(recycler);  
  
        int totalHeight = 0;  
        int totalWidth = 0;  
        int offsetX = 0;  
        int offsetY = 0;  
        //计算每个item的位置信息,存储在itemFrames里面  
        for (int i = 0; i < getItemCount(); i++) {  
            //从缓存中取出  
            View view = recycler.getViewForPosition(i);  
            //添加到RecyclerView中  
            addView(view);  
            //测量  
            measureChildWithMargins(view, 0, 0);  
            //获取测量后的宽高  
            int height = getDecoratedMeasuredHeight(view);  
            int width = getDecoratedMeasuredWidth(view);  
            //把每一个子View的宽高加起来获得总的  
            totalHeight += height;  
            totalWidth += width;  
            //边界信息保存到Rect里面  
            Rect rect = itemFrames.get(i);  
            if (rect == null) {  
                rect = new Rect();  
            }  
  
            rect.set(offsetX, offsetY, offsetX + width, offsetY + height);  
            itemFrames.put(i, rect);  
            //横竖方向的偏移  
            offsetX += width;  
            offsetY += height;  
        }  
        mTotalHeight = Math.max(totalHeight, getVerticalSpace());  
        mTotalWidth = Math.max(totalWidth, getHorizontalSpace());  
  
        fill(recycler, state);  
  
    }  
  
    //回收不必要的view(超出屏幕的),取出需要的显示出来  
    private void fill(RecyclerView.Recycler recycler, RecyclerView.State state) {  
        //获得屏幕的边界信息  
        Rect displayFrame = new Rect(horizontalOffset, verticalOffset, horizontalOffset + getHorizontalSpace(),  
                verticalOffset + getVerticalSpace());  
  
        //滑出屏幕回收到缓存中  
        Rect childFrame = new Rect();  
        for (int i = 0; i < getChildCount(); i++) {  
            View view = getChildAt(i);  
            childFrame.left = getDecoratedLeft(view);  
            childFrame.top = getDecoratedTop(view);  
            childFrame.right = getDecoratedRight(view);  
            childFrame.bottom = getDecoratedBottom(view);  
            //判断是否在显示区域里面  
            if (!Rect.intersects(displayFrame, childFrame)) {  
                removeAndRecycleView(view, recycler);  
            }  
        }  
        //在屏幕上显示出  
        for (int i = 0; i < getItemCount(); i++) {  
            if (Rect.intersects(displayFrame, itemFrames.get(i))) {//判断是否在屏幕中  
                View view = recycler.getViewForPosition(i);  
                measureChildWithMargins(view, 0, 0);  
                addView(view);  
                Rect rect = itemFrames.get(i);  
                layoutDecorated(view, rect.left - horizontalOffset, rect.top - verticalOffset,  
                        rect.right - horizontalOffset, rect.bottom - verticalOffset);  
            }  
        }  
  
  
    }  
  
    @Override  
    public boolean canScrollVertically() {  
        return true;  
    }  
  
    @Override  
    public boolean canScrollHorizontally() {  
        return true;  
    }  
  
  
    @Override  
    public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {  
        detachAndScrapAttachedViews(recycler);  
        if (verticalOffset + dy < 0) {//滑动到最顶部  
            dy = -verticalOffset;  
        } else if (verticalOffset + dy > mTotalHeight - getVerticalSpace()) {//滑动到底部  
            dy = mTotalHeight - getVerticalSpace() - verticalOffset;  
        }  
  
        offsetChildrenVertical(-dy);  
        fill(recycler, state);  
        verticalOffset += dy;  
        return dy;  
    }  
  
    @Override  
    public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {  
        detachAndScrapAttachedViews(recycler);  
        if (horizontalOffset + dx < 0) {//滑动到最左边  
            dx = -horizontalOffset;  
        } else if (horizontalOffset + dx > mTotalWidth - getHorizontalSpace()) {//滑动到最右边  
            dx = mTotalWidth - getHorizontalSpace() - horizontalOffset;  
        }  
  
        offsetChildrenHorizontal(-dx);  
        fill(recycler, state);  
        horizontalOffset += dx;  
        return dx;  
    }  
  
    //获取控件的竖直高度  
    private int getVerticalSpace() {  
        return getHeight() - getPaddingBottom() - getPaddingTop();  
    }  
  
    //获取控件的水平宽度  
    private int getHorizontalSpace() {  
        return getWidth() - getPaddingLeft() - getPaddingRight();  
    }  
}  

代码中都有注释就不多说了。所以我们通过自定义的LayoutManager就可以实现各种我们所想要的效果了!
网易云信
2023-12-06 广告
UIkit是一套轻量级、模块化且易于使用的开源UI组件库,由YOOtheme团队开发。它提供了丰富的界面元素,包括按钮、表单、表格、对话框、滑块、下拉菜单、选项卡等等,适用于各种类型的网站和应用程序。UIkit还支持响应式设计,可以根据不同... 点击进入详情页
本回答由网易云信提供
推荐律师服务: 若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询

为你推荐:

下载百度知道APP,抢鲜体验
使用百度知道APP,立即抢鲜体验。你的手机镜头里或许有别人想知道的答案。
扫描二维码下载
×

类别

我们会通过消息、邮箱等方式尽快将举报结果通知您。

说明

0/200

提交
取消

辅 助

模 式