概述
最近在写公司项目需求,有一个流式布局FlowLayout需要进行修改,这个一般用于显示标签信息,看了一下之前的代码,感觉可拓展性太差,要实现这次的效果,有点麻烦,索性自己打造一个流式布局,这次决定像ListView,RecycleView一样,采用Adapter设计模式进行实现,这样具体对某个子View进行修改的话,可以直接在Adapter中进行修改。
效果
分析
1.继承自谁?我们可以看到内部包含许多View,因此这是一个自定义ViewGroup
2.测量 onMeasure(),这里我们需要知道FlowLayout的高度可以随便给,但是宽度不可以是wrap_content,因此我们需要在对wrap_conent时抛出异常提醒,具体怎么判断宽度是否为wrap_content? 这里需要根据测量模式进行处理,简单可以理解为:
2.1 EXACTLY:表示设置了精确值,一般当设置其宽、高为dp或是match_parent时,会将其测量模式设置为EXACTLY; 2.2 AT_MOST:一般当设置其宽、高为wrap_content时,会将其测量设置为AT_MOST; 2.3 UNSPECIFIED:表示子布局想要多大就多大,此种模式比较少见。一般会在系统的View中,例如ScrollView…
3.摆放 onLayout:这里我们需要对子View按照顺序进行换行或摆放,定位
实现
1.首先需要处理的是onMeasure()方法
1.1 需要处理什么时候换行?
当前已摆放View的宽度+即将要摆放View的宽度>该FlowLayout的宽度+PaddingLeft+PaddingRight
1.2 高度计算
FlowLayout的高度 = 所有行的最大高度+PaddingTop+PaddingBottom
1.3 当View设置为GONE则不进行测量
注意⚠️:这里我们设置了两个List集合,一个是我们所有的集合按照我们每一行的View进行储存,为了避免代码臃肿,在onlayout在写一遍,这个集合我们在这里进行储存,我们在摆放时需要使用
下面看下具体的实现代码
private List<List<View>> mChildViews = new ArrayList<>();
private ArrayList<View> childViews = new ArrayList<>();//每一行的View
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//判断用户是否设置宽度为wrap_content,如果是wrap_content则抛出异常
if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.AT_MOST) {
throw new RuntimeException("FlowLayout does not allow setting layout_width to wrap_content");
}
mCurrentLines = 0;
//获取View个数
int childCount = getChildCount();
//获取宽度
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
//计算高度
int heightSize = getPaddingTop() + getPaddingBottom();
int lineWidth = 0;
int maxLineHeight = 0;
mChildViews.clear();
childViews.clear();
//循环所有的View计算高度 注意处理Gone,padding margin
for (int i = 0; i < childCount; i++) {
//获取子View
View mChildView = getChildAt(i);
if (mChildView.getVisibility() == GONE) continue;
//这句话执行完毕之后 就可以获取子View的宽高了 因为这句话会调用子View 的measure方法
measureChild(mChildView, widthMeasureSpec, heig