转载 Carson_Ho
Android开源库V - Layout:淘宝、天猫都在用的UI框架
- 前言
- 目录
-
- 为什么要使用 V - Layout
- 简介
- 应用场景
- 原理解析
- 使用步骤
-
- 步骤1:创建RecyclerView & VirtualLayoutManager 对象并进行绑定
- 步骤2:设置回收复用池大小
- 步骤3:设置Adapter
- 步骤4:根据数据列表,创建对应的LayoutHelper
-
- 1. 线性布局(LinearLayoutHelper)
- 2. 网格布局(GridLayout)
- 3. 固定布局(FixLayoutHelper)
- 4. 可选显示的固定布局(ScrollFixLayoutHelper)
- 5. 浮动布局(FloatLayoutHelper)
- 6. 栏格布局(ColumnLayoutHelper)
- 7. 通栏布局(SingleLayoutHelper)
- 8. 一拖N布局 (OnePlusNLayoutHelper)
- 9. 吸边布局(StickyLayoutHelper)
- 10. 瀑布流布局(StaggeredGridLayoutHelper)
- 自定义布局(即自定义LayoutHelper)
- 步骤5:将生成的LayoutHelper 交给Adapter,并绑定到RecyclerView 对象
- 实例说明
- 总结
前言
- V- Layout 是阿里出品的基础 UI 框架,用于快速实现页面的复杂布局,在手机天猫 Android版 内广泛使用
- 2017年4月V- Layout在Github上开源!
Github - alibaba - vlayout
- 阅读本文前请先看:
Android Tangram模型:连淘宝、天猫都在用的UI框架模型你一定要懂
目录
为什么要使用 V - Layout
在讲解 V - Layout 前,我们先来搞懂一个问题:为什么要使用 V - Layout
背景
- Android中 UI 性能消耗主要来自于两个方面:
- 布局层次嵌套导致多重 measure/layout
- View控件的创建和销毁
- 为了解决上述问题,现有的解决方式是:
- 少用嵌套布局
- 使用 ListView/GirdView/RecyclerView等基础控件来处理View的回收与复用。
但是,很多时候我们需要在一个长列表下做多种类型的布局来分配各种元素,,特别是电商平台首页等页面,布局元素结构更加复杂多样。如下图:
此时的解决方案有所变化:不采用子View的复用,只采用一个主要的复用容器(如ListView 或 RecyclerView+LinearLayoutManager),然后在其中使用嵌套方式直接用各个组件进行拼接,减少了复用的能力
问题
这种做法还是会损失Android应用的性能。
解决方案
- 通过自定义 LayoutManager 管理所有的布局类型
- 即阿里出品的基础 UI 框架项目 VirtualLayout就是采用该方式来解决上述问题
简介
- 定义:VirtualLayout是阿里出品的基础 UI 框架项目
- 作用:快速实现复杂的布局格式的混排 & 通过组件回收提高性能
- 基于 RecyclerView & LayoutManager扩展
- 目前已在Github开源:Github - alibaba - vlayout
应用场景
- 复杂的布局格式混排,如:浮动布局、栏格布局、通栏布局、一拖N布局、瀑布流布局,还可以组合使用这些布局
- 具体场景是:如电商平台首页、活动页等等
V - Layout 目前已在手机天猫 & 淘宝 Android 版内广泛使用
原理解析
V - Layout的本质原理是:通过自定义一个VirtualLayoutManager(继承自 LayoutManager),用于管理一系列LayoutHelper,将具体的布局能力交给LayoutHelper来完成,从而 快速实现组合布局 的需求。
- 每个 LayoutHelper负责 页面某一个范围内的布局
- V - Layout默认实现了10种默认布局:(对应同名的LayoutHelper)
源码类说明
V - Layout的源码类图如下:
具体类说明
V - Layout默认实现了10种默认布局:(对应同名的LayoutHelper)
下面会进行详细介绍。
特别注意:
- 每一种LayoutHelper负责布局一批组件范围内的组件,不同组件范围内的组件之间,如果类型相同,可以在滑动过程中回收复用。因此回收粒度比较细,且可以跨布局类型复用。
- 支持扩展外部:即注册新的LayoutHelper,实现特殊的布局方式。下面会详细说明
介绍完类之后,我将详细分析 V - Layout的工作流程。
工作流程
V - Layout的工作流程分为 初始化 & 布局流程。如下图:
下面我将对初始化 & 布局流程作详细分析。
初始化
- 在使用 V - layout 快速实现复杂布局前,需要先做一系列的初始化工作。
初始化流程与使用普通的 RecyclerView + LayoutManager 初始化流程基本一致:Vlayout的使用者
- 此处的初始化 实际上 就是 使用者在使用 V - layout 时需要做的初始化工作。
- 此处主要先讲解下数据列表的获取:本质上,是对页面实体 进行 卡片 & 组件的拆解,形成一个位置列表
- 其他初始化步骤将在下面实例讲解进行详细说明
具体布局流程
- 当完成初始化工作后,每当用户刚打开页面第一次渲染布局 或 用户滑动页面时,都会进行一次布局流程
- 布局流程的本质是:自定义 VirtualLayoutManager持续获取页面状态,并通过LayoutHelperFinder找到对应的LayoutHelper从而实现对应的布局逻辑,从而快速实现组合布局 的需求
- 具体流程如下
总结
下面用一张图总结 V - Layout 的原理 & 工作流程
在讲完原理后,接下来我将如何使用 V - Layout。
使用步骤
- V - Layout的使用其实就是上面说的初始化工作
- 使用步骤如下:
下面我将对每个步骤进行详细说明。
步骤1:创建RecyclerView & VirtualLayoutManager 对象并进行绑定
recyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
// 创建RecyclerView对象
VirtualLayoutManager layoutManager = new VirtualLayoutManager(this);
// 创建VirtualLayoutManager对象
// 同时内部会创建一个LayoutHelperFinder对象,用来后续的LayoutHelper查找
recyclerView.setLayoutManager(layoutManager);
// 将VirtualLayoutManager绑定到recyclerView
步骤2:设置回收复用池大小
如果一屏内相同类型的 View 个数比较多,需要设置一个合适的大小,防止来回滚动时重新创建 View)
// 设置组件复用回收池
RecyclerView.RecycledViewPool viewPool = new RecyclerView.RecycledViewPool();
recyclerView.setRecycledViewPool(viewPool);
viewPool.setMaxRecycledViews(0, 10);
步骤3:设置Adapter
设置 V - Layout的Adapter有两种方式:
- 方式1:继承 自 DelegateAdapter
- 方式2:继承 自 VirtualLayoutAdapter
下面会进行详细说明:
方式1:继承 自 DelegateAdapter
-
定义:
DelegateAdapter是V - Layout专门为管理 LayoutHelper定制的 Adapter
-
作用:通过管理不同布局的Adapter,继而管理不同的 LayoutHelper,从而实现使用不同组合布局
- 特别注意:
虽不可直接绑定LayoutHelper,但是它内部有一个继承自RecyclerView.Adapter的内部类Adapter可以绑定LayoutHelper
; - 即
通过一个List把绑定好的Adapter打包起来,再放去DelegateAdapter,这样就可以实现组合使用不同的布局
- 特别注意:
-
具体做法:
写法与复写系统自带的Adapter非常类似:只比系统自带的RecyclerAdapter需要多重载onCreateLayoutHelper方法,其余类似
- 关于Android系统自带的RecyclerAdapter的使用具体请看文章Android开发:ListView、AdapterView、RecyclerView全面解析
public class MyAdapter extends DelegateAdapter.Adapter<MyAdapter.MainViewHolder> { // 比系统自带的RecyclerAdapter需要多重载onCreateLayoutHelper() @Override public LayoutHelper onCreateLayoutHelper() { return layoutHelper; } ... // 其余写法与复写系统自带的Adapter相同 }
方式2:继承 自 VirtualLayoutAdapter
-
定义:
当需要实现复杂需求时, 可以通过继承VirtualLayoutAdapter从而实现自定义Adapter
-
具体使用
public class MyAdapter extends VirtualLayoutAdapter { ...// 自定义Adapter逻辑 }
步骤4:根据数据列表,创建对应的LayoutHelper
- 系统
已封装好以下布局类型(对应同名的LayoutHelper)
- 具体使用如下:
1. 线性布局(LinearLayoutHelper)
- 布局说明:布局子元素(Item)以线性排布的布局
具体使用// 设置线性布局 LinearLayoutHelper linearLayoutHelper = new LinearLayoutHelper(); // 创建对应的LayoutHelper对象 // 所有布局的公共属性(属性会在下面详细说明) linearLayoutHelper.setItemCount(4);// 设置布局里Item个数 linearLayoutHelper.setPadding(10,10,10,10);// 设置LayoutHelper的子元素相对LayoutHelper边缘的距离 linearLayoutHelper.setMargin(10,10,10,10);// 设置LayoutHelper边缘相对父控件(即RecyclerView)的距离 linearLayoutHelper.setBgColor(Color.GRAY);// 设置背景颜色 linearLayoutHelper.setAspectRatio(6);// 设置设置布局内每行布局的宽与高的比 // linearLayoutHelper特有属性 linearLayoutHelper.setDividerHeight(1); // 设置每行Item的距离
1. 所有布局的共有属性说明:
a. setItemCount属性
- 作用:
设置整个布局里的Item数量
如设置的Item总数如与Adapter的getItemCount()方法返回的数量不同,会取决于后者
- 具体使用
// 接口示意 public void setItemCount(int Count) // 具体使用 linearLayoutHelper.setItemCount(4);
b. Adding & Margin属性
- 定义:都是边距的含义,但二者边距的定义不同:
- Padding:是 LayoutHelper 的子元素相对 LayoutHelper 边缘的距离;
- Margin:是 LayoutHelper 边缘相对父控件(即RecyclerView)的距离。具体如下图:
- 具体使用
// 接口示意
public void setPadding(int leftPadding, int topPadding, int rightPadding, int bottomPadding)
public void setMargin(int leftMargin, int topMargin, int rightMargin, int bottomMargin)
// 具体使用
linearLayoutHelper.setPadding(10,10,10,10);
// 设置LayoutHelper的子元素相对LayoutHelper边缘的距离
linearLayoutHelper.setMargin(10,10,10,10);
// 设置LayoutHelper边缘相对父控件(即RecyclerView)的距离
c. bgColor属性
- 作用:设置布局背景颜色
- 具体使用:
// 接口示意 public void setBgColor(int bgColor) // 具体使用 linearLayoutHelper.setBgColor(Color.YELLOW);
d. aspectRatio属性
- 作用:
设置布局内每行布局的宽与高的比
。如下图
- 具体使用
// 接口示意 public void setAspectRatio(float aspectRatio); // LayoutHelper定义的aspectRatio ((VirutalLayoutManager.LayoutParams) layoutParams).mAspectRatio // 视图的LayoutParams定义的aspectRatio // 在LayoutHelper计算出视图宽度之后,用来确定视图高度时使用的, // 它会覆盖通过LayoutHelper的aspectRatio计算出来的视图高度,因此具备更高优先级。 // 具体使用 linearLayoutHelper.setAspectRatio(6);
2. LinearLayoutHelper布局的特有属性说明
a. dividerHeight属性
- 作用:
设置每行Item之间的距离
设置的间隔会与RecyclerView的addItemDecoration()添加的间隔叠加
。
- 具体使用
// 接口示意 public void setDividerHeight(int dividerHeight) // 具体使用 linearLayoutHelper.setDividerHeight(1);
2. 网格布局(GridLayout)
- 布局说明:布局里的Item以网格的形式进行排列
- 具体使用
// 设置Grid布局 GridLayoutHelper gridLayoutHelper = new GridLayoutHelper(3); // 在构造函数设置每行的网格个数 // 公共属性 gridLayoutHelper.setItemCount(6);// 设置布局里Item个数 gridLayoutHelper.setPadding(20, 20, 20, 20);// 设置LayoutHelper的子元素相对LayoutHelper边缘的距离 gridLayoutHelper.setMargin(20, 20, 20, 20);// 设置LayoutHelper边缘相对父控件(即RecyclerView)的距离 gridLayoutHelper.setBgColor(Color.GRAY);// 设置背景颜色 gridLayoutHelper.setAspectRatio(6);// 设置设置布局内每行布局的宽与高的比 // gridLayoutHelper特有属性(下面会详细说明) gridLayoutHelper.setWeights(new float[]{ 40, 30, 30});//设置每行中 每个网格宽度 占 每行总宽度 的比例 gridLayoutHelper.setVGap(20);// 控制子元素之间的垂直间距 gridLayoutHelper.setHGap(20);// 控制子元素之间的水平间距 gridLayoutHelper.setAutoExpand(false);//是否自动填充空白区域 gridLayoutHelper.setSpanCount(3);// 设置每行多少个网格 // 通过自定义SpanSizeLookup来控制某个Item的占网格个数 gridLayoutHelper