前言
前面我们已经讲解过measure和layout的实际调用流程
这节我们通过一个案例,具体演示这些方法是如何使用的
接下来我们要实现的是一个综合了瀑布流布局和流式布局功能的自定义布局
瀑布流布局和流式布局的大概效果,就是有若干元素,它们能够自动按行排列,一行空间不够就自动切换到下一行
区别是,瀑布流布局(FallsLayout)一般是等宽的,而流式布局(FlowLayout)一般是等高的
而我们的布局是支持既不等高也不等宽的元素的
这样的布局没有瀑布流布局和流式布局好看,但我们只要控制子元素的宽高固定,它就可以立刻呈现这两种布局的效果
所以我们的布局是一个万能的流布局
实现思路
每个元素都有上下左右四个顶点,我们分别用两个List,来记录这些顶点的x,y坐标值,并按从小到大排序
当我们加入一个新元素时,其左上角一定是和其中某个坐标对齐的
我们只要按照从小到大的顺序,遍历所有[x, y]的组合,尝试将新元素放到该位置
如果该元素的范围不与已经添加的所有元素重合,则说明该位置就是可用的位置,将新元素摆放到这里即可
要注意的是,控件是有padding和margin的,measure和layout时要考虑这些细节
关键代码
package com.easing.commons.android.ui.control.layout;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import com.easing.commons.android.app.CommonActivity;
import com.easing.commons.android.manager.Dimens;
import com.easing.commons.android.ui.adapter.ViewAdapter;
import com.easing.commons.android.value.measure.Rect;
import java.util.LinkedList;
//瀑布流布局(元素从左到右,从上到下,见缝就插,没有行的概念)
@SuppressWarnings("all")
public class FallsLayout extends ViewGroup {
protected CommonActivity context;
protected ViewAdapter adapter;
protected int margin = Dimens.toPx(0);
//记录所有的边界点
protected final Object lock = new Object();
protected final LinkedList xList = new LinkedList();
protected final LinkedList yList = new LinkedList();
//已经使用的区域
protected final LinkedList usedRects = new LinkedList();
//父级宽度
protected int parentWidth;
public FallsLayout(Context context) {
super(context, null);
}
public FallsLayout(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
init(context, attributeSet);
}
//初始化
protected void init(Context context, AttributeSet attributeSet) {
this.context = (CommonActivity) context;
}
@Override
protected LayoutParams generateDefaultLayoutParams() {
MarginLayoutParams lp = new MarginLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
lp.setMargins(margin, margin, margin, margin);
return lp;
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
MarginLayoutParams lp = new MarginLayoutParams(getContext(), attrs);
return lp;
}
@Override
protected LayoutParams generateLayoutParams(LayoutParams p) {
MarginLayoutParams lp = new MarginLayoutParams(p);
return lp;
}
@Override
protected void onMeasure(int wSpec, int hSpec) {
//解析测量参数
int mode_w = MeasureSpec.getMode(wSpec);
int mode_h = MeasureSpec.getMode(hSpec);
int size_w = MeasureSpec.getSize(wSpec);
int size_h = MeasureSpec.getSize(hSpec);
//瀑布布局必须明确指定宽高
if (mode_w != MeasureSpec.EXACTLY)
throw new RuntimeException("FallsLayout must exactly specify width");
parentWidth = size_w;
//添加起始位置
xList.clear();
yList.clear();
usedRects.clear();
xList.add(getPaddingLeft());
yList.add(getPaddingTop());
//循环遍历子View,测量总尺寸
for (int i = 0; i
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?