为什么要使用vlayout

[vlayout]是对RecyclerView的LayoutManage的扩展,所以它接管了RecyclerView的整个布局方式,可以通过它实现各种各样的布局方式,非常的灵活。它提供的默认布局方式解耦所有的View和布局之间的关系: Linear, Grid, 吸顶, 浮动, 固定位置等,大致分为两类:一是非fix类型布局,像线性、Grid、栏格等,它们的特点是布局在整个页面流里,随页面滚动而滚动;另一类就是fix类型的布局,它们的子节点往往不随页面滚动而滚动。除了这个,它用来管理大的模块布局组合,使得同一RecyclerView内的组件可以复用,减少View的创建和销毁过程

通过demo学习

Paste_Image.png

上面这张图是通过运行vlayout提供的demo得到的效果图,看起来挺多view,也挺复杂的,很难相信这只是一个RecyclerView,是不是也间接证明了vLayout的强大。

下面来拆解下这么复杂的布局是如何在一个RecyclerView里实现的。

开始使用

基本用法

final VirtualLayoutManager layoutManager = new VirtualLayoutManager(this);

recyclerView.setLayoutManager(layoutManager);

final DelegateAdapter delegateAdapter = new DelegateAdapter(layoutManager, true);

recyclerView.setAdapter(delegateAdapter);

final List adapters = new LinkedList<>();

//需要添加新的布局,就定义一个新的adapter

delegateAdapter.setAdapters(adapters);

需要定义DelegateAdapter.Adapter的子类,如下

static class SubAdapter extends DelegateAdapter.Adapter {

private Context mContext;

private LayoutHelper mLayoutHelper;

private LayoutParams mLayoutParams;

private int mCount = 0;

public SubAdapter(Context context, LayoutHelper layoutHelper, int count) {

this(context, layoutHelper, count, new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 300));

}

public SubAdapter(Context context, LayoutHelper layoutHelper, int count, @NonNull LayoutParams layoutParams) {

this.mContext = context;

this.mLayoutHelper = layoutHelper;

this.mCount = count;

this.mLayoutParams = layoutParams;

}

@Override

public LayoutHelper onCreateLayoutHelper() {

return mLayoutHelper;

}

@Override

public MainViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

return new MainViewHolder(LayoutInflater.from(mContext).inflate(R.layout.item, parent, false));

}

@Override

public void onBindViewHolder(MainViewHolder holder, int position) {

// only vertical

holder.itemView.setLayoutParams(

new LayoutParams(mLayoutParams));

}

@Override

protected void onBindViewHolderWithOffset(MainViewHolder holder, int position, int offsetTotal) {

((TextView) holder.itemView.findViewById(R.id.title)).setText(Integer.toString(offsetTotal));

}

@Override

public int getItemCount() {

return mCount;

}

}

添加一个普通的布局:adapter

adapters.add(new SubAdapter(this, new LinearLayoutHelper(), 1) {

@Override

public void onViewRecycled(MainViewHolder holder) {

if (holder.itemView instanceof ViewPager) {

((ViewPager) holder.itemView).setAdapter(null);

}

}

@Override

public MainViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

if (viewType == 1)

return new MainViewHolder(

LayoutInflater.from(VLayoutActivity.this).inflate(R.layout.view_pager, parent, false));

return super.onCreateViewHolder(parent, viewType);

}

@Override

public int getItemViewType(int position) {

return 1;

}

@Override

protected void onBindViewHolderWithOffset(MainViewHolder holder, int position, int offsetTotal) {

}

@Override

public void onBindViewHolder(MainViewHolder holder, int position) {

if (holder.itemView instanceof ViewPager) {

ViewPager viewPager = (ViewPager) holder.itemView;

viewPager.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 200));

// from position to get adapter

viewPager.setAdapter(new PagerAdapter(this, viewPool));

}

}

});

看下效果图

Paste_Image.png

添加一个悬浮的布局:adapter

FloatLayoutHelper layoutHelper = new FloatLayoutHelper();

layoutHelper.setAlignType(FixLayoutHelper.BOTTOM_LEFT);

layoutHelper.setDefaultLocation(100, 400);

LayoutParams layoutParams = new LayoutParams(150, 150);

adapters.add(new SubAdapter(this, layoutHelper, 1, layoutParams));

效果图:

Paste_Image.png

分析:

FloatLayoutHelper是悬浮布局的实现类

setAlignType 有四种: TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT

setDefaultLocation: x, y 代表与边界的距离,和alignType有关系,比如:TOP_LEFT, x 是与左边的距离,y是与上边的距离

