您的位置:首页 > 移动开发 > Android开发

Android 流式布局FlowLayout

2015-03-25 13:57 621 查看
这个是在GitHub上看到的,GitHub地址:https://github.com/blazsolar/FlowLayout

用FlowLayout方便了很多,这是一个很强大的自定义控件,向原作者致敬,下面先放两张我项目中的两张效果图





第一张图在个性标签处用了FlowLayout,第二张图在添加头像处用到了FlowLayout,好了,进入正题

主要用到里边的FlowLayout.java类

package com.wanglai.widget;

import java.util.ArrayList;
import java.util.List;

import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Build;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;

import com.wanglai.main.R;

/**
* FlowLayout will arrange child elements horizontally one next to another. If there is not enough
* space for next view new line will be added.
* FlowLayout会水平排列子元素,当一行的空间不足时,将添加新行
* User: Blaz Solar
* Date: 5/6/13
* Time: 8:17 PM
*/
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public class FlowLayout extends ViewGroup {

private int mGravity = (isIcs() ? Gravity.START : Gravity.LEFT) | Gravity.TOP;

private final List<List<View>> mLines = new ArrayList<List<View>>();
private final List<Integer> mLineHeights = new ArrayList<Integer>();
private final List<Integer> mLineMargins = new ArrayList<Integer>();

public FlowLayout(Context context) {
this(context, null);
}

public FlowLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public FlowLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
/* 绑定values/attrs.xml里面定义的属性*/
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.FlowLayout, defStyle, 0);

try {
int index = a.getInt(R.styleable.FlowLayout_android_gravity, -1);
if(index > 0) {
setGravity(index);
}
} finally {
/*至此属性绑定成功*/
a.recycle();
}

}

/**
* {@inheritDoc}
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);

int sizeWidth = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight();
int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);

int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
int modeHeight = MeasureSpec.getMode(heightMeasureSpec);

int width = 0;
int height = getPaddingTop() + getPaddingBottom();

int lineWidth = 0;
int lineHeight = 0;

int childCount = getChildCount();

for(int i = 0; i < childCount; i++) {

View child = getChildAt(i);
boolean lastChild = i == childCount - 1;

if(child.getVisibility() == View.GONE) {

if(lastChild) {
width = Math.max(width, lineWidth);
height += lineHeight;
}

continue;
}

measureChildWithMargins(child, widthMeasureSpec, lineWidth, heightMeasureSpec, height);

LayoutParams lp = (LayoutParams) child.getLayoutParams();

int childWidthMode = MeasureSpec.AT_MOST;
int childWidthSize = sizeWidth;

int childHeightMode = MeasureSpec.AT_MOST;
int childHeightSize = sizeHeight;

if(lp.width == LayoutParams.MATCH_PARENT) {
childWidthMode = MeasureSpec.EXACTLY    ;
childWidthSize -= lp.leftMargin + lp.rightMargin;
} else if(lp.width >= 0) {
childWidthMode = MeasureSpec.EXACTLY;
childWidthSize = lp.width;
}

if(lp.height >= 0) {
childHeightMode = MeasureSpec.EXACTLY;
childHeightSize = lp.height;
} else if (modeHeight == MeasureSpec.UNSPECIFIED) {
childHeightMode = MeasureSpec.UNSPECIFIED;
childHeightSize = 0;
}

child.measure(
MeasureSpec.makeMeasureSpec(childWidthSize, childWidthMode),
MeasureSpec.makeMeasureSpec(childHeightSize, childHeightMode)
);

int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;

if(lineWidth + childWidth > sizeWidth) {

width = Math.max(width, lineWidth);
lineWidth = childWidth;

height += lineHeight;
lineHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;

} else {
lineWidth += childWidth;
lineHeight = Math.max(lineHeight, child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
}

if(lastChild) {
width = Math.max(width, lineWidth);
height += lineHeight;
}

}

width += getPaddingLeft() + getPaddingRight();

setMeasuredDimension(
(modeWidth == MeasureSpec.EXACTLY) ? sizeWidth : width,
(modeHeight == MeasureSpec.EXACTLY) ? sizeHeight : height);
}

/**
* {@inheritDoc}
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {

mLines.clear();
mLineHeights.clear();
mLineMargins.clear();

int width = getWidth();
int height = getHeight();

int linesSum = getPaddingTop();

int lineWidth = 0;
int lineHeight = 0;
List<View> lineViews = new ArrayList<View>();

float horizontalGravityFactor;
switch ((mGravity & Gravity.HORIZONTAL_GRAVITY_MASK)) {
case Gravity.LEFT:
default:
horizontalGravityFactor = 0;
break;
case Gravity.CENTER_HORIZONTAL:
horizontalGravityFactor = .5f;
break;
case Gravity.RIGHT:
horizontalGravityFactor = 1;
break;
}

for(int i = 0; i < getChildCount(); i++) {

View child = getChildAt(i);

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

LayoutParams lp = (LayoutParams) child.getLayoutParams();

int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
int childHeight = child.getMeasuredHeight() + lp.bottomMargin + lp.topMargin;

if(lineWidth + childWidth > width) {
mLineHeights.add(lineHeight);
mLines.add(lineViews);
mLineMargins.add((int) ((width - lineWidth) * horizontalGravityFactor) + getPaddingLeft());

linesSum += lineHeight;

lineHeight = 0;
lineWidth = 0;
lineViews = new ArrayList<View>();
}

lineWidth += childWidth;
lineHeight = Math.max(lineHeight, childHeight);
lineViews.add(child);
}

mLineHeights.add(lineHeight);
mLines.add(lineViews);
mLineMargins.add((int) ((width - lineWidth) * horizontalGravityFactor) + getPaddingLeft());

linesSum += lineHeight;

int verticalGravityMargin = 0;
switch ((mGravity & Gravity.VERTICAL_GRAVITY_MASK)	) {
case Gravity.TOP:
default:
break;
case Gravity.CENTER_VERTICAL:
verticalGravityMargin = (height - linesSum) / 2;
break;
case Gravity.BOTTOM:
verticalGravityMargin = height - linesSum;
break;
}

int numLines = mLines.size();

int left;
int top = getPaddingTop();

for(int i = 0; i < numLines; i++) {

lineHeight = mLineHeights.get(i);
lineViews = mLines.get(i);
left = mLineMargins.get(i);

int children = lineViews.size();

for(int j = 0; j < children; j++) {

View child = lineViews.get(j);

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

LayoutParams lp = (LayoutParams) child.getLayoutParams();

// if height is match_parent we need to remeasure child to line height
if(lp.height == LayoutParams.MATCH_PARENT) {
int childWidthMode = MeasureSpec.AT_MOST;
int childWidthSize = lineWidth;

if(lp.width == LayoutParams.MATCH_PARENT) {
childWidthMode = MeasureSpec.EXACTLY;
} else if(lp.width >= 0) {
childWidthMode = MeasureSpec.EXACTLY;
childWidthSize = lp.width;
}

child.measure(
MeasureSpec.makeMeasureSpec(childWidthSize, childWidthMode),
MeasureSpec.makeMeasureSpec(lineHeight - lp.topMargin - lp.bottomMargin, MeasureSpec.EXACTLY)
);
}

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

int gravityMargin = 0;

if(Gravity.isVertical(lp.gravity)) {
switch (lp.gravity) {
case Gravity.TOP:
default:
break;
case Gravity.CENTER_VERTICAL:
case Gravity.CENTER:
gravityMargin = (lineHeight - childHeight - lp.topMargin - lp.bottomMargin) / 2 ;
break;
case Gravity.BOTTOM:
gravityMargin = lineHeight - childHeight - lp.topMargin - lp.bottomMargin;
break;
}
}

child.layout(left + lp.leftMargin,
top + lp.topMargin + gravityMargin + verticalGravityMargin,
left + childWidth + lp.leftMargin,
top + childHeight + lp.topMargin + gravityMargin + verticalGravityMargin);

left += childWidth + lp.leftMargin + lp.rightMargin;

}

top += lineHeight;
}

}

@Override
protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new LayoutParams(p);
}

/**
* {@inheritDoc}
*/
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
}

