首页 > 代码库 > 子控件根据父控件行宽自动换行---LineWrapLayout实现

子控件根据父控件行宽自动换行---LineWrapLayout实现


一些带搜索功能的app,在搜索栏下面一般会提供一些关键字供用户选择。

也可以根据用户输入的文字,在下一次使用的时候该文字出现在常用关键字里面,只要轻轻一点就可以搜索了,无需再次输入。

关键字可以动态添加,这就要考虑换行的问题了

废话不多说,先上效果图:

先定义2个自定义属性

 <declare-styleable name="linewarplayout">        
         <attr name="magin" format="integer" />  
         <attr name="itemBg" format="reference"></attr>   
    </declare-styleable>

magin:关键字之间间隔

itemBg:关键字的背景


.......

算了不写了

上代码

package com.tang.linewraplayout;

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

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public class LineWrapLayout extends ViewGroup
{
	
	private int magin = 20;//每个VIEW之间的间距
	private List<List<View>> mAllChildViews = new ArrayList<List<View>>();//所有子控件
	private List<Integer> mLineHeight =new ArrayList<Integer>();//每一行的高度
	public interface OnItemClickListener//点击事件接口
	{
		public void onClick(View view);
	}
	private OnItemClickListener clickListener;
	
	public void setOnItemClickListener(OnItemClickListener clickListener)
	{
		this.clickListener=clickListener;
	}
	private Context context;
	private int bgId=0;//textview背景ID
	public LineWrapLayout(Context context, AttributeSet attrs) 
	{
		super(context, attrs);
		this.context = context;
		TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.linewarplayout);
		magin = a.getInt(R.styleable.linewarplayout_magin, 20);
		bgId = a.getResourceId(R.styleable.linewarplayout_itemBg, R.drawable.text_bg);
	}
	
	/**
	 * 关键字数据
	 * @param data
	 */
	public void setData(List<String> data)
	{
		Log.i("AAA", "setData:"+data.size());
		for(int i = 0;i<data.size();i++)
		{
			TextView textView = new TextView(context);
			textView.setText(data.get(i));
			if(bgId!=0)
				textView.setBackgroundResource(bgId);
			textView.setOnClickListener(new OnClickListener() {
				@Override
				public void onClick(View v) {
					// TODO Auto-generated method stub
					clickListener.onClick(v);
				}
			});
			this.addView(textView);
		}
	}
	@Override
	protected ViewGroup.LayoutParams generateDefaultLayoutParams()
	{
		return new MarginLayoutParams(LayoutParams.WRAP_CONTENT,
				LayoutParams.WRAP_CONTENT);
	}
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		// TODO Auto-generated method stub
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		//取得控件的宽高
		int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
		int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
		//测量模式
		int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
		int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
		
		int mWidth = 0;
		int mHeight = 0;
		
		int lineWidth = 0;
		int lineHeight = 0;

		int mCount = getChildCount();
		for (int i = 0; i < mCount; i++)
		{
			View child = getChildAt(i);
			measureChild(child, widthMeasureSpec, heightMeasureSpec);
			MarginLayoutParams lp = (MarginLayoutParams) child
					.getLayoutParams();
			// 当前子控件占据的宽度和高度+子控件之间的间距
			int childWidth = child.getMeasuredWidth() + magin +lp.leftMargin
					+ lp.rightMargin;
			int childHeight = child.getMeasuredHeight() + magin +lp.topMargin
					+ lp.bottomMargin;
			int temp = lineWidth + childWidth;
			if (temp <= sizeWidth)
			{
				//当新加的子控件宽度+当前行所有子控件之和还小于父控件宽度
				//说明当前行还可以添加
				lineWidth += childWidth;
				lineHeight = Math.max(lineHeight, childHeight);
			} 
			else
			{
				//否则的话就要增加一行了
				mWidth = Math.max(lineWidth, mWidth);// 取最大的
				lineWidth = childWidth; // 重新开启新行,开始记录
				// 加上当前高度,
				mHeight += lineHeight;
				// 开启记录下一行的高度
				lineHeight = childHeight;
			}
		}
		//加上最后一行的高度
		mHeight += lineHeight;
		mWidth = Math.max(mWidth, lineWidth);
		mHeight =Math.max(mHeight, lineHeight);
	
		setMeasuredDimension((modeWidth == MeasureSpec.EXACTLY) ? sizeWidth
				: mWidth, (modeHeight == MeasureSpec.EXACTLY) ? sizeHeight
				: mHeight);
	}
	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) 
	{
		mAllChildViews.clear();
		mLineHeight.clear();
		int width = getWidth();

		int lineWidth = 0;
		int lineHeight = 0;
		// 每一行所有的childView
		List<View> lineViews = new ArrayList<View>();
		int mCount = getChildCount();
		Log.i("AAA", "mCount:"+mCount);
		// 遍历所有
		for (int i = 0; i < mCount; i++)
		{
			View child = getChildAt(i);
			MarginLayoutParams lp = (MarginLayoutParams) child
					.getLayoutParams();
			int childWidth = child.getMeasuredWidth();
			int childHeight = child.getMeasuredHeight();
			int temp = childWidth + magin +lp.leftMargin+ lp.rightMargin + lineWidth;

			// 如果不需要换行
			if (temp <= width)
			{
				lineWidth = temp;
				lineHeight = Math.max(lineHeight, childHeight + magin +lp.topMargin
						+ lp.bottomMargin);
				lineViews.add(child);
			}
			else
			{
				// 记录这一行的高度
				mLineHeight.add(lineHeight);
				// 将当前行的View保存,然后new 一个List保存下一行的child
				mAllChildViews.add(lineViews);
				lineViews = new ArrayList<View>();
				lineViews.add(child);
				lineHeight = childHeight + magin +lp.topMargin+ lp.bottomMargin;
				lineWidth = childWidth + magin +lp.leftMargin+ lp.rightMargin;
			}
		}
		// 将最后一行加入队列
		mLineHeight.add(lineHeight);
		mAllChildViews.add(lineViews);
		
		
		int left = 0;
		int top = 0;
		// 总行数
		int count = mAllChildViews.size();
		for (int i = 0; i < count; i++)
		{
			lineViews = mAllChildViews.get(i);
			lineHeight = mLineHeight.get(i);
			// 遍历当前行所有的View
			for (int j = 0; j < lineViews.size(); j++)
			{
				View child = lineViews.get(j);
				MarginLayoutParams lp = (MarginLayoutParams) child
						.getLayoutParams();

				int lc = left + magin+lp.leftMargin;
				int tc = top + magin+lp.topMargin;
				int rc =lc + child.getMeasuredWidth();
				int bc = tc + child.getMeasuredHeight();

				child.layout(lc, tc, rc, bc);
				
				left += child.getMeasuredWidth() + lp.rightMargin+lp.leftMargin
						+ magin;
			}
			left = 0;
			top += lineHeight;
		}
		
	}

}