可以随手势拖动

添加 宽比高为2的布局

LinearLayoutHelper layoutHelper1 = new LinearLayoutHelper();

layoutHelper1.setAspectRatio(2.0f);

adapters.add(new SubAdapter(this, layoutHelper1, 1));

效果图:

Paste_Image.png

分析:

setAspectRatio(2.0f)设置宽比高比例为2的布局

添加子列表

LinearLayoutHelper layoutHelper2 = new LinearLayoutHelper();

layoutHelper2.setAspectRatio(4.0f);

layoutHelper2.setDividerHeight(10);

layoutHelper2.setMargin(10, 30, 10, 10);

layoutHelper2.setPadding(10, 30, 10, 10);

layoutHelper2.setBgColor(0xFFF5A623);

adapters.add(new SubAdapter(this, layoutHelper2, 6) {

@Override

public void onBindViewHolder(MainViewHolder holder, int position) {

if (position % 2 == 0) {

LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 300);

layoutParams.mAspectRatio = 5;

holder.itemView.setLayoutParams(layoutParams);

}

}

});

Paste_Image.png

分析:

我们看到的是一个垂直方向的列表,如果想要变成一个水平方向上的列表,需要修改VirtualLayoutManager的orientation

layoutManager.setOrientation(VirtualLayoutManager.HORIZONTAL);

效果图:

Paste_Image.png

添加一个sticky布局,黏住但不固定,手势之后会变化

StickyLayoutHelper layoutHelper = new StickyLayoutHelper();

layoutHelper.setOffset(100);

layoutHelper.setAspectRatio(4);

adapters.add(new SubAdapter(this, layoutHelper, 1, new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 100)));

Paste_Image.png

添加只有一个view的布局

SingleLayoutHelper singleLayoutHelper = new SingleLayoutHelper();

singleLayoutHelper.setBgColor(Color.rgb(135, 225, 90));

singleLayoutHelper.setAspectRatio(4);

singleLayoutHelper.setMargin(10, 20, 10, 20);

singleLayoutHelper.setPadding(10, 10, 10, 10);

adapters.add(new SubAdapter(this, singleLayoutHelper, 2, new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 100)));

Paste_Image.png

分析:

当你使用SingleLayoutHelper时,如果SubAdapter的数量是多少,界面上只有显示一个view.

添加一个表格布局

ColumnLayoutHelper layoutHelper = new ColumnLayoutHelper();

layoutHelper.setBgColor(0xff00f0f0);

layoutHelper.setWeights(new float[]{40.0f, Float.NaN, 40});

adapters.add(new SubAdapter(this, layoutHelper, 5) {

@Override

public void onBindViewHolder(MainViewHolder holder, int position) {

if (position == 0) {

LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 300);

layoutParams.mAspectRatio = 4;

holder.itemView.setLayoutParams(layoutParams);

} else {

LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 300);

layoutParams.mAspectRatio = Float.NaN;

holder.itemView.setLayoutParams(layoutParams);

}

}

});

Paste_Image.png

分析:

setWeights(new float[]{40.0f, Float.NaN, 40})确定了每列的比重权值,默认总权值是100,第一列和第三列是40,其他都是任意。我们看第一行为什么是这样的一个高度,因为下面的代码layoutParams.mAspectRatio = 4限制了宽高比是4。

添加一个OnePlusNLayoutHelper(数量为2)

需要先了解下它支持的布局方式有下面几种

* 1 + 0

* -------------------------

* | 1 |

* -------------------------

*

* 1 + 1

* -------------------------

* | | |

* | 1 | 2 |

* | | |

* -------------------------

*

* 1 + 2

* -------------------------

* | | 2 |

* | 1 |-----------|

* | | 3 |

* -------------------------

*

* 1 + 3

* -------------------------

* | | 2 |

* | 1 |-----------|

* | | 3 | 4 |

* -------------------------

* 1 + 4

* -------------------------

* | | 2 |

* | 1 |-----------|

* | | 3 | 4 | 5 |

* -------------------------

比如下面

OnePlusNLayoutHelper helper = new OnePlusNLayoutHelper();

helper.setBgColor(0xff876384);

helper.setAspectRatio(4.0f);

helper.setColWeights(new float[]{40f, 45f});

helper.setMargin(10, 20, 10, 20);

helper.setPadding(10, 10, 10, 10);

adapters.add(new SubAdapter(this, helper, 2));

Paste_Image.png

分析:

setAspectRatio(4.0f)设置整体布局的宽高比,setColWeights(new float[]{40f, 45f})设置列的权重,总权重是100,这里设置了第一列权重是40,第二列权重是45

