Android流式佈局實現

Android流式佈局實現

Android流式佈局實現

上一篇給大家介紹了一下 Android根據標籤長度自動換行,後來在開發實際中發現了這個自定義控件在addview(textview)時,修改textview顯示的內容時,這個自定義控件不能自動修改寬度去適應佈局的變化,這一篇經過查找資料後摸索了一個比較流行的流式佈局,這個流式佈局在各大app上都非常的手歡迎(天貓、京東等的購物車都採用了這個流式佈局)

接下來走一下完整的自定義控件的流程來給大家簡單的介紹一下

1.控件的創建
(1)當這個流式佈局在被加載如內存並顯示在屏幕上這一過程中,首先會調用view.measure(w,h)這個方法,表示測量view的寬度與高度,其中參數w與h分別表示這個控件的父控件的寬高。
(2)在view.measure()方法的調用過程中又會調用view本身的一個回調方法,onMeasure(),這個是view自身的一個回調方法,用於讓開發者在自定義View的時候重新計算自身的大小。一般會在這個方法中循環遍歷,計算出這個控件的全部子孫控件的寬高。
(3)在View的寬高計算完成以後,考慮將這個控件顯示到屏幕的指定位置上,此時view的onLayout()方法會被調用。 一般同時會在這個方法中計算出全部子孫控件在這個控件中的位置。
可能基本流程有些枯燥,接下來結合代碼看看。

2.流式佈局的實現

onMeasure()方法中: //通過計算每一個子控件的高度,得到自己的高度

for (int i = 0, childCount = getChildCount(); i < childCount; ++i) {
        View childView = getChildAt(i);
        LayoutParams childLayoutParams = childView.getLayoutParams();
        childView.measure(
                getChildMeasureSpec(widthMeasureSpec, paddingLeft + paddingRight,
                        childLayoutParams.width),
                getChildMeasureSpec(heightMeasureSpec, paddingTop + paddingBottom,
                        childLayoutParams.height));
        int childWidth = childView.getMeasuredWidth();
        int childHeight = childView.getMeasuredHeight();

        lineHeight = Math.max(childHeight, lineHeight);

        if (childLeft + childWidth + paddingRight > selfWidth) {
            childLeft = paddingLeft + childWidth;
            childTop += mVerticalSpacing + lineHeight;
            lineHeight = childHeight;
        } else {
            childLeft += childWidth + mHorizontalSpacing;
        }
    }

首先通過循環,遍歷這個控件的所有子控件,同時調用子控件的measure()方法,這時measure方法的兩個參數是控件能給這個子控件的最大寬高(我們都知道的,子控件再大,顯示的大小也不能比父控件還大)。這裏getChildMeasureSpec()方法的作用是用來計算一個合適子視圖的尺寸大小(寬度或者高度),結合我們從子視圖的LayoutParams所給出的MeasureSpec信息來獲取最合適的結果。比如,如果這個View知道自己的大小尺寸(因爲它本身的MeasureSpec的model爲Exactly,)並且子視圖的大小恰好跟父窗口一樣大,父窗口必須用給定的大小去layout子視圖
參數含義:spec 父窗口傳遞給子視圖的大小和模式
padding 父窗口的邊距,也就是xml中的android:padding
childDimension 子視圖想要繪製的準確大小,但最終不一定繪製此值

當得到了每一個子控件的大小以後,再要計算自己的寬高就簡單了。
int wantedHeight = childTop + lineHeight + paddingBottom;

同理,在onLayout中的這一句

for (int i = 0, childCount = getChildCount(); i < childCount; ++i) {
        View childView = getChildAt(i);

        if (childView.getVisibility() == View.GONE) {
            continue;
        }

        int childWidth = childView.getMeasuredWidth();
        int childHeight = childView.getMeasuredHeight();

        lineHeight = Math.max(childHeight, lineHeight);

        if (childLeft + childWidth + paddingRight > myWidth) {
            childLeft = paddingLeft + childWidth;
            childTop += mVerticalSpacing + lineHeight;
            lineHeight = childHeight;
        }
        childView.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
        childLeft += childWidth + mHorizontalSpacing;
    } 

首先通過循環遍歷,控制每個item子控件的顯示位置,如果當前行還能放得下一個item,就放到當前行,如果放不下就放到下一行的最左邊。
最終,遍歷完成,也就相當於把自己的位置顯示完成了。

完整代碼如下


/**
 * Created by Administrator on 2016/7/20.
 * Android流式佈局  
 * @auther madreain
 */

public class LabelLayout extends ViewGroup {
    private float mVerticalSpacing; //每個item縱向間距
    private float mHorizontalSpacing; //每個item橫向間距

