FlowLayout主要实现了onMeasure
//先测量子view然后再测量自己的view @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { Log.d(TAG,"onMeasure widthMeasureSpec :" + widthMeasureSpec + " heightMeasureSpec :" +heightMeasureSpec ) ; //参考framelayout ,根据view的生命周期,初始化参数应该在onMeasure, initMeasureParams(); int childCount = getChildCount(); //解析.xml,获得viewGroup的宽度和高度,mode int selfWidth = MeasureSpec.getSize(widthMeasureSpec); int selfHeight = MeasureSpec.getSize(heightMeasureSpec); final boolean measureMatchParentChildren = MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY || MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY; List<View> lineViews = new ArrayList<>(); //保存一行中的所有的view int lineWidthUsed = 0; //记录这行已经使用了多宽的size int lineHeight = 0; // 一行的行高 int parentNeededWidth = 0; // measure过程中,子View要求的父ViewGroup的宽 int parentNeededHeight = 0; // measure过程中,子View要求的父ViewGroup的高 Log.i(TAG," childCount: " + childCount + " selfWidth:" + selfWidth + " selfHeight :" + selfHeight ); for(int i = 0; i< childCount;i++) { View childView = getChildAt(i); final MarginLayoutParams childLW =( MarginLayoutParams) childView.getLayoutParams(); Log.i(TAG, "childview:" + childView); //测量子view int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, getPaddingLeft() + getPaddingRight() + childLW.leftMargin + childLW.rightMargin , childLW.width); int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, getPaddingTop()+ getPaddingBottom() +childLW.topMargin + childLW.bottomMargin, childLW.height); childView.measure(childWidthMeasureSpec, childHeightMeasureSpec); //获取子view的高度和宽度,需要用getMeausureWidth,这个是测量后值就存在了 int childMeasureWidth = childView.getMeasuredWidth(); Log.i(TAG, "childMeasureWidth :" + childMeasureWidth); int childMeausreHeight = childView.getMeasuredHeight(); Log.i(TAG, "childMeausreHeight :" + childMeausreHeight); //通过宽度来判断是否需要换行,如果需要换行后的每行的行高来获取整个viewGroup的高度 if (childMeasureWidth + lineWidthUsed + mHorizontalSpacing > selfWidth) { allLines.add(lineViews); lineHeights.add(lineHeight); //当行的宽度和高度 parentNeededWidth = Math.max(parentNeededWidth, lineWidthUsed + mHorizontalSpacing); parentNeededHeight = parentNeededHeight + lineHeight + mVerticalSpacing; //换行后恢复所有的数据 lineViews = new ArrayList<>(); lineWidthUsed = 0; lineHeight = 0; } // lineViews是记录每行的view ,这样方便layout lineViews.add(childView); //记录每一行使用的宽度 lineWidthUsed = childMeasureWidth + mHorizontalSpacing + lineWidthUsed; //记录每一行的高度 lineHeight = Math.max(childMeausreHeight, lineHeight); Log.i(TAG, " lineWidthUsed :" + lineWidthUsed + " lineHeight:" + lineHeight); //边界处理,最后一行处理,因为最后一行的数据不会满足换行的条件呢,因此需要考虑此情况 if(i == childCount -1) { allLines.add(lineViews); lineHeights.add(lineHeight); //当行的宽度和高度 parentNeededWidth = Math.max(parentNeededWidth, lineWidthUsed + mHorizontalSpacing); parentNeededHeight = parentNeededHeight + lineHeight + mVerticalSpacing; } } int realWidth = measureMatchParentChildren ? selfWidth : parentNeededWidth; int realHeight = measureMatchParentChildren? selfHeight: parentNeededHeight; setMeasuredDimension(realWidth,realHeight); }
onMeasure先测量子view然后测量自己,主要的知识点和出现的错误为:
1.通过调用getChildMeasureSpec方法将LayoutParmes转换为measureSpec,然后调用Measure方法来测量子view
2.如何换行,换行的条件
3.当需要获得子view的宽度和高度 ,调用 getMeasuredWidth,getMeasuredHeight,主要此值在onMeasure的时候获得
4.需要考虑边缘情况,最后一行的情况