添加一个OnePlusNLayoutHelper(数量为4)

OnePlusNLayoutHelper helper = new OnePlusNLayoutHelper();

helper.setBgColor(0xffef8ba3);

helper.setAspectRatio(2.0f);

helper.setColWeights(new float[]{40f});

helper.setRowWeight(30f);

helper.setMargin(10, 20, 10, 20);

helper.setPadding(10, 10, 10, 10);

adapters.add(new SubAdapter(this, helper, 4) {

@Override

public void onBindViewHolder(MainViewHolder holder, int position) {

super.onBindViewHolder(holder, position);

LayoutParams lp = (LayoutParams) holder.itemView.getLayoutParams();

if (position == 0) {

lp.rightMargin = 1;

} else if (position == 1) {

} else if (position == 2) {

lp.topMargin = 1;

lp.rightMargin = 1;

}

}

});

Paste_Image.png

分析:

当数量为4,为什么是这样一个展示页面,参考上面的(1+3)

setAspectRatio(2.0f)设置整个布局宽高比是2,setColWeights(new float[]{40f})设置第一列权重是40,

setRowWeight(30f)设置第一行的权重是30,行总权重是100

添加一个固定布局

FixLayoutHelper layoutHelper = new FixLayoutHelper(FixLayoutHelper.TOP_RIGHT, 20, 20);

adapters.add(new SubAdapter(this, layoutHelper, 1) {//29

@Override

public void onBindViewHolder(MainViewHolder holder, int position) {

super.onBindViewHolder(holder, position);

LayoutParams layoutParams = new LayoutParams(200, 200);

holder.itemView.setLayoutParams(layoutParams);

}

});

Paste_Image.png

分析:

固定类型(alignType)有四种:TOP_LEFT(左上角),TOP_RIGHT(右上角),BOTTOM_LEFT(左下角),BOTTOM_RIGHT(右下角)

固定的边距,如上x=20, y=20, 所以就得到上面的固定样式

LayoutParams layoutParams = new LayoutParams(200, 200); 设置固定布局的子view的宽高尺寸

添加一个方格

GridLayoutHelper layoutHelper = new GridLayoutHelper(4);

layoutHelper.setWeights(new float[]{20f, 26.665f});

layoutHelper.setMargin(7, 0, 7, 0);

layoutHelper.setHGap(3);

adapters.add(new SubAdapter(this, layoutHelper, 8));

Paste_Image.png

分析:

GridLayoutHelper(4)类似于GridView,设置列数量位4

setWeights(new float[]{20f, 26.665f})设置第一列和第二列的权重分别是20,26.665,其他列均分,列总权重是100,计算一下,第三列和第四列的权重是26.6675

setHGap(3)类似于GridView,设置水平间距是3像素

new SubAdapter(this, layoutHelper, 8) 设置方格总数量是8

添加一个Staggered布局

final StaggeredGridLayoutHelper helper = new StaggeredGridLayoutHelper(2, 10);

helper.setMargin(20, 10, 10, 10);

helper.setPadding(10, 10, 20, 10);

helper.setBgColor(0xFF86345A);

adapters.add(new SubAdapter(this, helper, 27) {

@Override

public void onBindViewHolder(MainViewHolder holder, int position) {

super.onBindViewHolder(holder, position);

LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 200);

if (position % 2 == 0) {

layoutParams.mAspectRatio = 1.0f;

} else {

layoutParams.height = 340 + position % 7 * 20;

}

holder.itemView.setLayoutParams(layoutParams);

}

});

Paste_Image.png

分析:

StaggeredGridLayoutHelper(2, 10)设置列数量为2,间距为10像素

new SubAdapter(this, helper, 27) 设置子view总数量为27

下面这段代码是设置每个子view的宽高尺寸,所以才形成了Staggered

LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 200);

if (position % 2 == 0) {

layoutParams.mAspectRatio = 1.0f;

} else {

layoutParams.height = 340 + position % 7 * 20;

}

vLayout提供的默认布局

Paste_Image.png

一般情况下,这些都是够用的,如果出现不够的情况,可以参考上面的布局来实现自己的。

源码分析其实现的原理

因为篇幅有限,这里就直接定位到其实现的最底层代码,定位到VirtualLayoutManager的public void setLayoutHelpers(@Nullable List helpers)方法,通过这个方法我们注意到上面我们所调用的helper,都已经转换成配置信息了,最后一段代码requestLayout();重新刷新布局,接下来会调用public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state),然后就会调用public void layoutChild(View child, int left, int top, int right, int bottom)对每个子view进行布局

