android 自定义view 流式布局

本文来自 慕课网视频学习 以及 github上源码总结 

hyman 的视频 链接:http://www.imooc.com/learn/237

tagGroup的github链接: https://github.com/2dxgujun/AndroidTagGroup


上图:






上原理:

自定义控件 流式布局 (关键字搜索 、热门标签)

自定义ViewGroup 
1、 onMeasure :测量子View 的宽和高 ,设置自己的宽和高
2、 onLayout : 设置子View 的位置


onMeasure : 根据子View 的布局文件 ,为子View 设置 测量模式 和 测量值


测量 = 测量模式 + 测量值


测量模式: 3 种 (MeasusreSpec)
EXACTLY : 100dp ,match_parent;
AT_MOS : wrap_content 
UNSEPCIFIED : 子View 想多大就多大 


LayoutParams
ViewGroup -- LayoutParams 
子View.getLayoutParams(); -----> 父View 的LayoutParams ;

override the method 

generateLayoutParams();

我们要写的流式布局  使用系统的MarginLayoutparams 即可


特别注意: 核心逻辑 就是 分别在  onMeasure 和 onLayout   进行view填充时的换行和不换行的逻辑 宽和高的计算 处理 以及最后一行的处理

     其中包括 父view的最终width 和 height的 获取 子view的left 和top的计算

上源码:


package diyview;

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

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

public class FlowLayout extends ViewGroup {

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

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

	public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);
	}


	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
		int modeWidth  = MeasureSpec.getMode(widthMeasureSpec);
//		int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
		int modeHeight  = MeasureSpec.getMode(heightMeasureSpec);
		
		int width = 0;
		int height = 0;
		
		int lineWidth = 0;
		int lineHeight = 0;
		
		int cCount = getChildCount();
		if (cCount>0) {
			for(int i = 0; i<cCount;i++){
				View child = getChildAt(i);
				measureChild(child, widthMeasureSpec, heightMeasureSpec);
				MarginLayoutParams lp  = (MarginLayoutParams) child.getLayoutParams();
				
				int childWidth = child.getMeasuredWidth()+lp.leftMargin+lp.rightMargin;
				int childHeight = child.getMeasuredHeight()+lp.topMargin+lp.bottomMargin;
				
				if (lineWidth+childWidth>sizeWidth-getPaddingLeft()-getPaddingRight()) {
					//换行
					width = Math.max(width, lineWidth);
					lineWidth = childWidth;
					height+=lineHeight;
					lineHeight = childHeight;
				}else {
					lineWidth+= childWidth;
					lineHeight = Math.max(lineHeight,childHeight);
				}
				if (i==cCount-1) {
					//最后一行
					width = Math.max(width, lineWidth);
					height+=lineHeight;
				}
			
			}
			setMeasuredDimension(modeWidth==MeasureSpec.EXACTLY?widthMeasureSpec:width+getPaddingLeft()+getPaddingRight(), modeHeight==MeasureSpec.EXACTLY?heightMeasureSpec:height+getPaddingBottom()+getPaddingTop());
		}
		
		
	}
	//所有子View的集合
	private List<List<View>> mAllViews = new ArrayList<List<View>>();
	//每一行的行高
	private List<Integer> mLineHeight = new ArrayList<Integer>();
	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		mAllViews.clear();
		mLineHeight.clear();
		//自定义View的宽
		int width =getWidth();
		int lineWidth = 0;
		int lineHeight = 0;
		List<View> lineViews = new ArrayList<View>();
		int cCount = getChildCount();
		for (int i = 0; i < cCount; i++) {
			View child  = getChildAt(i);
			MarginLayoutParams lp  = (MarginLayoutParams) child.getLayoutParams();
			if (lineWidth+child.getMeasuredWidth()+lp.leftMargin+lp.rightMargin>width-getPaddingLeft()-getPaddingRight()) {
				mAllViews.add(lineViews);
				mLineHeight.add(lineHeight);
				
				//重置 下一行的行高 行宽
				lineWidth = 0;
				lineHeight =child.getMeasuredHeight()+lp.topMargin+lp.bottomMargin;
				
				lineViews = new ArrayList<View>();
			}
			lineViews.add(child);
			lineWidth += child.getMeasuredWidth()+lp.leftMargin+lp.rightMargin;
			lineHeight  = Math.max(lineHeight,child.getMeasuredHeight()+lp.topMargin+lp.bottomMargin);
		}
		//for end 
		mAllViews.add(lineViews);
		mLineHeight.add(lineHeight);
		
		//子View的位置
		int left = getPaddingLeft();
		int top = getPaddingTop();
		int lineSum = mAllViews.size();
		
		for (int i = 0; i < lineSum; i++) {
			lineViews= mAllViews.get(i);
			lineHeight = mLineHeight.get(i);
			for (int j = 0; j < lineViews.size(); j++) {
				View child = lineViews.get(j);
				if (child.getVisibility() == View.GONE) {
					continue;
				}
				MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
				
				int lc = left+lp.leftMargin;
				int tc = top+lp.topMargin;
				int rc  =lc+child.getMeasuredWidth();
				int bc = tc +child.getMeasuredHeight();
				//为子View 进行布局
				child.layout(lc, tc, rc, bc);
				left+=child.getMeasuredWidth()+lp.leftMargin+lp.rightMargin;
			}
			left = getPaddingLeft();
			top +=lineHeight;
			
		}
		
	}
	/**
	 * 当前对应的ViewGroup对应的LayoutParams
	 */
	@Override
	public LayoutParams generateLayoutParams(AttributeSet attrs) {
		return new MarginLayoutParams(getContext(),attrs);
	}

}

