子控件根据父控件行宽自动换行---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

时间: 2024-11-08 20:15:07

子控件根据父控件行宽自动换行---LineWrapLayout实现的相关文章

WPF查找子控件和父控件方法[转帖]

WPF查找子控件和父控件方法 一.查找某种类型的子控件,并返回一个List集合 public List<T> GetChildObjects<T>(DependencyObject obj, Type typename) where T : FrameworkElement { DependencyObject child = null; List<T> childList = new List<T>(); for (int i = 0; i <= V

查找子控件和父控件方法

一.查找某种类型的子控件,并返回一个List集合 public List<T> GetChildObjects<T>(DependencyObject obj, Type typename) where T : FrameworkElement        {            DependencyObject child = null;            List<T> childList = new List<T>(); for (int i

子控件超出父控件响应

// 事件传递给subview - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { CGPoint subviewP = [self convertPoint:point toView:_subview]; if ([_subview pointInside:subviewP withEvent:event]) { return _subview; }else { return [super hitTest:point

父控件、子控件

1 每一个控件其实都是一个容器可以将其他控件放到该控件的内部比较常见的还是将UIView作为容器 2 可以将A控件放入B控件A控件是B控件的子控件B控件是A控件的父控件 3 每一个控制器都有一个UIView控制器本身是不可见能够看到的是控制器的View每一个控制器中都一个UIVIew的属性控制器中管理的所有子控件都是该控件的子控件

Qt子控件样式不生效,因为父控件样式设定

在帮助中搜索qt style sheets 找到这句话: If we want the property to apply only to one specific QLineEdit, we can give it a name using QObject::setObjectName() and use an ID Selector to refer to it: 意思是如果不想子控件继承父控件,就在父控件加# if(m_MainWidget.objectName().isEmpty()){

WPF利用VisualTreeHelper遍历寻找对象的子级对象或者父级对象

简介 本文将完整叙述我利用VisualTreeHelper实现题述功能的全部过程,想直接看函数实现的朋友可以跳到函数实现部分. 或者直接在GitHub上下载源码. 在WPF中我们经常会遇到这种情况:当我们尝试着去寻找窗体或者页面中某个控件的子控件或者父控件的时候,我们只能寻找到它的第一级的逻辑子级对象或者父级对象.当我们想更深入的时候,就没有办法了. 甚至在我们自定义的DataTemplate中的控件,我们都没办法对其访问.比如在ListView中自定义的控件,我们就没办法获取指定位置的控件了.

事件委托应用:在父控件中创建子控件,并接收值

传值过程使用委托方法 定义一个打开按扭,一个文本框 1 /// <summary> 2 /// 增加父控件 3 /// </summary> 4 public void AddParent(BaseControl ctl) 5 { 6 foreach (Control ct2 in this.Parent.Controls) 7 { 8 if (ct2.Name == ctl.Name) { 9 ct2.Focus(); 10 return; 11 } 12 } 13 ctl.Wi

如果希望点击父控件子控件也响应的话, 可以给子控件加如下属性: ?android:duplicateParentState="true"

如果希望点击父控件子控件也响应的话, 可以给子控件加如下属性: android:duplicateParentState="true" 来自为知笔记(Wiz)

五种情况下会刷新控件状态(刷新所有子FWinControls的显示)——从DFM读取数据时、新增加子控件时、重新创建当前控件的句柄时、设置父控件时、显示状态被改变时

五种情况下会刷新控件状态(刷新控件状态才能刷新所有子FWinControls的显示): 在TWinControls.PaintControls中,对所有FWinControls只是重绘了边框,而没有整个重绘这些FWinControl子控件.那么什么时候才整个重绘全部FWinControls呢?这时候,就不是一个单纯的WM_PAINT来解决控件重绘的问题了,而是这个TWinControl.UpdateShowing函数: procedure TWinControl.UpdateShowing; v