[github高级控件]带你走近-自定义标签云
在项目中经常遇到一些标签云的效果,比如城市的选择,景点类型选择,酒店房型选择 这些常见使用标签云效果就比较好了。
今天一步步的写一个标签云view,借鉴于github上TagCloudView
1.准备工作
attr.xml
<resources><declare-styleable name="TagTextViewStyle"><attr name="t_textSize" format="dimension"/><attr name="t_textColor" format="color"/><attr name="t_itemBorder" format="dimension"/><attr name="t_viewBorder" format="dimension"/><attr name="t_tagBackground" format="reference"/><attr name="t_singleLine" format="boolean"/><attr name="t_imageWidth" format="dimension"/><attr name="t_imageHeight" format="dimension"/><attr name="t_rightArrow" format="integer"/><attr name="t_showArrow" format="boolean"/><attr name="t_showMore" format="boolean"/><attr name="t_moreTextStr" format="string"/><attr name="t_moreTextWidth" format="dimension"/></declare-styleable></resources>
然后定义一个TagTextView,继承与ViewGroup,接下来就从attr中获取这些属性
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.TagTextViewStyle, defStyleAttr, defStyleAttr);mTextSize = typedArray.getDimensionPixelSize(R.styleable.TagTextViewStyle_t_textSize, 12);//更多属性自行实现...
2.重写onMeasure
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);//1,计算子view//2,计算tag view 实际需要高度//3,根据高度设置}
计算子view
MeasureSpec封装了父布局传递给子布局的布局要求,每个MeasureSpec代表了一组宽度和高度的要求。一个MeasureSpec由size和mode组成,它有三种模式:UNSPECIFIED(未指定),父元素部队自元素施加任何束缚,子元素可以得到任意想要的大小;EXACTLY(完全),父元素决定自元素的确切大小,子元素将被限定在给定的边界里而忽略它本身大小;AT_MOST(至多),子元素至多达到指定大小的值
这里不指定具体的mode和size作特殊处理,只是使用到size和mode
//计算 ViewGroup 上级容器为其推荐的宽高int widthMode = MeasureSpec.getMode(widthMeasureSpec);int heightMode = MeasureSpec.getMode(heightMeasureSpec);mWidthSize = MeasureSpec.getSize(widthMeasureSpec);mHeightSize = MeasureSpec.getSize(heightMeasureSpec);// 计算出所有的childView的宽和高 measureChildren(widthMeasureSpec, heightMeasureSpec);
measureChildren一行代码就计算了所以的childview的宽,高,这里不具体分析,可以自行查看源码分析measureChildren原理。
计算TagTextView的高度并确定item的位置
如果所有标签没有超过1行,那么TagTextView的高度就是item的高度;如果超过一行,那么高度h=item高度*行数+所有间距
这里实现2中模式:多行模式和单行模式
先介绍怎么实现多行模式
private int getMultiTotalHeight(int totalWidth, int totalHeight) {int childWidth;int childHeight;//遍历所有的子viewfor (int i = 0; i < getChildCount(); i++) {View child = getChildAt(i);childWidth = child.getMeasuredWidth();childHeight = child.getMeasuredHeight();//总的宽度 = 所有子view宽+item间距totalWidth += childWidth + mItemBorder;//设置第一行高度 因为layout t = totalHeight - childHeight//要让totalHeight - childHeight不为负数 所以先设置一行高度if (i == 0) {totalHeight = childHeight + mItemBorder;}//2种情况所有item宽度大于viewgroup宽度,这时候需要换行;不大于viewgroup宽度,继续//所有的totalWidth=item宽度+之间的边距,加上左边第一个item与viewGroup边距mViewBorder,加上右边mItemBorder边距,大于viewGroup的宽,需要换行if (totalWidth + mItemBorder + mViewBorder > mWidthSize) {totalWidth = mItemBorder;//换行设置间距//高度 = 原高度 + item高度 + item间距totalHeight += childHeight + mItemBorder;//layout确定子view在view group中的位置child.layout(totalWidth + mViewBorder,totalHeight - childHeight,totalWidth + childWidth + mViewBorder,totalHeight);totalWidth += childWidth;} else {//横排:起始 间隔viewboder距离开始,到总的width(items+item间距)+view间距//竖排:起始 离顶部viewboder间距开始,到总的heightchild.layout(totalWidth - childWidth + mViewBorder,totalHeight - childHeight,totalWidth + mViewBorder,totalHeight);}}return totalHeight + mItemBorder;}
设置高度
//子view计算的宽,高度去决定测量的宽高值setMeasuredDimension(mWidthSize, heightMode == MeasureSpec.EXACTLY ? mHeightSize : totalHeight);
做完上面的步骤,view的测量,布局都已经完成,接下来只需要把item view放进来。
添加item到ViewGroup
public void setTags(List<String> tagDatas) {if (tagDatas == null) {return;}mTags = tagDatas;//先清空所有的viewremoveAllViews();String tag;for (int i = 0; i < mTags.size(); i++) {tag = tagDatas.get(i);//加载布局文件TextView tagView = (TextView) mInflater.inflate(R.layout.layout_item, null);tagView.setText(tag);tagView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize);tagView.setBackgroundResource(mTagBackground);tagView.setTextColor(mTextColor);LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);tagView.setLayoutParams(params);//添加viewaddView(tagView); }//刷新postInvalidate();}
多行显示tag就在这里写完了,运行,看下标签云的效果
写完多行模式,接下来写单行模式,我们会问,多行模式下不超过viewgroup宽度不就是单行吗,为什么还要定义单行模式?
这里的单行模式是指不管有多少item都值显示单行,超出的item不显示,用”…”代替。
单行模式与多行模式基本类似,主要区别在于获取totalHeight和addView部分。接下来对这两个部分进行实现。
单行模式实现
初始化单行模式view
private void initSingleLineView(int widthMeasureSpec, int heightMeasureSpec) {//判断是否是单行模式if (!mSingleLine) {return;}//是否显示向右箭头if (mShowArrow) {mArrowIv = new ImageView(getContext());mArrowIv.setImageResource(mArrowResId);mArrowIv.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));mArrowIv.setScaleType(ImageView.ScaleType.CENTER_INSIDE);measureChild(mArrowIv, widthMeasureSpec, heightMeasureSpec);mArrowIconWidth = mArrowIv.getMeasuredWidth();mImageHeight = mArrowIv.getMeasuredHeight();addView(mArrowIv);}//是否显示更多itemif (mShowMore) {mMoreTextTv = (TextView) mInflater.inflate(R.layout.layout_item, null);mMoreTextTv.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize);mMoreTextTv.setTextColor(mTextColor);mMoreTextTv.setBackgroundResource(mTagBackground);@SuppressLint("DrawAllocation")LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);mMoreTextTv.setLayoutParams(layoutParams);mMoreTextTv.setText(mMoreTextStr == null || mMoreTextStr.equals("") ? "..." : mMoreTextStr);measureChild(mMoreTextTv, widthMeasureSpec, heightMeasureSpec);mMoreTextWidth = mMoreTextTv.getMeasuredWidth();addView(mMoreTextTv);}}
计算单行模式高度及确定位置2
单行模式看起来代码比较多,其实并不复杂,只是比多行模式多了一个arrow icon和查看更多item
private int getSingleTotalHeight(int totalWidth, int totalHeight) {int childWidth;int childHeight = 0;totalWidth += mViewBorder;//设置左边距//上图红色边框的总宽度int textTotalWidth = getTextTotalWidth();//items 总宽度小于 viewgroup - 箭头图片width 不用显示更多itemif (textTotalWidth < mWidthSize - mArrowIconWidth) {mMoreTextStr = null;mMoreTextWidth = 0;}//计算item中宽度并确定布局for (int i = 0; i < getChildCount(); i++) {View child = getChildAt(i);childWidth = child.getMeasuredWidth();childHeight = child.getMeasuredHeight();if (i == 0) {totalWidth += childWidth;totalHeight = childHeight + mViewBorder;} else {totalWidth += childWidth + mItemBorder;}//左边距+items宽+item边距+更多item宽+arrow icon宽+右边距 < viewgroup宽if (mViewBorder + totalWidth + mItemBorder + mMoreTextWidth + mArrowIconWidth+ mViewBorder < mWidthSize) {//横排:起始 间隔mItemBorder距离开始,到总的width(items+item间距)+mItemBorder间距//竖排:起始 离顶部viewboder间距开始,到总的heightchild.layout(totalWidth - childWidth + mItemBorder,totalHeight - childHeight,totalWidth + mItemBorder,totalHeight);} else {//超出部分不显示 ,前面已经加上了,所以这里需要减去totalWidth -= childWidth + mViewBorder;break;}}//更多itemif (mMoreTextTv != null) {//起始位置 从最后一个item+item边距位置x开始 //结束位置 x+moretextWidthmMoreTextTv.layout(mViewBorder+ totalWidth + mItemBorder,totalHeight - childHeight,totalWidth + mViewBorder + mItemBorder + mMoreTextWidth,totalHeight);}totalHeight += mViewBorder;//箭头 与上面类似if (mArrowIv != null) {mArrowIv.layout(mWidthSize - mArrowIconWidth - mViewBorder,(totalHeight - mImageHeight) / 2,mWidthSize - mViewBorder,(totalHeight - mImageHeight) / 2 + mImageHeight);}return totalHeight;}
这样单行模式也就完成了,剩下的就是加入监听事件,优化等,功能扩展,这里不多介绍
完整demo下载地址:TagTextView
[github高级控件]带你走近-自定义标签云相关推荐
- 十四、windows窗体高级控件
1 PictureBox控件 PictureBox控件可以显示来自位图.图标或者原文件,以及来自增强的元文件.Jpeg或GIF文件的图形.如果控件不足以显示整幅图像,则捡钱图像以适应控件的大小 usi ...
- Android高级控件(二)——SurfaceView实现GIF动画架包,播放GIF动画,自己实现功能的初体现...
Android高级控件(二)--SurfaceView实现GIF动画架包,播放GIF动画,自己实现功能的初体现 写这个的原因呢,也是因为项目中用到了gif动画,虽然网上有很多的架包可以实现,不过我们还 ...
- Android高级控件----AdapterView与Adapter详解
在J2EE中提供过一种非常好的框架--MVC框架,实现原理:数据模型M(Model)存放数据,利用控制器C(Controller)将数据显示在视图V(View)上.在Android中有这样一种高级控件 ...
- 2022 最新 Android 基础教程,从开发入门到项目实战【b站动脑学院】学习笔记——第八章:高级控件
本章介绍了App开发常用的一些高级控件用法,主要包括:如何使用下拉框及其适配器.如何使用列表 类视图及其适配器.如何使用翻页类视图及其适配器.如何使用碎片及其适配器等.然后结合本章所学 的知识,演示了 ...
- 三、PyQt5高级控件的使用
(四)PyQt5高级控件的使用 PyQt5中包含了很多用于简化窗口设计的可视化控件,除了常用控件外,还有一些关于进度.展示数据等的高级控件. 本章重点讲解PyQt5程序开发中用到的一些高级控件,主要包 ...
- Android高级控件(一)——ListView绑定CheckBox实现全选,增加和删除等功能
Android高级控件(一)--ListView绑定CheckBox实现全选,增加和删除等功能 这个控件还是挺复杂的,也是项目中应该算是比较常用的了,所以写了一个小Demo来讲讲,主要是自定义adap ...
- Android高级控件(四)——VideoView 实现引导页播放视频欢迎效果,超级简单却十分的炫酷
Android高级控件(四)--VideoView 实现引导页播放视频欢迎效果,超级简单却十分的炫酷 是不是感觉QQ空间什么的每次新版本更新那炫炫的引导页就特别的激动,哈哈,其实他实现起来真的很简单很 ...
- Android 高级控件(七)——RecyclerView的方方面面
Android 高级控件(七)--RecyclerView的方方面面 RecyclerView出来很长时间了,相信大家都已经比较了解了,这里我把知识梳理一下,其实你把他看成一个升级版的ListView ...
- Android的高级控件(自动提示文本框与下拉列表)
一.高级控件与常用控件的区别:是否使用适配器 二.适配器 1.种类 ①.数组适配器 ArrayAdapter new ArrayAdapter<String>(this,R.l ...
- android_高级控件_1
有蛮久没更新了今天来记录一下android的高级控件 AutoCompleteTextView(自动补全): 开发过web项目的小伙伴们应该知道在web项目中实现自动补全是有多麻烦,在安卓开发中实现自 ...
最新文章
- 黑科技,教你用Python打电话,控制手机技术,快来学一下
- 基于Springboot实现宠物医院综合管理系统
- Qt动态多语言的实现(VS2012开发)
- ie9浏览器两个ajax请求同步不兼容_浏览器拦截问题
- Yinchuan-B The Great Wall
- 使用cpan安装perl模块
- linux shell oracle脚本_领导:如何用shell脚本统计Oracle数据库进程明细和存储过程信息...
- 工业以太网交换机在实际应用中的优势
- adam算法效果差原因_冷库制冷效果差原因
- 关系型数据库性能优化总结(转)
- Linux下清除DNS缓存
- iterator adapter inserter
- JSP教程第4讲笔记
- python 使用 config 文件
- b站投稿 您的稿件未能成功转码。原因:该视频时长不足1秒,请检查视频时长并尝试重新上传。解决办法
- excel随机数_excel怎样生成随机数
- PostgreSQL 数据库跨版本升级常用方案
- ESL-chapter8-EM算法介绍1-混合高斯的例子
- 如何在大学里成为IT技术大神?
- 【报告分享】2020年中国老年教育市场研究报告-IT桔子(附下载)