android 动态label,android 实现标签流,动态添加标签并获取

8ceb7611d5f7

Screenrecorder-2019-07-08-21-38-09-988[00-00-00--00-00-13].gif

文章知识了解

学习到的知识点

1、measureChild 减去了 ViewGroup的padding 保证child最大可用空间

2、measureChildWithMargins 减去了ViewGroup的padding和子View的margin 保证child最大可用空间

3、measureChild 后,子View.getMeasuredWidth 是包含子View的padding的

4、自定义VIewGroup中,通过循环遍历调用addView的方法,不会每次都调用onMeasure或onLayout

5、为了优化,父容器建议考虑使用FrameLayout或者Linlayout

6、onMeasure中第二次setMeasuredDimension的参数如果与第一次一致,将不触发onLayout

7、TextView需要设置默认的背景色,如果不设置获取的宽高将会是0,这一点可以通过getDefaulSize的源码知道

8、软键盘的弹出/收缩,也会重新调用onLayout、onmeasure

9、TextView使用setBackground会导致背景有些部分会显示不出来,所以需要使用setBackgroundResource,它是使用的是资源ID,setBackground()和setBackgroundDrawable()的使用是一样的

可以自定义哪些参数?

1、定义选中/未选中的背景色,文字选中/未选中的颜色

2、定义TextView padding 文字左右距离

3、定义文字的大小

4、定义TextView之间水平方向的距离

5、定义TextView之间竖直平方向的距离

6、TextView的高度

7、是否添加新增按钮

8、新增按钮宽度大小

9、可以回调新增的数据

10、按下Enter键可以拿到数据

使用方法

public class MainActivity extends AppCompatActivity implements LabelLayoutView.OnInputValueListener{

private String TAG =MainActivity.class.getSimpleName();

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

final LabelLayoutView labelLayoutView = findViewById(R.id.lablayout);

final List labelModelArrayList = new ArrayList<>();

for (int i=0;i<10;i++){

LabelModel model = new LabelModel();

if (i % 2 == 0){

model.setTextValue("sd");

}else {

model.setTextValue("一周热门话题");

}

labelModelArrayList.add(model);

}

labelLayoutView.setStringList(labelModelArrayList);

labelLayoutView.setOnInputValueListener(this);

findViewById(R.id.bt_main_cleanfocus).setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

labelLayoutView.cleanEditFocus();

}

});

}

@Override

public void onInoutItem(String value) {

Log.i(TAG,value);

}

}

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical">

android:id="@+id/lablayout"

android:layout_width="match_parent"

android:layout_height="wrap_content"/>

android:id="@+id/bt_main_cleanfocus"

android:text="失去焦点"

android:layout_width="wrap_content"

android:layout_height="wrap_content" />

全部代码展示

xml展示

rectanger_co50_solid_grayf7f7f7.xml

xmlns:android="http://schemas.android.com/apk/res/android"

android:shape="rectangle">

android:radius="50dp"/>

android:color="@color/gray_F7F7F7" />

rectanger_co50_solid_zie9e1f6.xml

xmlns:android="http://schemas.android.com/apk/res/android"

android:shape="rectangle">

android:radius="50dp"/>

android:color="@color/zi_E9E1F6" />

attrs.xml

model类展示

public class LabelModel {

private String textValue;

private boolean isClick;

public String getTextValue() {

return textValue;

}

public void setTextValue(String textValue) {

this.textValue = textValue;

}

public boolean isClick() {

return isClick;

}

public void setClick(boolean click) {

isClick = click;

}

}

自定义标签流代码展示

/**

* @date: 2019/7/1 0001

* @author: gaoxiaoxiong

* @description:自定义标签流

**/

