自定义控件之TagGroup
来源:互联网 发布:淘宝客户运营平台在哪 编辑:程序博客网 时间:2024/06/05 16:35
转载请注明出处:http://blog.csdn.net/crazy1235/article/details/74907150
标签瀑布流布局!
实现方式有很多种。
继承LinearLayout
继承ViewGroup
继承别的布局…
继承LinearLayout
继承LinearLayout相对来说,实现比较简单!不需要自己处理onMeasure() 和 onLayout() 函数!
整个布局设置成 LinearLayout.VERTICAL 排列模式!
然后每一行作为一个子LinearLayout,通过 addView(LinearLayout)进去!子LinearLayout是 LinearLayout.HORIZONTAL 排列模式!
当一行addView(tag)的时候超过了最外层的宽度,则需要另起一行!
继承ViewGroup
继承ViewGroup的情况,比较麻烦。需要自己处理onMeasure()和onLayout() !
自定义一套TagGroup 需要注意的地方:
标签的margin值
标签的padding值
TagGroup的padding值
标签的行间距
标签的列间距
字体大小
字体颜色
标签按下的状态
选中标签
……
SuperTagGroup
首先是,属性定义:
<declare-styleable name="SuperTagGroup"> <attr name="horizontal_spacing" format="dimension" /> <attr name="vertical_spacing" format="dimension" /> <attr name="tag_horizontal_padding" format="dimension" /> <attr name="tag_vertical_padding" format="dimension" /> <attr name="tag_text_size" format="dimension" /> <attr name="tag_text_color" format="color" /> <attr name="tag_corner_radius" format="dimension" /> <attr name="tag_border_width" format="dimension" /> <attr name="tag_border_color" format="color" /> <attr name="tag_border_checked_color" format="color" /> <attr name="tag_bg_color" format="color" /> <attr name="tag_bg_checked_color" format="color" /> <attr name="tag_bg_drawable" format="reference" /> <attr name="max_selected_count" format="integer" /> </declare-styleable>
然后在构造函数中,读取出这些属性
public SuperTagGroup(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.SuperTagGroup, defStyleAttr, R.style.SuperTagGroup); horizontalSpace = ta.getDimension(R.styleable.SuperTagGroup_horizontal_spacing, 0); verticalSpace = ta.getDimension(R.styleable.SuperTagGroup_vertical_spacing, 0); horizontalPadding = (int) ta.getDimension(R.styleable.SuperTagGroup_tag_horizontal_padding, 0); verticalPadding = (int) ta.getDimension(R.styleable.SuperTagGroup_tag_vertical_padding, 0); textSize = ta.getDimension(R.styleable.SuperTagGroup_tag_text_size, 0); textColor = ta.getColor(R.styleable.SuperTagGroup_tag_text_color, 0); cornerRadius = ta.getDimension(R.styleable.SuperTagGroup_tag_corner_radius, 0); borderWidth = ta.getDimension(R.styleable.SuperTagGroup_tag_border_width, 0); borderColor = ta.getColor(R.styleable.SuperTagGroup_tag_border_color, 0); tagBorderCheckedColor = ta.getColor(R.styleable.SuperTagGroup_tag_border_checked_color, 0); tagBgColor = ta.getColor(R.styleable.SuperTagGroup_tag_bg_color, 0); tagBgCheckedColor = ta.getColor(R.styleable.SuperTagGroup_tag_bg_checked_color, 0); tagBgDrawable = ta.getResourceId(R.styleable.SuperTagGroup_tag_bg_drawable, 0); maxSelectedNum = ta.getInt(R.styleable.SuperTagGroup_max_s ta.recycle(); init(); }
接着就是重要的onMeasure() 函数
用来对自身布局和子view进行测量,得到测量宽高来进行设置
主要就是针对每个子view进行测量,注意过程中要加上我们设置的padding值,和spacing值!
针对各个子view测量完毕之后,再加上布局的padding值,即可得到布局的宽高!
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { Log.d("SuperTagGroup", "onMeasure"); // width size & mode int widthSize = MeasureSpec.getSize(widthMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec); // height size & mode int heightSize = MeasureSpec.getSize(heightMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); // padding int paddingLeft = getPaddingLeft(); int paddingTop = getPaddingTop(); int paddingRight = getPaddingRight(); int paddingBottom = getPaddingBottom(); // the width & height final result int resultWidth = 0; int resultHeight = 0; int lineWidth = 0; int lineHeight = 0; for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); if (child.getVisibility() == View.GONE) { continue; } if (!(child instanceof SuperTagView)) { throw new IllegalStateException("SuperTagGroup can only has SuperTagView child"); } // measure child measureChild(child, widthMeasureSpec, heightMeasureSpec); // 这里要记得加上子view的margin值// MarginLayoutParams childLayoutParams = (MarginLayoutParams) child.getLayoutParams(); int childWidth = child.getMeasuredWidth(); int childHeight = child.getMeasuredHeight(); lineHeight = Math.max(childHeight, lineHeight); if (lineWidth + childWidth > widthSize - paddingLeft - paddingRight) { // 需要换一行 resultWidth = Math.max(resultWidth, lineWidth); // 每一行都进行比较,最终得到最宽的值 resultHeight += verticalSpace + lineHeight; lineWidth = (int) (childWidth + horizontalSpace); // 新的一行的宽度 lineHeight = childHeight; // 新的一行的高度 } else { // 当前行的宽度 lineWidth += childWidth + horizontalSpace; // 当前行最大的高度 lineHeight = Math.max(lineHeight, childHeight); } // 最后一个, 需要再次比较宽 if (i == getChildCount() - 1) { resultWidth = Math.max(resultWidth, lineWidth); } } resultWidth += paddingRight + paddingLeft; // 布局最终的高度 resultHeight += lineHeight + paddingBottom + paddingTop; setMeasuredDimension(widthMode == MeasureSpec.EXACTLY ? widthSize : resultWidth, heightMode == MeasureSpec.EXACTLY ? heightSize : resultHeight); }
onLayout() 是用来确定各个子view的四个点的位置
@Override protected void onLayout(boolean changed, int l, int t, int r, int b) { // padding int paddingLeft = getPaddingLeft(); int paddingTop = getPaddingTop(); int paddingRight = getPaddingRight(); int lineHeight = 0; // 子view的左侧和顶部位置 int childLeft = paddingLeft; int childTop = paddingTop; for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); if (child.getVisibility() == View.GONE) { continue; } // 找到最后一个append tag if (((SuperTagView) child).isAppendTag()) { if (appendTagIndex != -1 && latestAppendTagView != null) { latestAppendTagView.setAppendTag(false); } appendTagIndex = i; ((SuperTagView) child).setAppendTag(true); latestAppendTagView = (SuperTagView) child; } int childWidth = child.getMeasuredWidth(); int childHeight = child.getMeasuredHeight(); lineHeight = Math.max(lineHeight, childHeight); if (childLeft + childWidth + paddingRight > getWidth()) { // 需要换行 childLeft = paddingLeft; childTop += lineHeight + verticalSpace; lineHeight = childHeight; } // 布局 child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight); // 计算下一个子view X的位置 childLeft += childWidth + horizontalSpace; } }
SuperTagView
针对SuperTagGroup设计了这样一个自定义子view。可以直接在xml布局中添加tag,当然也可以动态的添加删除tag!
还是首先来看 属性定义。
<declare-styleable name="SuperTagView"> <attr name="is_append_tag" format="boolean" /> <attr name="horizontal_padding" format="dimension" /> <attr name="vertical_padding" format="dimension" /> <attr name="corner_radius" format="dimension" /> <attr name="border_width" format="dimension" /> <attr name="border_color" format="color" /> <attr name="border_checked_color" format="color" /> <attr name="bg_color" format="color" /> <attr name="bg_checked_color" format="color" /> </declare-styleable>
然后在构造函数中读取属性
public SuperTagView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.SuperTagView, defStyleAttr, R.style.SuperTagGroup_TagView); isAppendTag = ta.getBoolean(R.styleable.SuperTagView_is_append_tag, false); horizontalPadding = (int) ta.getDimension(R.styleable.SuperTagView_horizontal_padding, SuperTagUtil.dp2px(context, SuperTagUtil.DEFAULT_HORIZONTAL_PADDING)); verticalPadding = (int) ta.getDimension(R.styleable.SuperTagView_vertical_padding, SuperTagUtil.dp2px(context, SuperTagUtil.DEFAULT_VERTICAL_PADDING)); cornerRadius = ta.getDimension(R.styleable.SuperTagView_corner_radius, 0); borderWidth = ta.getDimension(R.styleable.SuperTagView_border_width, 0); borderColor = ta.getColor(R.styleable.SuperTagView_border_color, SuperTagUtil.DEFAULT_TAG_BORDER_COLOR); bgColor = ta.getColor(R.styleable.SuperTagView_bg_color, SuperTagUtil.DEFAULT_TAG_BG_COLOR); borderCheckedColor = ta.getColor(R.styleable.SuperTagView_border_checked_color, 0); bgCheckedColor = ta.getColor(R.styleable.SuperTagView_bg_checked_color, 0); ta.recycle(); init(); }
这里需要注意,不仅要绘制背景色还要绘制边框。还要考虑到选中标签的效果!
所以使用了 StateListDrawable
根据背景色,边框色,选中的颜色来创建StateListDrawable作为背景
private Drawable generateBackgroundDrawable() { StateListDrawable stateListDrawable = new StateListDrawable(); stateListDrawable.addState(CHECK_STATE, new TagBgDrawable(bgCheckedColor, bgRectF, borderCheckedColor, borderRectF, borderWidth, cornerRadius)); stateListDrawable.addState(new int[]{}, new TagBgDrawable(bgColor, bgRectF, borderColor, borderRectF, borderWidth, cornerRadius)); return stateListDrawable; }
@Override protected int[] onCreateDrawableState(int extraSpace) { int[] states = super.onCreateDrawableState(extraSpace + CHECK_STATE.length); if (isChecked()) { mergeDrawableStates(states, CHECK_STATE); } return states; } @Override public void setChecked(boolean checked) { if (this.isChecked != checked) { this.isChecked = checked; refreshDrawableState(); } } @Override public boolean isChecked() { return isChecked; } @Override public void toggle() { setChecked(!isChecked); }
效果图
- 动态添加删除tag
- 最多选中一个tag
- 最多选中5个tag
- 不限选中个数
- 嵌套ScrollView的使用情况
引用开源库
compile 'com.jacksen:supertaggroup:1.0'
gradle 4.0 版本之后,使用下面依赖方式
implementation 'com.jacksen:supertaggroup:1.0'
源码
https://github.com/crazy1235/SuperTagGroup
欢迎star
参考
https://github.com/zhuanghongji/FlowLayout
https://github.com/PepernotenX/FlowLayout
- 自定义控件之TagGroup
- TagGroup自定义标签布局
- 自定义控件之翻页控件
- 自定义控件之组合控件
- 自定义控件之组合控件
- 自定义控件之组合控件
- 自定义控件之自定义属性
- 自定义控件之自定义开关
- 自定义控件之自定义xmlns
- Android自定义控件之自定义日历控件
- Android自定义控件之自定义组合控件
- Android 自定义控件之自定义组合控件
- Android自定义控件之自定义组合控件
- 自定义控件之ListBox
- 自定义控件之直方图
- 自定义控件之表头
- Winform之自定义控件
- 自定义控件之初步
- EffictiveJava学习笔记--创建和销毁对象
- [Linux C语言](学习笔记)标准输入流输出流以及错误流的重定向机制(2)
- K进制下的大数 字符串取模
- JDBC之调用存储过程
- 用c语言实现继承和多态
- 自定义控件之TagGroup
- kubernetes安全测试
- Vue.js的认知
- Linux之旅
- [前端与移动开发] 【前端技术分享】Web前端性能优化的9大问题
- Python编程_Lesson001_python简介和输入输出
- 分布式系统SDK端重试策略
- A1001. A+B Format (20)
- 合并区间