在onMeasure中对控件的高度和宽度进行计算

在onlayout中将子控件添加到指定的位置

使用setData将关键字数据集传递进来,生成子控件,添加点击事件

使用OnItemClickListener接口将子控件的点击事件传递出去

注意:若前者计算不正确的话子控件会漏掉一部分,显示不完全


使用:

    <com.tang.linewraplayout.LineWrapLayout
        android:id="@+id/linewarplayout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/editText1"
        android:layout_below="@+id/editText1" 
        android:layout_marginTop="15dp"
        linewarplayout:magin="20"
        linewarplayout:itemBg="@drawable/text_bg"     >
    </com.tang.linewraplayout.LineWrapLayout>

	protected void onCreate(Bundle savedInstanceState) 
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		LineWrapLayout layout = (LineWrapLayout) findViewById(R.id.linewarplayout);
		
		List<String>list = new ArrayList<String>();
		String s="";
		for(int i=0;i<9;i++)
		{	
			s=s+"X";
			String temp ="第"+(i+1)+"个:"+s;
			list.add(temp);
		}
		layout.setData(list);
		layout.setOnItemClickListener(new OnItemClickListener() {
			@Override
			public void onClick(View view) {
				// TODO Auto-generated method stub
				Log.i("AAA", ((TextView)view).getText().toString());
			}
		});
	}



代码下载:http://download.csdn.net/detail/tangnengwu/7822189

子控件根据父控件行宽自动换行---LineWrapLayout实现