    public LabelLayout (Context context) {
        super(context);
    }
    public LabelLayout (Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    public void setHorizontalSpacing(float pixelSize) {
        mHorizontalSpacing = pixelSize;
    }
    public void setVerticalSpacing(float pixelSize) {
        mVerticalSpacing = pixelSize;
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int selfWidth = resolveSize(0, widthMeasureSpec);

        int paddingLeft = getPaddingLeft();
        int paddingTop = getPaddingTop();
        int paddingRight = getPaddingRight();
        int paddingBottom = getPaddingBottom();

        int childLeft = paddingLeft;
        int childTop = paddingTop;
        int lineHeight = 0;

        //通過計算每一個子控件的高度,得到自己的高度
        for (int i = 0, childCount = getChildCount(); i < childCount; ++i) {
            View childView = getChildAt(i);
            LayoutParams childLayoutParams = childView.getLayoutParams();
            childView.measure(
                    getChildMeasureSpec(widthMeasureSpec, paddingLeft + paddingRight,
                            childLayoutParams.width),
                    getChildMeasureSpec(heightMeasureSpec, paddingTop + paddingBottom,
                            childLayoutParams.height));
            int childWidth = childView.getMeasuredWidth();
            int childHeight = childView.getMeasuredHeight();

            lineHeight = Math.max(childHeight, lineHeight);

            if (childLeft + childWidth + paddingRight > selfWidth) {
                childLeft = paddingLeft;
                childTop += mVerticalSpacing + lineHeight;
                lineHeight = childHeight;
            } else {
                childLeft += childWidth + mHorizontalSpacing;
            }
        }

        int wantedHeight = childTop + lineHeight + paddingBottom;
        setMeasuredDimension(selfWidth, resolveSize(wantedHeight, heightMeasureSpec));
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int myWidth = r - l;

        int paddingLeft = getPaddingLeft();
        int paddingTop = getPaddingTop();
        int paddingRight = getPaddingRight();

        int childLeft = paddingLeft;
        int childTop = paddingTop;

        int lineHeight = 0;

        //根據子控件的寬高,計算子控件應該出現的位置。
        for (int i = 0, childCount = getChildCount(); i < childCount; ++i) {
            View childView = getChildAt(i);

            if (childView.getVisibility() == View.GONE) {
                continue;
            }

            int childWidth = childView.getMeasuredWidth();
            int childHeight = childView.getMeasuredHeight();

            lineHeight = Math.max(childHeight, lineHeight);

            if (childLeft + childWidth + paddingRight > myWidth) {
                childLeft = paddingLeft;
                childTop += mVerticalSpacing + lineHeight;
                lineHeight = childHeight;
            }
            childView.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
            childLeft += childWidth + mHorizontalSpacing;
        }
    }
}

下面介紹代碼中加入這個流式佈局及在流式佈局中添加子佈局

label_layout_show_label = new LabelLayout (this);
        //加入流式佈局    
        LinearLayout.LayoutParams label_layout_show_labellayoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        label_layout_show_labellayoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
        label_layout_show_labellayoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
        label_layout_show_labellayoutParams.setMargins(PixelOrdpManager.dip2px(getBaseContext(), 13), PixelOrdpManager.dip2px(getBaseContext(), 12), 0, PixelOrdpManager.dip2px(getBaseContext(), 12));
        label_layout_show_labellayoutParams.gravity = Gravity.CENTER;
        label_layout_show_label.setLayoutParams(label_layout_show_labellayoutParams);

        label_layout_show_label_parent.addView(label_layout_show_label);

        //流式佈局中加入子佈局    
        textViewLabelOne = new TextView(this);
        textViewLabelTwo = new TextView(this);
        textViewLabelThere = new TextView(this);

        textViewLabelOne.setText("");
        textViewLabelTwo.setText("");
        textViewLabelThere.setText("");

        textViewLabelOne.setBackgroundResource(R.drawable.add_success_label);
        textViewLabelOne.setTextColor(getResources().getColor(R.color.M4A4D4F));
        textViewLabelOne.setTextSize(10);
        textViewLabelOne.setGravity(Gravity.CENTER);

        textViewLabelTwo.setBackgroundResource(R.drawable.add_success_label);
        textViewLabelTwo.setTextColor(getResources().getColor(R.color.M4A4D4F));
        textViewLabelTwo.setTextSize(10);
        textViewLabelTwo.setGravity(Gravity.CENTER);

        textViewLabelThere.setBackgroundResource(R.drawable.add_success_label);
        textViewLabelThere.setTextColor(getResources().getColor(R.color.M4A4D4F));
        textViewLabelThere.setTextSize(10);
        textViewLabelThere.setGravity(Gravity.CENTER);

        LinearLayout.LayoutParams textViewLabellayoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        textViewLabellayoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT;
//        textViewLabellayoutParams.width = PixelOrdpManager.dip2px(getBaseContext(),100);
        textViewLabellayoutParams.height = PixelOrdpManager.dip2px(getBaseContext(), 25);
        textViewLabellayoutParams.setMargins(PixelOrdpManager.dip2px(getBaseContext(), 13), PixelOrdpManager.dip2px(getBaseContext(), 12), 0, PixelOrdpManager.dip2px(getBaseContext(), 12));
        textViewLabellayoutParams.gravity = Gravity.CENTER;
        textViewLabelOne.setLayoutParams(textViewLabellayoutParams);
        textViewLabelTwo.setLayoutParams(textViewLabellayoutParams);
        textViewLabelThere.setLayoutParams(textViewLabellayoutParams);


        label_layout_show_label.addView(textViewLabelOne);
        label_layout_show_label.addView(textViewLabelTwo);
        label_layout_show_label.addView(textViewLabelThere);
發佈了56 篇原創文章 · 獲贊 7 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章