在我刚学Android的时候,看到b站的手机端app里,在显示搜索热词的时候有这样一个效果:
我当时觉得很神奇,直到后来某一天我突然想明白是怎么回事了。
这次就带来这样一个控件:可以自定义添加标签,并且新添加的标签可以根据其长度,如果当前行放不下的话自动换到下一行。
首先说一下实现思路:我们可以把整个东西看成是一个纵向排布的LinearLayout,里面的每一行内容就是一个横向排布的子
LinearLayout里装若干个TextView,所谓的”自动换行“事实上就是判断新增加的TextView在放入后是否会超出其右边界,如
超出则新建一个横向排布的子LinearLayout,即新的一行,把TextView放入其中。现在的问题就变成有没有办法知道一个已知内
容的TextView的宽度?当然有办法,Paint类下的measureText方法提供了这个功能。
原理讲完,下面上控件本体:
SelfAdaptionColunmLayout.java:
public class SelfAdaptionColumnLayout extends LinearLayout {
// 图标位于标签左边
public static final int ICON_LEFT = 0x001;
// 图标位于标签右边
public static final int ICON_RIGHT = 0x002;
private static final String KEY_TEXTVIEW = "KAY_TEXTVIEW";
private static final String KEY_TEXTITEM = "KEY_TEXTITEM";
private Context context;
private ArrayList<HashMap> list;
private int layoutWidth;
// 行间距
private int lineMargin = 10;
// 列间距,即同一行相邻标签之间的距离
private int columnMargin = 10;
// 默认标签文字颜色
private int defaultColor = Color.parseColor("#000000");
// 默认标签文字大小
private int defaultSize = 16;
// 标签内的图标位置
private int iconGravity = ICON_LEFT;
// 标签内的图标距离文字的距离
private int iconPadding = 5;
private int currentLength = 0;
// 标签点击回调
private OnItemClickListener listener;
public SelfAdaptionColumnLayout(Context context) {
this(context, null);
}
public SelfAdaptionColumnLayout(Context context, AttributeSet attrs) {
this(context, attrs, -1);
}
public SelfAdaptionColumnLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
/**
* 初始化方法
*
* @param context
*/
private void init(Context context) {
this.context = context;
list = new ArrayList<>();
setOrientation(VERTICAL);
setGravity(Gravity.LEFT);
getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
getViewTreeObserver().removeGlobalOnLayoutListener(this);
layoutWidth = getWidth();
notifyDataSetChanged();
}
});
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
if (getChildCount() != 0) {
throw new RuntimeException("layout should not have any child");
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
layoutWidth = getMeasuredWidth();
}
/**
* 绘制标签
*
* @param position
* @param textview
* @param item
*/
private void drawText(final int position, TextView textview, TextItem item) {
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setTextSize(item.getTextSize() != 0 ? item.getTextSize() : defaultSize);
if (item.getIcon() != null && item.isShowIcon()) {
item.getIcon().setBounds(0, 0, dp2px(item.getIconSize()), dp2px(item.getIconSize()));
}
if (getChildCount() == 0) {
LinearLayout parent = new LinearLayout(context);
parent.setGravity(Gravity.CENTER_VERTICAL);
parent.setOrientation(HORIZONTAL);
textview.setText(item.getText());
textview.setGravity(Gravity.CENTER_VERTICAL);
textview.setSingleLine(true);
textview.setTextSize(item.getTextSize() != 0 ? item.getTextSize() : defaultSize);
textview.setTextColor(item.getTextColor() != 0 ? item.getTextColor() : defaultColor);
textview.setPadding(dp2px(item.getTextPaddingLeftRight()), dp2px(item.getTextPaddingTopBottom()), dp2px(item.getTextPaddingLeftRight()), dp2px(item.getTextPaddingTopBottom()));
textview.setCompoundDrawables(item.getIcon() != null && item.isShowIcon() && iconGravity == ICON_LEFT ? item.getIcon() : null, null,
item.getIcon() != null && item.isShowIcon() && iconGravity == ICON_RIGHT ? item.getIcon() : null, null);
textview.setCompoundDrawablePadding(dp2px(iconPadding));
textview.setBackgroundDrawable(item.getTextBackground());
textview.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (listener != null) {
listener.onItemClick(position, ((TextItem) list.get(position).get(KEY_TEXTITEM)).getText());
}
}
});
if (textview.getParent() != null) {
((LinearLayout) textview.getParent()).removeView(textview);
}
parent.addView(textview, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
addView(parent, new LinearLayout.LayoutParams(ViewGroup.Lay