public class LabelLayoutView extends ViewGroup {

private String TAG = LabelLayoutView.class.getSimpleName();

private List modelArrayList = new ArrayList<>();

private List textViewList = new ArrayList<>();

private int tVHorizontalSpace = 10;//TextView之间的水平间距

private int tVVerticalSpace = 20;//竖直方向的间距

private int tViewPaddingLeft = 0;

private int tVViewPaddingRight = 0;

private int tVViewTextSize = 0;//文字大小

private int tVViewTextHeight = 0;//TextView的高度

private int tVViewClickTextColor;//文字点中的颜色

private int tVViewDefuTextColor;//文字默认的颜色

private int tVViewDefuBackColor;//默认的颜色

private int tVViewClickBackColor;//点中,背景色的修改

private int edTextMaxInputLength = 6;//最大输入个数

private int edTextWidth = 0;//输入框的宽度

private boolean isNewAdd = false;//是否要含有+号

private int tvAddTextWidth = 0;//定义加号的长度

private Context mContext;

private OnInputValueListener onInputValueListener;

public void setOnInputValueListener(OnInputValueListener onInputValueListener) {

this.onInputValueListener = onInputValueListener;

}

public interface OnInputValueListener {

void onInoutItem(String value);

}

//获取点击中的

public List getChoiceModelList() {

List list = new ArrayList<>();

for (LabelModel model : modelArrayList) {

if (model.isClick()) {

list.add(model);

}

}

return list;

}

/**

* @date: 2019/7/1 0001

* @author: gaoxiaoxiong

* @description:获取所有的 TextView

**/

public List getTextViewList() {

return textViewList;

}

public LabelLayoutView(Context context) {

this(context, null);

}

public LabelLayoutView(Context context, AttributeSet attrs) {

this(context, attrs, 0);

}

public LabelLayoutView(Context context, AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

this.mContext = context;

TypedArray mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.LabelLayoutView);

//定义默认的颜色

tVViewDefuBackColor = mTypedArray.getResourceId(R.styleable.LabelLayoutView_tVViewDefuBackColor, R.drawable.rectanger_co50_solid_grayf7f7f7);

tVViewClickBackColor = mTypedArray.getResourceId(R.styleable.LabelLayoutView_tVViewClickBackColor, R.drawable.rectanger_co50_solid_zie9e1f6);

tVHorizontalSpace = mTypedArray.getDimensionPixelOffset(R.styleable.LabelLayoutView_tVHorizontalSpace, dip2px(14));

tVVerticalSpace = mTypedArray.getDimensionPixelOffset(R.styleable.LabelLayoutView_tVVerticalSpace, dip2px(12));

//定义默认的文字颜色

tVViewDefuTextColor = mTypedArray.getColor(R.styleable.LabelLayoutView_tVViewDefuTextColor, getResources().getColor(R.color.gray_A8A8A8));

//定义选中后的文字颜色

tVViewClickTextColor = mTypedArray.getColor(R.styleable.LabelLayoutView_tVViewDefuTextColor, getResources().getColor(R.color.zi_8C75B3));

tViewPaddingLeft = mTypedArray.getDimensionPixelOffset(R.styleable.LabelLayoutView_tViewPaddingLeft, dip2px(12));

tVViewPaddingRight = mTypedArray.getDimensionPixelOffset(R.styleable.LabelLayoutView_tVViewPaddingRight, dip2px(12));

tVViewTextSize = mTypedArray.getDimensionPixelOffset(R.styleable.LabelLayoutView_tVViewTextSize, 12);

tVViewTextHeight = mTypedArray.getDimensionPixelOffset(R.styleable.LabelLayoutView_tVViewTextHeight, dip2px(25));

edTextWidth =mTypedArray.getDimensionPixelOffset(R.styleable.LabelLayoutView_edTextWidth,dip2px(87)) ;

edTextMaxInputLength =mTypedArray.getInt(R.styleable.LabelLayoutView_edTextMaxInputLength,6);

isNewAdd = mTypedArray.getBoolean(R.styleable.LabelLayoutView_isNewAdd,false);

tvAddTextWidth =mTypedArray.getDimensionPixelOffset(R.styleable.LabelLayoutView_edTextWidth,dip2px(87)) ;

mTypedArray.recycle();

}

public void setStringList(List list) {

if (list.size() > 0) {

removeAllViews();

this.textViewList.clear();

this.modelArrayList.clear();

this.modelArrayList.addAll(list);

for (int i = 0; i < list.size(); i++) {

LabelModel model = list.get(i);

TextView textView = createTextView(i, model.getTextValue());

addView(textView);

}

if (isNewAdd) {

addView(createAddTextView());

}

}

}

//创建TextView

private TextView createTextView(int position, String string) {

TextView textView = new TextView(mContext);

LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, tVViewTextHeight);

textView.setLayoutParams(lp);

textView.setPadding(tViewPaddingLeft, 0, tVViewPaddingRight, 0);

textView.setText(string);

textView.setTextColor(tVViewDefuTextColor);

textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, tVViewTextSize);

textView.setGravity(Gravity.CENTER);

textView.setBackgroundResource(R.drawable.rectanger_co50_solid_grayf7f7f7);//设置的是最底层的颜色

textView.setTag(position);

textView.setOnClickListener(new OnClickListener() {

@Override

public void onClick(View v) {

int i = (int) v.getTag();

LabelModel model = modelArrayList.get(i);

if (model.isClick()) {

model.setClick(false);

textViewList.get(i).setBackgroundResource(tVViewDefuBackColor);

textViewList.get(i).setTextColor(tVViewDefuTextColor);

} else {

model.setClick(true);

textViewList.get(i).setBackgroundResource(tVViewClickBackColor);

textViewList.get(i).setTextColor(tVViewClickTextColor);

}

}

});

textViewList.add(textView);

return textView;

}

//创建一个含有+号的TextView

private TextView createAddTextView() {

final TextView textView = new TextView(mContext);

LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(tvAddTextWidth, tVViewTextHeight);

textView.setLayoutParams(lp);

textView.setPadding(tViewPaddingLeft, 0, tVViewPaddingRight, 0);

textView.setText("+");

textView.setTextColor(tVViewDefuTextColor);

textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, tVViewTextSize);

textView.setGravity(Gravity.CENTER);

textView.setBackgroundResource(R.drawable.rectanger_co50_solid_grayf7f7f7);//设置的是最底层的颜色