public class ThreeActivity extends Activity {

	FlowLayout layout;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.flow_xml);

		layout = (FlowLayout) findViewById(R.id.flowlayout);

		String[] strs = new String[] { "1234", "劉備", "張飛", "關羽", "藝術", "国家",
				"我是小明小红小森", "东方红", "红星照我去战斗", "红星闪闪", "这一刻", "西湖的水", "红颜旧" };
		for (int i = 0; i < strs.length; i++) {
			Button b = new Button(this);
			MarginLayoutParams lp = new MarginLayoutParams(MarginLayoutParams.WRAP_CONTENT, MarginLayoutParams.WRAP_CONTENT);
			b.setText(strs[i]);
			b.setLayoutParams(lp);
			layout.addView(b);
		}
	}

}


再看一下 tagGroup的源码:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
	{
		final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
		final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
		final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
		final int heightSize = MeasureSpec.getSize(heightMeasureSpec);

		measureChildren(widthMeasureSpec, heightMeasureSpec);

		int width = 0;
		int height = 0;

		int row = 0; // The row counter.
		int rowWidth = 0; // Calc the current row width.
		int rowMaxHeight = 0; // Calc the max tag height, in current row.

		final int count = getChildCount();
		for (int i = 0; i < count; i++)
		{
			final View child = getChildAt(i);
			final int childWidth = child.getMeasuredWidth();
			final int childHeight = child.getMeasuredHeight();

			if (child.getVisibility() != GONE)
			{
				rowWidth += childWidth;
				if (rowWidth > widthSize)
				{ // Next line.
					rowWidth = childWidth; // The next row width.
					height += rowMaxHeight + verticalSpacing;
					rowMaxHeight = childHeight; // The next row max height.
					row++;
				} else
				{ // This line.
					rowMaxHeight = Math.max(rowMaxHeight, childHeight);
				}
				rowWidth += horizontalSpacing;
			}
		}
		// Account for the last row height.
		height += rowMaxHeight;

		// Account for the padding too.
		height += getPaddingTop() + getPaddingBottom();

		// If the tags grouped in one row, set the width to wrap the tags.
		if (row == 0)
		{
			width = rowWidth;
			width += getPaddingLeft() + getPaddingRight();
		} else
		{// If the tags grouped exceed one line, set the width to match the
			// parent.
			width = widthSize;
		}

		setMeasuredDimension(widthMode == MeasureSpec.EXACTLY ? widthSize
				: width, heightMode == MeasureSpec.EXACTLY ? heightSize
				: height);
	}

	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b)
	{
		final int parentLeft = getPaddingLeft();
		final int parentRight = r - l - getPaddingRight();
		final int parentTop = getPaddingTop();
		final int parentBottom = b - t - getPaddingBottom();

		int childLeft = parentLeft;
		int childTop = parentTop;

		int rowMaxHeight = 0;

		final int count = getChildCount();
		for (int i = 0; i < count; i++)
		{
			final View child = getChildAt(i);
			final int width = child.getMeasuredWidth();
			final int height = child.getMeasuredHeight();

			if (child.getVisibility() != GONE)
			{
				if (childLeft + width > parentRight)
				{ // Next line
					childLeft = parentLeft;
					childTop += rowMaxHeight + verticalSpacing;
					rowMaxHeight = height;
				} else
				{
					rowMaxHeight = Math.max(rowMaxHeight, height);
				}
				child.layout(childLeft, childTop, childLeft + width, childTop
						+ height);

				childLeft += width + horizontalSpacing;
			}
		}
	}
		@Override
	public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs)
	{
		return new TagGroup.LayoutParams(getContext(), attrs);
	}

		/**
	 * Per-child layout information for layouts.
	 */
	public static class LayoutParams extends ViewGroup.LayoutParams
	{
		public LayoutParams(Context c, AttributeSet attrs)
		{
			super(c, attrs);
		}

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







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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值