流式布局(应用:热门标签,照片)
自定义ViewGroup
1.onMeasure:测量子View的宽和高,设置自己的宽和高
onMeasure根据子View的布局文件,为子View设置测量模式和测量值。
测量:包括测量模式和测量值
测量模式包裹以下三种模式:
1.EXACTLY:是指显示指定大小时,例如:100dp,match_parent
2.AT_MOST:wrap_content
3.UNSPCIFIED:子View想要多大就给他多大,一般出现在ScroolView中,很少见
2.onLayout:设置子View的位置
ViewGroup会对应一个LayoutParams
子View.getLayoutParams()获得的是父控件的LayoutParameters
这里我们只需要知道子空间之间的间距,所以我们只需要指定我们LayoutParams为MarginLayoutParameters即可,
自定义ViewGroup
1.onMeasure:测量子View的宽和高,设置自己的宽和高
onMeasure根据子View的布局文件,为子View设置测量模式和测量值。
测量:包括测量模式和测量值
测量模式包裹以下三种模式:
1.EXACTLY:是指显示指定大小时,例如:100dp,match_parent
2.AT_MOST:wrap_content
3.UNSPCIFIED:子View想要多大就给他多大,一般出现在ScroolView中,很少见
2.onLayout:设置子View的位置
ViewGroup会对应一个LayoutParams
子View.getLayoutParams()获得的是父控件的LayoutParameters
这里我们只需要知道子空间之间的间距,所以我们只需要指定我们LayoutParams为MarginLayoutParameters即可,
当然在实际开发中我们应该根据需要来选择LayoutParams,也可以自定义LayoutParams。
效果:
代码如下:
package com.example.liaoli.customflowlayout.view; import android.content.Context; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import java.util.ArrayList; import java.util.List; /** * Created by liaoli on 2015/11/16. */ public class FlawLayout extends ViewGroup { public FlawLayout(Context context) { super(context); } public FlawLayout(Context context, AttributeSet attrs) { super(context, attrs); } public FlawLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } /** * 測量子類的寬和高,以便確定自己的寬和高 * @param widthMeasureSpec * @param heightMeasureSpec */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int mWidth = MeasureSpec.getSize(widthMeasureSpec); int widthMeasureMode = MeasureSpec.getMode(widthMeasureSpec); int mHeight = MeasureSpec.getSize(heightMeasureSpec); int heightMeasureMode = MeasureSpec.getMode(heightMeasureSpec); int childCount = getChildCount(); //記錄wrap_content時候FlawLayout的寬和高 int wrapContentWidth = 0; int wrapContentHeight= 0; //記錄每一行的寬度和高度 int lineWidth = 0; int lineHeight = 0; for(int i = 0;i < childCount;i++){ View child = getChildAt(i); //測量每一個子View的寬和高 measureChild(child, widthMeasureSpec, heightMeasureSpec); MarginLayoutParams layoutParams = (MarginLayoutParams) child.getLayoutParams(); //得到計算子View佔據的寬度和高度 int childWidthInParent = child.getMeasuredWidth() + layoutParams.leftMargin + layoutParams.rightMargin; int childHeightInParent = child.getMeasuredHeight() + layoutParams.topMargin + layoutParams.bottomMargin; if (lineWidth + childWidthInParent > mWidth - getPaddingLeft() - getPaddingRight()) { //此時要換行,行的高度增加 wrapContentHeight += lineHeight; //此時一行的寬度已經可以確定,如果行寬比原來的最大行寬大,則寬度變為大的 wrapContentWidth = Math.max(lineWidth,wrapContentWidth); //換行后充值行寬和行高 lineHeight = childHeightInParent; lineWidth = childWidthInParent; }else { //不換行,本行寬度增加 lineWidth += childWidthInParent; //不換行,本行高度有可能變化 lineHeight = Math.max(lineHeight,childHeightInParent); } if(i == childCount -1){ //如果是最後一個Child,要將此次View所在的行高加上 wrapContentHeight += lineHeight; //比較最後一行與之前的最大寬度進行比較,去打的作為wrap_content的寬度 wrapContentWidth = Math.max(lineWidth,wrapContentWidth); } } setMeasuredDimension(widthMeasureMode == MeasureSpec.EXACTLY ? mWidth:wrapContentWidth+getPaddingLeft()+getPaddingRight(), heightMeasureMode == MeasureSpec.EXACTLY ? mHeight:wrapContentHeight+getPaddingTop() + getPaddingBottom()); } List<List<View>> allchildViews = new ArrayList<>(); List<Integer> lineHeights = new ArrayList<>(); @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { allchildViews.clear(); lineHeights.clear(); int mwidth = getWidth(); //記錄每一行的寬度和高度 int lineWidth = 0; int lineHeight = 0; List<View> lineViews = new ArrayList<>(); int childCount = getChildCount(); for(int i = 0 ; i < childCount ; i++){ View child = getChildAt(i); MarginLayoutParams layoutParams = (MarginLayoutParams) child.getLayoutParams(); int childWidth = child.getMeasuredWidth()+ layoutParams.leftMargin + layoutParams.rightMargin;; int childHeight = child.getMeasuredHeight()+ layoutParams.topMargin + layoutParams.bottomMargin;; if(lineWidth + childWidth > mwidth -getPaddingLeft() - getPaddingRight()){ //换行 lineHeights.add(lineHeight); lineWidth = 0; lineHeight = childHeight; allchildViews.add(lineViews); lineViews = new ArrayList<>(); } //不换行 lineWidth += childWidth; lineHeight = Math.max(childHeight,lineHeight); lineViews.add(child); if(i == childCount -1){ //要將此次View所在的行高加上最后一行的行高 lineHeights.add(lineHeight); //左后一行的view allchildViews.add(lineViews); } } int left = getPaddingLeft(); int top = getPaddingTop(); int lineNumbers = lineHeights.size(); for(int i = 0 ; i < lineNumbers ; i++){ lineViews = allchildViews.get(i); lineHeight = lineHeights.get(i); for(int j = 0 ; j < lineViews.size() ; j++){ View child = lineViews.get(j); if(child.getVisibility() == View.GONE){ continue; } MarginLayoutParams layoutParams = (MarginLayoutParams) child.getLayoutParams(); int childTopInparent = top + layoutParams.topMargin; int childBottomInparent = top + layoutParams.topMargin +child.getMeasuredHeight(); int childLeftInparent = left + layoutParams.leftMargin; int childRightInparent = left + layoutParams.leftMargin + child.getMeasuredWidth() ; child.layout(childLeftInparent,childTopInparent,childRightInparent,childBottomInparent); //下一个View的left其实点 left += layoutParams.leftMargin + child.getMeasuredWidth()+ layoutParams.rightMargin; } //每一行完毕后,left又回到最左边,二top则增加了一行 left = getPaddingLeft(); top += lineHeight; } } /** * 指定我們自己想要的LayoutParams,如果現有的LayoutParams不能滿足需求,我們科以自定義 */ @Override public LayoutParams generateLayoutParams(AttributeSet attrs) { return new MarginLayoutParams(getContext(),attrs); } }
源码:http://pan.baidu.com/s/1jG1yrvo