首先我們來看造成這個(gè)問題的原因:
那天在完成這組代碼的時(shí)候,為了給大家體現(xiàn)可操作的自由性,我并沒有去給大家完善業(yè)務(wù)邏輯,所以造成了這個(gè)坑存在的問題,那么我門首先來看造成這個(gè)問題的原因是什么
首先看代碼,我門采取的方式,是將測量后的代碼用集合保存起來再給layout進(jìn)行布局,
那么參考源碼

這里調(diào)用兩次,而在低版本當(dāng)中,,而用集合收集保存兩次之后總行書數(shù)為6行,那么這個(gè)問題深入分析后,做了一次測試


那么從這兩張圖可以分析出,在7.0之后和7.0之前系統(tǒng)對(duì)于源碼由一定程度的改變,




可以很明顯看到,27版本依賴于一個(gè)mReportNextDraw變量而這個(gè)變量在下面performDraw進(jìn)行了一次變幻,這里面運(yùn)行流程比較復(fù)雜,有需要的同學(xué)自己深入,我們?cè)谶@里明白一點(diǎn)就是,layout調(diào)用被被了一層條件,第一次不執(zhí)行, 第二次執(zhí)行了reportNextDraw()過后才執(zhí)行
從運(yùn)行機(jī)制來講,在7.0之前圖1所調(diào)用的scheduleTraversals她會(huì)依次調(diào)用onMeasure-->onLayout,但是在7.0之后我門會(huì)發(fā)現(xiàn)運(yùn)行機(jī)制發(fā)生了改變,如果是第一次的onMeasure那么他不會(huì)去調(diào)用onLayout,所以這里變成了兩次onMeasure一次onLayout 那么這個(gè)時(shí)候在我們的List當(dāng)中就會(huì)存在6行,當(dāng)我講測量固定了一個(gè)大的值之后我們看到了效果

這里可以很明顯看到其實(shí)子控件被我們定位到了3行開外超過了父控件的顯示高度
所以解決有兩種方案,第一種在onMeasure里面添加clear()

