现在的位置: 首页 > 综合 > 正文

FlowLayout

2018年05月16日 ⁄ 综合 ⁄ 共 9116字 ⁄ 字号 评论关闭
package org.apmem.tools.layouts;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import org.apmem.tools.R;
/**
 * 注意:这个东西并未把左边、上边、下边、右边这两块区域给计算进去,所以它的水平和垂直间距对这四边是不起作用的,不过,
 * 也有优点就是它不会影响周边控件,如果需要为四周写东西,则只需要手动加上即可。
 *
 */
public class FlowLayout extends ViewGroup {
    public static final int HORIZONTAL = 0;
    public static final int VERTICAL = 1;

    private int horizontalSpacing = 0;
    private int verticalSpacing = 0;
    private int orientation = 0;
//    private boolean debugDraw = true;

    public FlowLayout(Context context) {
        super(context);

        this.readStyleParameters(context, null);
    }

    public FlowLayout(Context context, AttributeSet attributeSet) {
        super(context, attributeSet);

        this.readStyleParameters(context, attributeSet);
    }

    public FlowLayout(Context context, AttributeSet attributeSet, int defStyle) {
        super(context, attributeSet, defStyle);

        this.readStyleParameters(context, attributeSet);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int sizeWidth = MeasureSpec.getSize(widthMeasureSpec) - this.getPaddingRight() - this.getPaddingLeft();
        int sizeHeight = MeasureSpec.getSize(heightMeasureSpec) - this.getPaddingTop() - this.getPaddingBottom();

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

        int size;//总宽度,也是它们所能容纳的最大宽度
        int mode;

        if (orientation == HORIZONTAL) {
            size = sizeWidth;
            mode = modeWidth;
        } else {
            size = sizeHeight;
            mode = modeHeight;
        }

        int lineThicknessWithSpacing = 0;//带间隔的折线高度
        int lineThickness = 0;//折线总高度
        int lineLengthWithSpacing = 0;//带间隔的折线总长度,其实它只是一个中间值
        int lineLength;//其实是总长度,包含了控件和间隔的长度

        int prevLinePosition = 0;//表示Y轴上的位置

        int controlMaxLength = 0;//控制的最大长度
        int controlMaxThickness = 0;//控制的最大高度

        final int count = getChildCount();
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() == GONE) {
                continue;
            }

            child.measure(
            		//如果确切知道大小的话就让其自适应,否则按照最大的给它。
                    MeasureSpec.makeMeasureSpec(sizeWidth, modeWidth == MeasureSpec.EXACTLY ? MeasureSpec.AT_MOST : modeWidth),
                    MeasureSpec.makeMeasureSpec(sizeHeight, modeHeight == MeasureSpec.EXACTLY ? MeasureSpec.AT_MOST : modeHeight)
            );

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

            int hSpacing = this.getHorizontalSpacing(lp);//得到水平间距
            int vSpacing = this.getVerticalSpacing(lp);//得到垂直间距

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

            int childLength;//子view的长度
            int childThickness;//子view的高度
            int spacingLength;//子view的间距长度
            int spacingThickness;//子view的间距高度

            if (orientation == HORIZONTAL) {
                childLength = childWidth;
                childThickness = childHeight;
                spacingLength = hSpacing;
                spacingThickness = vSpacing;
            } else {
                childLength = childHeight;
                childThickness = childWidth;
                spacingLength = vSpacing;
                spacingThickness = hSpacing;
            }
//            这个lineLengthWithSpacing很精妙,每一行的第一个长度为item实际的宽度,第一个之后就是实际占用的宽度,妈的
            //这个看了好久才明白
            lineLength = lineLengthWithSpacing + childLength;//已占用的总长度,
            lineLengthWithSpacing = lineLength + spacingLength;//带间距的已占用总长度

            boolean newLine = lp.newLine || (mode != MeasureSpec.UNSPECIFIED && lineLength > size);
            if (newLine) {
                prevLinePosition = prevLinePosition + lineThicknessWithSpacing;

                lineThickness = childThickness;
                lineLength = childLength;
                lineThicknessWithSpacing = childThickness + spacingThickness;
                lineLengthWithSpacing = lineLength + spacingLength;
            }
            //带间距的总高度
            lineThicknessWithSpacing = Math.max(lineThicknessWithSpacing, childThickness + spacingThickness);
            //不带间距的总高度
            lineThickness = Math.max(lineThickness, childThickness);
            //以左上角为标准
            int posX;
            int posY;
            if (orientation == HORIZONTAL) {
                posX = getPaddingLeft() + lineLength - childLength;
                posY = getPaddingTop() + prevLinePosition;
            } else {
                posX = getPaddingLeft() + prevLinePosition;
                posY = getPaddingTop() + lineLength - childHeight;
            }
            lp.setPosition(posX, posY);
            System.out.println("lineLength:"+lineLength);
            controlMaxLength = Math.max(controlMaxLength, lineLength);
            System.out.println("controlMaxLength:"+controlMaxLength);
            System.out.println("prevLinePosition:"+prevLinePosition+"  lineThickness"+lineThickness);
            //这个最大高度也很精妙prevLinePosition表示包含了控件高度和间距,但是最后一个则只需要加上 控件的本来高度,正好是完整的。	
            controlMaxThickness = prevLinePosition + lineThickness;
        }

        /* need to take paddings into account */
        if (orientation == HORIZONTAL) {
            controlMaxLength += getPaddingLeft() + getPaddingRight();
            controlMaxThickness += getPaddingBottom() + getPaddingTop();
        } else {
            controlMaxLength += getPaddingBottom() + getPaddingTop();
            controlMaxThickness += getPaddingLeft() + getPaddingRight();
        }

        if (orientation == HORIZONTAL) {
            this.setMeasuredDimension(resolveSize(controlMaxLength, widthMeasureSpec), resolveSize(controlMaxThickness, heightMeasureSpec));
        } else {
            this.setMeasuredDimension(resolveSize(controlMaxThickness, widthMeasureSpec), resolveSize(controlMaxLength, heightMeasureSpec));
        }
    }

    private int getVerticalSpacing(LayoutParams lp) {
        int vSpacing;
        if (lp.verticalSpacingSpecified()) {
            vSpacing = lp.verticalSpacing;
        } else {
            vSpacing = this.verticalSpacing;
        }
        return vSpacing;
    }

    private int getHorizontalSpacing(LayoutParams lp) {
        int hSpacing;
        //如果layoutParamters里面有水平间隔就用属性里面有值,如果没有就用布局里面的自定义属性,
        //即,优化使用参数里面的自定义间距属性的值,否则再使用布局里面自定义属性的值
        if (lp.horizontalSpacingSpecified()) {
            hSpacing = lp.horizontalSpacing;
        } else {
            hSpacing = this.horizontalSpacing;
        }
        return hSpacing;
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        final int count = getChildCount();
        for (int i = 0; i < count; i++) {
            View child = getChildAt(i);
            LayoutParams lp = (LayoutParams) child.getLayoutParams();
            child.layout(lp.x, lp.y, lp.x + child.getMeasuredWidth(), lp.y + child.getMeasuredHeight());
        }
    }

    @Override
    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
        boolean more = super.drawChild(canvas, child, drawingTime);
