目录
1.效果
有搜索框的地方经常可以看到这样的自定义控件,来保存搜索历史。
2.分析
1)attr属性提取
自定义容器Viewgroup,通过容器的addView,将历史搜索记录的textview传入,容器通过layout,决定textview的摆放位置。除了需要在自定义Viewgroup中处理子控件的与Viewgroup的padding,以及处理子控件距离Viewgroup的margin,还需要处理子控件与子控件之间的间距,为了易维护,可以将其设置为自定义属性
<declare-styleable name="TagsLayout">
<attr name="tagVerticalSpace" format="dimension" />
<attr name="tagHorizontalSpace" format="dimension" />
</declare-styleable>
2)自定义类代码
public class RecoderViewGroup extends ViewGroup {
private int childHorizontalSpace;
private int childVerticalSpace;
public RecoderViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray attrArray = context.obtainStyledAttributes(attrs, R.styleable.TagsLayout);
if (attrArray != null) {
childHorizontalSpace = attrArray.getDimensionPixelSize(R.styleable.TagsLayout_tagHorizontalSpace, 0);
childVerticalSpace = attrArray.getDimensionPixelSize(R.styleable.TagsLayout_tagVerticalSpace, 0);
//回收
attrArray.recycle();
}
}
//这个方法主要是用于父容器添加子View时调用,用于生成和此容器类型相匹配的布局参数类。
@Override
protected LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
}
//设置view的测量模式和大小
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
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 width = 0;
int height = 0;//容器的总体高度
int lineWidth = 0;//记录每一行的宽度,width不断取最大宽度
int lineHeight = 0;//每一行的高度,会累加至height
//额外值获取
int count = getChildCount();
int left = getPaddingLeft();
int right = getPaddingRight();
int top = getPaddingTop();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
if (child.getVisibility() == GONE)
continue;
// 测量child
measureChild(child, widthMeasureSpec, heightMeasureSpec);
// child宽度+自定义间隔=实际占宽
int childWidth = child.getMeasuredWidth() + childHorizontalSpace;
// child高度+自定义间隔=实际占高(还要考虑child自身设置margin影响)
int childHeight = child.getMeasuredHeight() + childVerticalSpace;
// child实际宽高+左右margin
LayoutParams lp = child.getLayoutParams();
if (lp != null && lp instanceof MarginLayoutParams) {
MarginLayoutParams params = (MarginLayoutParams) lp;
childWidth += params.leftMargin + params.rightMargin;
childHeight += params.topMargin + params.bottomMargin;
}
//当前行宽+child宽小于容器宽
if (lineWidth + childWidth <= sizeWidth - left - right) {
//将位置信息绑定到child的tag上
child.setTag(new Location(lineWidth + left, top + height,
lineWidth + childWidth + left - childHorizontalSpace,
height + childHeight + top - childVerticalSpace));
lineWidth += childWidth;
lineHeight = Math.max(lineHeight, childHeight);
}
//加入当前child,如果超出最大宽度,将没加入前的宽度给width,类加height 然后开启新行
else {
lineWidth = childWidth; // 记录新行的宽度
width = Math.max(lineWidth, childWidth); // 取最大的
height += lineHeight;//总体高度叠加
lineHeight = childHeight;// 记录新行的高度
child.setTag(new Location(left, top + height,
childWidth + left - childHorizontalSpace,
height + childHeight + top - childVerticalSpace));
}
}
width = Math.max(width, lineWidth) + getPaddingLeft() + getPaddingRight();
sizeHeight += getPaddingTop() + getPaddingBottom();
height += lineHeight + getPaddingTop() + getPaddingBottom();
//EXACTLY模式用自身测量值 否则将自己的测量值作为最终值
setMeasuredDimension(
(modeWidth == MeasureSpec.EXACTLY) ? sizeWidth : width,
(modeHeight == MeasureSpec.EXACTLY) ? sizeHeight : height);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int count = getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
if (child.getVisibility() == GONE)
continue;
Location location = (Location) child.getTag();
child.layout(location.left, location.top, location.right, location.bottom);
}
}
//记录位置
public class Location {
public Location(int left, int top, int right, int bottom) {
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
}
public int left;
public int top;
public int right;
public int bottom;
}
}
3)添加控件至viewGroup
final TextView textView = new TextView(SearchActivity.this);
textView.setText(list.get(i));
textView.setMaxLines(1);
textView.setGravity(Gravity.CENTER);
textView.setPadding(DensityUtil.dip2px(AppUtils.getAppContext(), 10),
DensityUtil.dip2px(AppUtils.getAppContext(), 2),
DensityUtil.dip2px(AppUtils.getAppContext(), 10),
DensityUtil.dip2px(AppUtils.getAppContext(), 2));
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13);
textView.setEllipsize(TextUtils.TruncateAt.END);
textView.setTextColor(getResources().getColor(R.color.tx_color_333333));
textView.setBackgroundResource(R.drawable.bt_xml_15radiu_brownsolid);
recoderVg.addView(textView, lp);