第二種
我直接修改了源碼不采用第三方的容器這里我修改了布局代碼
public class WaterfallFLowLayout extends ViewGroup {
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(), attires
}
//思路,通過前面兩節(jié)課我門知道了其實(shí),繪制流程最終會(huì)調(diào)用到我門的OnMesure 和 onLayout,
//而不通的布局,他們自己的實(shí)現(xiàn)不一樣,所以才有了我門使用的這些基本布局組件
//那么我們現(xiàn)在自己來開發(fā)一個(gè)瀑布式的流式布局
public WaterfallFLowLayout(Context context) {
super(context);
}
public WaterfallFLowLayout(Context context, AttributeSet attrs) {
super(context, attires
}
public WaterfallFLowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Log.i("barry", "onMeasure.......");
//此處我門可以知道這里是我們的爸爸的SIZE
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
//當(dāng)前空間寬高
int measureWidth = 0;
int measureHeight = 0;
//當(dāng)前行寬,行高,因?yàn)榇嬖诙嘈?,下一行?shù)據(jù)要放到下方,行高需要保存
int iCurLineW = 0;
int iCurLineH = 0;
//1.確認(rèn)自己當(dāng)前空間的寬高,這里因?yàn)闀?huì)有兩次OnMeasure,進(jìn)行二級(jí)測量優(yōu)化,所以采用IF_ELSE結(jié)構(gòu)
//二級(jí)優(yōu)化原理在源碼具體Draw時(shí),第一次不會(huì)直接進(jìn)行performDraw的調(diào)用反而是在下面重新進(jìn)行了一次scheduleTraversals
//在ViewRootImpl源碼2349-2372之中我門會(huì)看到 scheduleTraversals在我們的2363
if (widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY) {
measureWidth = widthSize;
measureHeight = heightSize;
} else {
//當(dāng)前VIEW寬高
int iChildWidth = 0;
int iChildHeight = 0;
//獲取子VIEW數(shù)量用于迭代
int childCount = getChildCount();
Log.i("barry", "childCount:" + childCount);
//單行信息容器
for (int i = 0; i < childCount; i++) {
View childAt = getChildAt(i);
//1.測量自己
measureChild(childAt, widthMeasureSpec, heightMeasureSpec);
//2.獲取XML資源
MarginLayoutParams layoutParams = (MarginLayoutParams) childAt.getLayoutParams();
//3.獲得實(shí)際寬度和高度(MARGIN+WIDTH)
iChildWidth = childAt.getMeasuredWidth() + layoutParams.leftMargin + layoutParams.rightMargin;
iChildHeight = childAt.getMeasuredHeight() + layoutParams.topMargin + layoutParams.bottomMargin;
Log.i("barry", "------widthSize-----:" + widthSize);
//4.是否需要換行
if (iCurLineW + iChildWidth > widthSize) {
//4.1.紀(jì)錄當(dāng)前行信息
//4.1.1.紀(jì)錄當(dāng)前行最大寬度,高度累加
measureWidth = Math.max(measureWidth, iCurLineH);
measureHeight += iCurLineH;
Log.i("barry", "------height---換行--:" + measureHeight);
//4.1.2.保存這一行數(shù)據(jù),及行高
//4.2.紀(jì)錄新的行信息
//4.2.1.賦予新行新的寬高
iCurLineW = iChildWidth;
iCurLineH = iChildHeight;
} else {
//5.1.不換行情況
//5.1.1.記錄某行內(nèi)的消息行內(nèi)寬度的疊加、高度比較
iCurLineW += iChildWidth;
iCurLineH = Math.max(iCurLineH, iChildHeight);
}
//6.如果正好是最后一行需要換行
if (i == childCount - 1) {
//6.1.記錄當(dāng)前行的最大寬度,高度累加
measureWidth = Math.max(measureWidth, iCurLineW);
measureHeight += iCurLineH;
}
}
}
Log.i("barry", "width:" + measureWidth);
Log.i("barry", "height:" + measureHeight);
//確認(rèn)保存自己的寬高
setMeasuredDimension(widthSize, measureHeight);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
Log.i("barry", "onLayout.....");
int startX = getPaddingLeft();
int startY = getPaddingTop();
int measuredWidth = getMeasuredWidth();
int measuredHeight = getMeasuredHeight();
//每個(gè)子控件占據(jù)的寬度
int childViewUseWidth = 0;
int childViewUseLineHight = 0;
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View childView = getChildAt(i);
if (childView.getVisibility() == GONE) {
continue;
}
//獲取每個(gè)子控件的layoutParams
MarginLayoutParams layoutParams = (MarginLayoutParams) childView.getLayoutParams();
int childViewMeasuredWidth = childView.getMeasuredWidth();
int childViewMeasuredHeight = childView.getMeasuredHeight();
//startX 變化為0 就換行, 每個(gè)子控件在擺放之前,判斷剩余控件是否足夠,用startX + childViewMeasuredWidth是否大于整個(gè)控件的寬度
//判斷的時(shí)候考慮PaddingRight
//考慮了子控件自己的margin值,每個(gè)子控件占據(jù)的寬度:childViewMeasuredWidth + leftMargin + rightMargin
childViewUseWidth = childViewMeasuredWidth + layoutParams.leftMargin + layoutParams.rightMargin;
if (startX + /*childViewMeasuredWidth*/childViewUseWidth > measuredWidth - getPaddingRight()) {
startX = getPaddingLeft();
//換行的時(shí)候,上一行使用的高度以一行的最高的為準(zhǔn)
startY += /*childViewMeasuredHeight*/childViewUseLineHight; //y左邊累加,因?yàn)楝F(xiàn)在所有的子控件高度都一樣
}
//擺放子控件
int leftChildView = startX + layoutParams.leftMargin;//考慮自己的margin
int topChildView = startY + layoutParams.topMargin;
int rightChildView = leftChildView + childViewMeasuredWidth;
int bottomChildView = topChildView + childViewMeasuredHeight;
//子控件布局
childView.layout(leftChildView, topChildView, rightChildView, bottomChildView);
//子控件擺放之后累加startX的值, 考慮每個(gè)孩子占據(jù)的寬度要加上marginLeft , marginRingt
startX += /*childViewMeasuredWidth*/childViewUseWidth;
//計(jì)算每一行使用的高度
childViewUseLineHight = Math.max(childViewUseLineHight, childViewMeasuredHeight + layoutParams.topMargin + layoutParams.bottomMargin);
}
}