android vlayout 吸附图片,全解析布局神器vlayout
为什么要使用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相关推荐
- Android异步加载全解析之引入二级缓存
Android异步加载全解析之引入二级缓存 为啥要二级缓存 前面我们有了一级缓存,为啥还要二级缓存呢?说白了,这就和电脑是一样的,我们电脑有内存和硬盘,内存读取速度快,所以CPU直接读取内存中的数据, ...
- Android 文件读写最全解析
本文目录 Android文件读写概述 读取raw目录文件 读取assets目录文件 data/data/(包名) 目录文件读写 写数据 读数据 sdcard文件读写 申请动态权限 写数据 读数据 sd ...
- Android通知Notification使用全解析,看这篇就够了
1.效果 2.简介 通知是 Android 在您的应用 UI 之外显示的消息,用于向用户提供提醒.来自其他人的通信或来自您的应用的其他及时信息.用户可以点击通知打开您的应用或直接从通知中执行操作. 2 ...
- Android 点击图片全屏
2019独角兽企业重金招聘Python工程师标准>>> 最近做一个项目类似于QQ空间,做到照片浏览的功能,对于QQ空间中点击图片放大至全屏,感觉效果很赞,于是也做了个类似的效果.如下 ...
- android中的动画全解析
Android为我们提供了2中动画 一: Tween Animation 渐变动画 通过对特定对象做图像的变换,例如: 平移, 缩放,旋转, 淡入淡出 等. 二: Frame Animation 帧动 ...
- Android 多窗口框架全解析
转载: https://blog.csdn.net/xiaosayidao/article/details/75045087 Android N的的多窗口框架中,总共包含了三种模式. Split-Sc ...
- Android异步消息处理机制 全解析
Android异步消息处理机制主要是指Handler的运行机制以及Hanlder所附带的MessageQueue和Looper的工作过程. 本文将通过分析源码(api-28)的形式,全面解析Handl ...
- Android fragment源码全解析
Fragment 相信基本上每个android developer都用过,但是知晓其原理 用的好的还是不多,今天就从源码的角度上来带着大家分析一下Fragment的源码,对fragment有了更深层次 ...
- android引导页图片全屏适配,关于图片适配不同尺寸的image View(实战)
分享人:广州华软佐罗 一. 前言 在前端开发过程中,设计稿中往往只提供一张图片,但是app内需要用到的尺寸各种各样. 同时图片不仅是信息的直接表达,也会为网站起到美观点缀的作用,图片的变形.过分裁切会 ...
最新文章
- 【Linux指标】内存篇
- mqtt连接失败_Flutter通过Mqtt消费ActivieMQ
- 资深美术分享:游戏开发如何确定画风?
- 【卷积核设计】Scaling Up Your Kernels to 31x31: Revisiting Large Kernel Design in CNNs
- javascript 中文与Unicode相互转化
- 如何破解Mac并为其提供真正应得的精美壁纸
- JavaScript绑定this
- 1002: Prime Path
- OpenCV---模板匹配
- Shiro和Zuul权限管理整合方案
- 持续化集成工具CruiseControl.NET
- vpay模式软件开发 vpay系统
- 用excel数据批量填充word表格
- proteus虚拟串口实现
- 2018秋招面经有感
- 力扣解法汇总2043-简易银行系统
- Java计算连续自交杂合概率代系变化
- Linux挂盘出现的问题
- OpenCV python去除图片水印
- WCF 之 什么是WCF
热门文章
- mysql 后台框架源码bootstrap
- 可视化理解四元数,愿你不再掉头发
- 《脱颖而出——成功网店经营之道》一导读
- 【leetcode】287 寻找重复数(查找)
- 怎么用u盘重装系统?两个方法免费教你电脑重装系统
- Flutter速来系列23-5、NestedScrollVi
- python的头文件和源文件_关于头文件和源文件的分别
- css3 手机相册,CSS相册简单实现方法(功能分析及代码)
- 不能打印机与计算机,为什么打印机不能打印(打印机为什么不能打印?)
- vue图形界面创建项目报:GraphQL error: Cannot return null for non-nullable field Mutation.projectCreate,解决方法