自定义LayoutManager的详解及其使用
来源:互联网 发布:企业网站关键词优化 编辑:程序博客网 时间:2024/06/07 02:00
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代表每次的增长值,返回值是真实移动的距离
下面贴出代码
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就可以实现各种我们所想要的效果了!
要实现自定义LayoutManager,首先实现generateDefaultLayoutParams()方法给child添加默认的LayoutParams,在onLayoutChildren这个方法里面首先detach掉界面上的view缓存到scrap里面,然后重新进行布局,调用getViewForPosition取出缓存的view,添加到RecyclerView里面并测量,接着把它的边界信息设置为我们所想要的样子,通过layoutDecorated()方法把需要显示的子view布局到界面上。
如果需要滑动,把canScrollVertically()和canScrollHorizontally()按需返回true,重写scrollVerticallyBy()或者scrollHorizontallyBy()方法,这两个方法需要返回真实的偏移距离,返回的dx或者dy可能并不是真实的移动距离,因为当滑动到边缘的时候真实移动距离可能就不是dx或者dy,所以在这个时候需要判断处理,移动就调用offsetChildrenHorizontal()或者offsetChildrenVertical()实现,然后重新布局到界面上就可以实现。
Demo下载
- 自定义LayoutManager的详解及其使用
- 自定义LayoutManager
- LayoutManager自定义
- android RecyclerView自定义 LayoutManager
- Recyclerview-自定义LayoutManager
- 利用自定义xml属性指定来RecyclerView的LayoutManager
- UIAlertController的使用及其自定义
- “傻瓜”式填充,自定义LayoutManager
- RecylerView 自定义 LayoutManager 基础一
- RecylerView 自定义 LayoutManager 基础二
- LayoutManager
- Clang-Format的使用及其自定义格式
- 自定义cell及其使用
- okhttp的详解及其缓存的使用
- RecyclerView自定义LayoutManager,打造不规则布局
- RecyclerView——实现自定义LayoutManager
- RecyclerView自定义LayoutManager实现横向瀑布流
- 自定义LayoutManager带你撸个LinearLayoutManager
- Swift利用闭包(closure)来实现传值-->前后两个控制器的反向传值
- iOS - 视图圆角
- 接口app简单封装(第一章)
- GRUB2配置文件"grub.cfg"详解(GRUB2实战手册)
- 优秀博客网站
- 自定义LayoutManager的详解及其使用
- hadoop----eclipse导入hadoop源码
- 欢迎使用CSDN-markdown编辑器
- app接口开发整理(1)
- Redis并发性能测试
- 正则表达式
- 涂涂乐的详细实现之二--UI布局和效果
- 16.js的执行环境和作用域
- Hadoop-2.7.0 源码导入