下面是我画的简单的调用示意图

vlayout.jpg

有兴趣交流和讨论的,请在下面发评论。

有兴趣一起学习和讨论kotlin,请加Q群:248445350

[vlayout]:https://github.com/alibaba/vlayout

android vlayout 吸附图片,全解析布局神器vlayout相关推荐

  1. Android异步加载全解析之引入二级缓存

    Android异步加载全解析之引入二级缓存 为啥要二级缓存 前面我们有了一级缓存,为啥还要二级缓存呢?说白了,这就和电脑是一样的,我们电脑有内存和硬盘,内存读取速度快,所以CPU直接读取内存中的数据, ...

  2. Android 文件读写最全解析

    本文目录 Android文件读写概述 读取raw目录文件 读取assets目录文件 data/data/(包名) 目录文件读写 写数据 读数据 sdcard文件读写 申请动态权限 写数据 读数据 sd ...

  3. Android通知Notification使用全解析,看这篇就够了

    1.效果 2.简介 通知是 Android 在您的应用 UI 之外显示的消息,用于向用户提供提醒.来自其他人的通信或来自您的应用的其他及时信息.用户可以点击通知打开您的应用或直接从通知中执行操作. 2 ...

  4. Android 点击图片全屏

    2019独角兽企业重金招聘Python工程师标准>>> 最近做一个项目类似于QQ空间,做到照片浏览的功能,对于QQ空间中点击图片放大至全屏,感觉效果很赞,于是也做了个类似的效果.如下 ...

  5. android中的动画全解析

    Android为我们提供了2中动画 一: Tween Animation 渐变动画 通过对特定对象做图像的变换,例如: 平移, 缩放,旋转, 淡入淡出 等. 二: Frame Animation 帧动 ...

  6. Android 多窗口框架全解析

    转载: https://blog.csdn.net/xiaosayidao/article/details/75045087 Android N的的多窗口框架中,总共包含了三种模式. Split-Sc ...

  7. Android异步消息处理机制 全解析

    Android异步消息处理机制主要是指Handler的运行机制以及Hanlder所附带的MessageQueue和Looper的工作过程. 本文将通过分析源码(api-28)的形式,全面解析Handl ...

  8. Android fragment源码全解析

    Fragment 相信基本上每个android developer都用过,但是知晓其原理 用的好的还是不多,今天就从源码的角度上来带着大家分析一下Fragment的源码,对fragment有了更深层次 ...

  9. android引导页图片全屏适配,关于图片适配不同尺寸的image View(实战)

    分享人:广州华软佐罗 一. 前言 在前端开发过程中,设计稿中往往只提供一张图片,但是app内需要用到的尺寸各种各样. 同时图片不仅是信息的直接表达,也会为网站起到美观点缀的作用,图片的变形.过分裁切会 ...

最新文章

  1. 【Linux指标】内存篇
  2. mqtt连接失败_Flutter通过Mqtt消费ActivieMQ
  3. 资深美术分享:游戏开发如何确定画风?
  4. 【卷积核设计】Scaling Up Your Kernels to 31x31: Revisiting Large Kernel Design in CNNs
  5. javascript 中文与Unicode相互转化
  6. 如何破解Mac并为其提供真正应得的精美壁纸
  7. JavaScript绑定this
  8. 1002: Prime Path
  9. OpenCV---模板匹配
  10. Shiro和Zuul权限管理整合方案
  11. 持续化集成工具CruiseControl.NET
  12. vpay模式软件开发 vpay系统
  13. 用excel数据批量填充word表格
  14. proteus虚拟串口实现
  15. 2018秋招面经有感
  16. 力扣解法汇总2043-简易银行系统
  17. Java计算连续自交杂合概率代系变化
  18. Linux挂盘出现的问题
  19. OpenCV python去除图片水印
  20. WCF 之 什么是WCF

热门文章

  1. mysql 后台框架源码bootstrap
  2. 可视化理解四元数,愿你不再掉头发
  3. 《脱颖而出——成功网店经营之道》一导读
  4. 【leetcode】287 寻找重复数(查找)
  5. 怎么用u盘重装系统?两个方法免费教你电脑重装系统
  6. Flutter速来系列23-5、NestedScrollVi
  7. python的头文件和源文件_关于头文件和源文件的分别
  8. css3 手机相册,CSS相册简单实现方法(功能分析及代码)
  9. 不能打印机与计算机,为什么打印机不能打印(打印机为什么不能打印?)
  10. vue图形界面创建项目报:GraphQL error: Cannot return null for non-nullable field Mutation.projectCreate,解决方法