android 實現流式佈局FlowLayout


import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;


/**
 * @author YeGuangRong
 *
 */
public class FlowLayout extends ViewGroup{


public FlowLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
}


public FlowLayout(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}


public FlowLayout(Context context) {
super(context);
// TODO Auto-generated constructor stub
}


/**
* 測量獲取佈局的寬和高
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
super.onMeasure(widthMeasureSpec, heightMeasureSpec);

/**
* 此部分可以獲取Viewgroup爲子View提供的測量模式和大小
*/
int widthSize   = MeasureSpec.getSize(widthMeasureSpec);
int widthMode   = MeasureSpec.getMode(widthMeasureSpec);
int heightSize  = MeasureSpec.getSize(heightMeasureSpec);
int heightMode  = MeasureSpec.getMode(heightMeasureSpec);

int childCount  = getChildCount();
int tatolHeight = 0;
int lineWidth   = 0;
int maxWidth    = 0;
//行數
int lineCount  = 0;

//需要將測量子View的測量模式設置爲AT_MOST(對應類似於wrap_content),否則如果使用父類的測量模式會被設爲EXACTLY,這樣就達不到流的效果
int newWidthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.AT_MOST);//如果使用EXACTLY的話每個子控件都會佈滿一行,就達不到我們需要的效果

//遍歷子控件,對每個子控件進行測量,最後得出ViewGroup最終的寬度和高度
for(int i = 0; i< childCount; i++){

View childView = getChildAt(i);
childView.measure(newWidthMeasureSpec, heightMeasureSpec);//開始測量子控件

            MarginLayoutParams marginLayoutParams = (MarginLayoutParams) childView.getLayoutParams();//獲取空見設置的margin值,前提是重寫generateLayoutParams(AttributeSet attrs)這個方法,見下文
            
            
if((lineWidth + childView.getMeasuredWidth() + marginLayoutParams.leftMargin+marginLayoutParams.rightMargin)>widthSize){//當空間疊加後所需要的寬度大於ViewGroup所能提供的最大寬度,則需要換行
tatolHeight = tatolHeight + childView.getMeasuredHeight()+marginLayoutParams.bottomMargin+marginLayoutParams.topMargin;
if(maxWidth < lineWidth){//將寬度最大的那一行作爲最終ViewGroup的寬度
maxWidth = lineWidth;
}
lineWidth = childView.getMeasuredWidth()+marginLayoutParams.leftMargin+marginLayoutParams.rightMargin;
}else{//不換行的情況,在行內將子控件疊加
lineWidth = lineWidth + childView.getMeasuredWidth()+marginLayoutParams.leftMargin+marginLayoutParams.rightMargin;
if(tatolHeight == 0){
tatolHeight= childView.getMeasuredHeight()+marginLayoutParams.bottomMargin+marginLayoutParams.topMargin;
}
}
}


/**
* 設置ViewGroup最終的寬和高
*/
if(maxWidth != 0){
setMeasuredDimension(maxWidth, tatolHeight);
}else{
setMeasuredDimension(lineWidth, tatolHeight);
}
}


/**
* 上面確定ViewGroup的寬和高後,我們需要對ViewGroup裏面的子控件進行擺放,確定每個控件的位置(位置可通過l,t,r,b四個值來設定)。
* l,是控件的左邊距離父控件(此處即爲該ViewGroup)的距離,通過getLeft()可獲得其值
* t,是控件的上邊距離距離其父控件的距離,通過getTop()可獲得其值.
* r,是控件的右邊距離距離其父控件的距離,通過getRight()可獲得其值.
* b,是控件的底邊距離距離其父控件的距離,通過getBottom()可獲得其值.
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// TODO Auto-generated method stub

int childCount = getChildCount();
int left = 0,right = 0,top = 0,bottom = 0;
int measureWidth = getMeasuredWidth();
int measureHeight= getMeasuredHeight();
// Log.e("measureHeight", "measureHeight = "+measureHeight+" measureWidth = "+measureWidth);

for(int i = 0; i< childCount; i++){
View childView = getChildAt(i);
MarginLayoutParams marginLayoutParams = (MarginLayoutParams) childView.getLayoutParams();
right          = left + childView.getMeasuredWidth()+marginLayoutParams.rightMargin;
bottom         = top + childView.getMeasuredHeight()+marginLayoutParams.bottomMargin;
if(i == 0){
left = marginLayoutParams.leftMargin;
}


if(right >= measureWidth){//如果擺放的子控件的右邊大於ViewGroup的最大寬度 ,則將控件從下一行開始擺放
top  = bottom+marginLayoutParams.topMargin;
left = marginLayoutParams.leftMargin;
right= left + childView.getMeasuredWidth()+marginLayoutParams.rightMargin;
bottom    = top + childView.getMeasuredHeight()+marginLayoutParams.bottomMargin;
childView.layout(left, top, right, bottom);
left = right+marginLayoutParams.leftMargin;
}else{//不用換行時,從左到右擺放子控件
childView.layout(left, top, right, bottom);
left = right+marginLayoutParams.leftMargin;
}
}
}

@Override  
public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs)  
{  
   return new MarginLayoutParams(getContext(), attrs);  
}  


}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章