關(guān)于流逝布局作業(yè)7.0顯示問題詳解

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


image.png

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


AFC080BBA1E9D5E4B00F7F7137982B22.jpg

3CB0B65114DA89305A016BDB1098A2B1.jpg

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


21版本

27版本
image.png
image.png

可以很明顯看到,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è)大的值之后我們看到了效果


1AEA585E7E20C2A67593167BC32B7314.jpg

這里可以很明顯看到其實(shí)子控件被我們定位到了3行開外超過了父控件的顯示高度

所以解決有兩種方案,第一種在onMeasure里面添加clear()


image.png

第二種
我直接修改了源碼不采用第三方的容器這里我修改了布局代碼

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);
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容