/**
* {@inheritDoc}
*/
@Override
protected LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
}

@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public void setGravity(int gravity) {
if(mGravity != gravity) {
if((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
gravity |= isIcs() ? Gravity.START : Gravity.LEFT;
}

if((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
gravity |= Gravity.TOP;
}

mGravity = gravity;
requestLayout();
}
}

public int getGravity() {
return mGravity;
}

/**
* @return <code>true</code> if device is running ICS or grater version of Android.
*/
private static boolean isIcs() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH;
}

public static class LayoutParams extends MarginLayoutParams {

public int gravity = -1;

public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);

TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.FlowLayout_Layout);

try {
gravity = a.getInt(R.styleable.FlowLayout_Layout_android_layout_gravity, -1);
} finally {
a.recycle();
}
}

public LayoutParams(int width, int height) {
super(width, height);
}

public LayoutParams(ViewGroup.LayoutParams source) {
super(source);
}

}

}
接下来就是在attrs.xml中加入相应的属性代码:
<declare-styleable name="FlowLayout">
<attr name="android:gravity" />
</declare-styleable>
<declare-styleable name="FlowLayout_Layout">
<attr name="android:layout_gravity" />
</declare-styleable>
现在就可以用到你的项目中了:

<com.wanglai.widget.FlowLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/save_signature_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/baidi"
android:gravity="start|top" >

<LinearLayout
style="@style/label_style"
android:background="@drawable/text_shape_yellow"
android:orientation="horizontal" >

<TextView
style="@style/label_text_style"
android:text="这是标签"
android:textColor="@color/listselect" />

<TextView
style="@style/label_text_style"
android:text="5"
android:textColor="@color/listselect" />
</LinearLayout>

<!-- 这里可以加入更多的子元素 -->
</com.wanglai.widget.FlowLayout>
当然,你也可以在java代码中动态添加子元素:

for(int k = 0;k<phonesUserDtos.size();k++){
PhonesUserDto pDto = phonesUserDtos.get(k);
//初始化子布局
View view = LayoutInflater.from(this).inflate(R.layout.wishtop_item, null);
FlowLayout.LayoutParams params = new FlowLayout.LayoutParams(150, 170);
view.setLayoutParams(params);
params.topMargin = 10;
view.setPadding(14, 0, 14, 0);
RoundImageView riView = (RoundImageView) view.findViewById(R.id.wishtop_headImage);
TextView tView = (TextView) view.findViewById(R.id.wishtop_tv_price);
riView.setVisibility(View.VISIBLE);
tView.setVisibility(View.VISIBLE);
String wishTopHead =pDto.getHeadImage();
if (wishTopHead != null && !"".equals(wishTopHead)) {
bitmapUtils.configDefaultLoadingImage(R.drawable.touxiang1);
bitmapUtils.configDefaultLoadFailedImage(R.drawable.touxiang1);
bitmapUtils.display(riView, Constant.AUTO_IMAGE_PATH + wishTopHead);
}
tView.setText(pDto.getRealname() + "");
sb.append(",").append(pDto.getUsername());
square_save_img_layout_wai.addView(view,0);
}
ok!是不是很简单呢。自己也是正在学习中,如果有什么不对的地方,欢迎指正

转载请注明出处
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: