Android 流式布局FlowLayout

这个是在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!是不是很简单呢。自己也是正在学习中,如果有什么不对的地方,欢迎指正

转载请注明出处


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值