textView.setOnClickListener(new OnClickListener() {

@Override

public void onClick(View v) {

createEditText(textView);

}

});

textViewList.add(textView);

return textView;

}

private EditText editText = null;

//创建EditText

private void createEditText(TextView textView) {

this.removeView(textView);

textViewList.remove(textView);

editText = new EditText(mContext);

LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(edTextWidth, tVViewTextHeight);

editText.setLayoutParams(lp);

editText.setHint("请输入");

editText.setMaxLines(1);

editText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(edTextMaxInputLength)});

editText.setPadding(tViewPaddingLeft, 0, tVViewPaddingRight, 0);

editText.setTextColor(tVViewDefuTextColor);

editText.setTextSize(TypedValue.COMPLEX_UNIT_SP, tVViewTextSize);

editText.setGravity(Gravity.CENTER);

editText.setImeOptions(EditorInfo.IME_ACTION_DONE);

editText.setSingleLine();

editText.setInputType(EditorInfo.TYPE_CLASS_TEXT);

editText.setBackgroundResource(R.drawable.rectanger_co50_solid_grayf7f7f7);//设置的是最底层的颜色

//失去焦点,就要保存数据

editText.setOnFocusChangeListener(new OnFocusChangeListener() {

@Override

public void onFocusChange(View v, boolean hasFocus) {

if (editText.getText().toString().length() <=0){

return;

}

if (!hasFocus){//失去焦点

if (onInputValueListener!=null){

LabelModel model = new LabelModel();

model.setTextValue(editText.getText().toString());

model.setClick(false);

modelArrayList.add(model);

LabelLayoutView.this.addView(createTextView(modelArrayList.size()-1,editText.getText().toString()));

onInputValueListener.onInoutItem(editText.toString());

}

editText.setText("");

//移除本EditTextView

LabelLayoutView.this.removeView(editText);

LabelLayoutView.this.addView(createAddTextView());

}

}

});

editText.setOnEditorActionListener(new TextView.OnEditorActionListener() {

@Override

public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {

if (actionId == EditorInfo.IME_ACTION_DONE){

cleanEditFocus();

}

return false;

}

});

addView(editText);

}

//清除焦点事件

public void cleanEditFocus(){

if (editText!=null)

editText.clearFocus();

}

public int dip2px(float dipValue) {

final float scale = Resources.getSystem().getDisplayMetrics().density;

return (int) (dipValue * scale + 0.5f);

}

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

if (getChildCount() <= 0) {

setMeasuredDimension(0, 0);

return;

}

int parentWidth = MeasureSpec.getSize(widthMeasureSpec);

int line = 1;

int currentWidth = 0;

int currentHeight = 0;

for (int i = 0; i < getChildCount(); i++) {

View textView = getChildAt(i);

measureChild(textView, widthMeasureSpec, heightMeasureSpec);

//这里要算出,高度的最大的那个,后面多加一个tVVerticalSpace,是希望底部与TextView有一定的距离

currentHeight = Math.max(currentHeight, textView.getMeasuredHeight() + tVVerticalSpace);

int textViewWidth = textView.getMeasuredWidth();

//一行的最后一个 & 无法放下一个textView和一个间隔

if (parentWidth - currentWidth < textViewWidth + tVHorizontalSpace) {

if (parentWidth - currentWidth >= textViewWidth) {//去掉间隔,判断是否可以放的下

currentWidth = currentWidth + textViewWidth;

} else {//放不下,就要去下一行进行存放

currentWidth = 0;

currentWidth = currentWidth + textViewWidth + tVHorizontalSpace;

line = line + 1;

}

} else {

if (parentWidth - currentWidth >= textViewWidth + tVHorizontalSpace) {

currentWidth = currentWidth + textViewWidth + tVHorizontalSpace;

}

}

}

setMeasuredDimension(parentWidth, line * currentHeight);

}

@Override

protected void onLayout(boolean changed, int l, int t, int r, int b) {

int left = 0;

int top = 0;

int parentWidth = this.getMeasuredWidth();

for (int i = 0; i < getChildCount(); i++) {

View textView = getChildAt(i);

int childWidth = textView.getMeasuredWidth();

int childHeight = textView.getMeasuredHeight();

//一行的最后一个 & 无法放下一个textView和一个间隔

if (parentWidth - left < childWidth + tVHorizontalSpace) {

if (parentWidth - left >= childWidth) {//去掉间隔,判断是否可以放的下

textView.layout(left, top, left + childWidth, childHeight + top);

left = left + childWidth + tVHorizontalSpace;

} else {

left = 0;

top = top + childHeight + tVVerticalSpace;

textView.layout(left, top, left + childWidth, childHeight + top);//从0位置开始绘制,绘制完后就会占位置,所以需要再加上绘制过的

left = left + childWidth + tVHorizontalSpace;

}

} else {

textView.layout(left, top, left + childWidth, childHeight + top);

left = left + childWidth + tVHorizontalSpace;

}

}

}

}