子控制元件根據父控制元件行寬自動換行---LineWrapLayout實現
阿新 • • 發佈:2019-01-29
一些帶搜尋功能的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());
}
});
}