//        this.drawDebugInfo(canvas, child);
        return more;
    }

    @Override
    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
        return p instanceof LayoutParams;
    }

    @Override
    protected LayoutParams generateDefaultLayoutParams() {
        return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
    }

    @Override
    public LayoutParams generateLayoutParams(AttributeSet attributeSet) {
        return new LayoutParams(getContext(), attributeSet);
    }

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

    private void readStyleParameters(Context context, AttributeSet attributeSet) {
        TypedArray a = context.obtainStyledAttributes(attributeSet, R.styleable.FlowLayout);
        try {
            horizontalSpacing = a.getDimensionPixelSize(R.styleable.FlowLayout_horizontalSpacing, 0);
            verticalSpacing = a.getDimensionPixelSize(R.styleable.FlowLayout_verticalSpacing, 0);
            orientation = a.getInteger(R.styleable.FlowLayout_orientation, HORIZONTAL);
//            debugDraw = a.getBoolean(R.styleable.FlowLayout_debugDraw, false);
        } finally {
            a.recycle();
        }
    }



    public static class LayoutParams extends ViewGroup.LayoutParams {
        private static int NO_SPACING = -1;

        private int x;
        private int y;
        private int horizontalSpacing = NO_SPACING;
        private int verticalSpacing = NO_SPACING;
        private boolean newLine = false;

        public LayoutParams(Context context, AttributeSet attributeSet) {
            super(context, attributeSet);
            this.readStyleParameters(context, attributeSet);
        }

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

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

        public boolean horizontalSpacingSpecified() {
            return horizontalSpacing != NO_SPACING;
        }

        public boolean verticalSpacingSpecified() {
            return verticalSpacing != NO_SPACING;
        }

        public void setPosition(int x, int y) {
            this.x = x;
            this.y = y;
        }

        private void readStyleParameters(Context context, AttributeSet attributeSet) {
            TypedArray a = context.obtainStyledAttributes(attributeSet, R.styleable.FlowLayout_LayoutParams);
            try {
                horizontalSpacing = a.getDimensionPixelSize(R.styleable.FlowLayout_LayoutParams_layout_horizontalSpacing, NO_SPACING);
                verticalSpacing = a.getDimensionPixelSize(R.styleable.FlowLayout_LayoutParams_layout_verticalSpacing, NO_SPACING);
                newLine = a.getBoolean(R.styleable.FlowLayout_LayoutParams_layout_newLine, false);
            } finally {
                a.recycle();
            }
        }
    }
}
</pre><pre code_snippet_id="365746" snippet_file_name="blog_20140527_3_2466645" name="code" class="java">
github上搜索flowlayout
https://github.com/ApmeM/android-flowlayout

附网上能用方法,此法错误,不可取 :

<pre name="code" class="java">package com.example.flowlayout;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;

public class FlowLayout1 extends ViewGroup {

	public FlowLayout1(Context context) {
		super(context);
	}
	
	

	public FlowLayout1(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
	}



	public FlowLayout1(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	//每个View的上下间距
	private int dividerLine = 20;
	//每个View的左右间距
	private int dividerCol = 20;

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		for (int i = 0; i < getChildCount(); i++) {
			final View child = getChildAt(i);
			child.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
		}
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
	}
	/**
	 * 此方案你写在这,永远是一个Demo级别的应用,因为大小已不可改变,它只能占满两个屏幕。
	 * 还有,它的上下左右的距离正好是20,这个东西很神奇,你画图才能明白这些东西。
	 */
	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		final int count = getChildCount();
		int row = 0;
		int lengthX = l;//这两个表示右下角
		int lengthY = t;
		for (int i = 0; i < count; i++) {
			final View child = getChildAt(i);
			int width = child.getMeasuredWidth();
			int height = child.getMeasuredHeight();
			lengthX += width + dividerCol;
			lengthY = t + row*(height+dividerLine)+ height + dividerLine; 
			if (lengthX > r) {
				lengthX = width + l + dividerCol;
				row++;
				lengthY = row * (height+dividerLine) + t + height + dividerLine;
			}
			child.layout(lengthX - width , lengthY - height, lengthX, lengthY);
		}
	}

}


抱歉!评论已关闭.