1. 程式人生 > >子控制元件根據父控制元件行寬自動換行---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());
			}
		});
	}