Android流式布局實現,android流布局
查看我的全部開源項目【開源實驗室】
歡迎加入我的QQ群:【201055521】,本部落格用戶端下載【請點擊】
摘要
新項目用到了一種全新布局————Android標籤流式布局的功能,正好一直說給大家講自訂控制項的實現,今天就為大家講一種android流式布局的實現。
本文原創,轉載請註明地址:http://blog.kymjs.com/
本文
在日常的app使用中,我們會在android 的app中看見熱門標籤等自動換行的流式布局,今天,我們就來看看如何自訂一個類似熱門標籤那樣的流式布局吧(源碼下載在下面最後給出)
這個控制項並不是我實現的,代碼是從網上搜流式布局找到的。我只是為大家講解一下實現過程以及原理。
先看代碼
public class FlowLayout extends ViewGroup { private float mVerticalSpacing; //每個item縱向間距 private float mHorizontalSpacing; //每個item橫向間距 public FlowLayout(Context context) { super(context); } public FlowLayout(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; } }}
從控制項建立過程說起流布局的實現
看到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; 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; childTop += mVerticalSpacing + lineHeight; lineHeight = childHeight; } childView.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight); childLeft += childWidth + mHorizontalSpacing; }
首先通過迴圈遍曆,控制每個item子控制項的顯示位置,如果當前行還能放得下一個item,就放到當前行,如果放不下就放到下一行的最左邊。
最終,遍曆完成,也就相當於把自己的位置顯示完成了。
效果