Android -- 自定义ViewGroup实现FlowLayout效果
1,在开发的时候,常在我们的需求中会有这种效果,添加一个商品的一些热门标签,效果图如下:
2,从上面效果可以看得出来,这是一个自定义的ViewGroup,然后实现换行效果,让我们一起来实现一下
- 自定义属性
从上面的效果来看,我们需要动态的设置每个lable的宽度和高度,所以我们编写如下的自定义属性
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="FlowLayout">
<!-- 标签之间的间距-->
<attr name="lineSpace" format="dimension"/>
<!-- 每一行之间的间距-->
<attr name="rowSpace" format="dimension"/>
</declare-styleable>
</resources>
在布局文件中使用
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:flowlayout="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.qianmo.flowlayout.FlowLayout
android:id="@+id/flowLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="20dip"
flowlayout:lineSpace="20dip"
flowlayout:rowSpace="10dip"/>
</LinearLayout>
在类中获取自定义属性
public class FlowLayout extends ViewGroup {
private static String TAG = "FlowLayout"; //自定义属性
private int LINE_SPACE;
private int ROW_SPACE; //放置标签的集合
private List<String> lables;
private List<String> lableSelects; public FlowLayout(Context context) {
this(context, null);
} public FlowLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
} public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr); //获取自定义属性
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.FlowLayout);
LINE_SPACE = a.getDimensionPixelSize(R.styleable.FlowLayout_lineSpace, 10);
ROW_SPACE = a.getDimensionPixelSize(R.styleable.FlowLayout_rowSpace, 10);
a.recycle(); }
}
- 初始化数据数据源
向FlowLayout类中添加数据
/**
* 添加标签
*
* @param lables 标签集合
* @param isAdd 是否添加
*/
public void setLables(List<String> lables, boolean isAdd) {
if (this.lables == null) {
this.lables = new ArrayList<>();
}
if (this.lableSelects == null) {
this.lableSelects = new ArrayList<>();
}
if (isAdd) {
this.lables.addAll(lables);
} else {
this.lables.clear();
this.lables = lables;
}
if (lables != null && lables.size() > 0) {
for (final String lable : lables) {
final TextView tv = new TextView(getContext());
tv.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT));
tv.setText(lable);
tv.setTextSize(20);
tv.setBackgroundResource(R.drawable.shape_item_lable_bg);
tv.setTextColor(Color.BLACK);
tv.setGravity(Gravity.CENTER);
tv.setPadding(12, 5, 12, 5); //判断是否选中
if (lableSelects.contains(lable)) {
tv.setSelected(true);
tv.setTextColor(getResources().getColor(R.color.tv_blue));
} else {
tv.setSelected(false);
tv.setTextColor(getResources().getColor(R.color.tv_gray));
} //点击之后选中标签
tv.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
tv.setSelected(tv.isSelected() ? false : true);
if (tv.isSelected()) {
tv.setTextColor(getResources().getColor(R.color.tv_blue));
lableSelects.add(lable);
} else {
tv.setTextColor(getResources().getColor(R.color.tv_gray));
lableSelects.remove(lable);
}
}
}); //添加到容器中
addView(tv);
}
}
}
下面的代码是textview的背景选择器
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<!--选中效果-->
<item android:state_selected="true">
<shape >
<solid android:color="#ffffff" />
<stroke android:color="@color/tv_blue"
android:width="2px"/>
<corners android:radius="10000dip"/>
</shape>
</item>
<!--默认效果-->
<item>
<shape >
<solid android:color="#ffffff" />
<stroke android:color="@color/divider_gray"
android:width="2px"/>
<corners android:radius="10000dip"/>
</shape>
</item>
</selector>
- 重写onMeasure方法
本布局在宽度上是使用的建议的宽度(填充父窗体或者具体的size),如果需要wrap_content的效果,还需要重新计算,当然这种需求是非常少见的,所以直接用建议宽度即可;布局的高度就得看其中的标签需要占据多少行(row ),那么高度就为row * 单个标签的高度+(row -1) * 行距,代码如下:
/**
* 通过测量子控件高度,来设置自身控件的高度
* 主要是计算
*
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//测量所有子view的宽高
measureChildren(widthMeasureSpec, heightMeasureSpec); //获取view的宽高测量模式
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec); //这里的宽度建议使用match_parent或者具体值,当然当使用wrap_content的时候没有重写的话也是match_parent所以这里的宽度就直接使用测量的宽度
int width = widthSize; int height;
//判断宽度
if (heightMode == MeasureSpec.EXACTLY) {
height = heightSize;
} else {
int row = 1;
int widthSpace = width; //宽度剩余空间
for (int i = 0; i < getChildCount(); i++) {
View view = getChildAt(i);
//获取标签宽度
int childW = view.getMeasuredWidth();
//判断剩余宽度是否大于此标签宽度
if (widthSpace >= childW) {
widthSpace -= childW;
} else {
row++;
widthSpace = width - childW;
}
//减去两边间距
widthSpace -= LINE_SPACE;
}
//获取子控件的高度
int childH = getChildAt(0).getMeasuredHeight();
//测算最终所需要的高度
height = (childH * row) + (row - 1) * ROW_SPACE;
} //保存测量高度
setMeasuredDimension(width, height);
}
- 重写OnLayout方法
onLayout(boolean changed, int l, int t, int r, int b)方法是一个抽象方法,自定义ViewGroup时必须实现它,用于给布局中的子控件分配位置,其中的参数l,t,r,b分别代表本ViewGroup的可用空间(除去margin和padding后的剩余空间)的左、上、右、下的坐标(相对于自身),相当于一个约束,如果子控件摆放的位置超过这个范围,超出的部分将不可见。
/**
* 摆放子view
*
* @param changed
* @param l
* @param t
* @param r
* @param b
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int row = 0;
int right = 0;
int bottom = 0;
for (int i = 0; i < getChildCount(); i++) {
View chileView = getChildAt(i);
int childW = chileView.getMeasuredWidth();
int childH = chileView.getMeasuredHeight();
right += childW;
bottom = (childH + ROW_SPACE) * row + childH;
if (right > (r - LINE_SPACE)) {
row++;
right = childW;
bottom = (childH + ROW_SPACE) * row + childH;
}
chileView.layout(right - childW, bottom - childH, right, bottom);
right += LINE_SPACE;
}
}
看一下实现的效果图
ok,这样我们就全部实现了,需要源码的同学可以在这里去下载
Android -- 自定义ViewGroup实现FlowLayout效果的更多相关文章
- android自定义viewgroup之我也玩瀑布流
先看效果图吧, 继上一篇<android自定义viewgroup实现等分格子布局>中实现的布局效果,这里稍微有些区别,每个格子的高度不规则,就是传说的瀑布流布局,一般实现这种效果,要么用第 ...
- Android 自定义ViewGroup 实战篇 -> 实现FlowLayout
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38352503 ,本文出自[张鸿洋的博客] 1.概述 上一篇已经基本给大家介绍了如 ...
- android自定义viewgroup实现等分格子布局
先上效果图: 实现这样的效果: 一般的思路就是,直接写布局文件,用LinearLayout 嵌套多层子LinearLayout,然后根据权重layout_weight可以达到上面的效果 还有就是利用g ...
- android 自定义ViewGroup和对view进行切图动画实现滑动菜单SlidingMenu
示意图就不展示了,和上一节的一样,滑动菜单SlidingMenu效果如何大家都比较熟悉,在这里我简单说明一下用自定义ViewGroup来实现. 实现方法:我们自定义一个ViewGroup实现左右滑动, ...
- android 自定义ViewGroup和对view进行切图动画实现滑动菜单SlidingMenu[转]
http://blog.csdn.net/jj120522/article/details/8095852 示意图就不展示了,和上一节的一样,滑动菜单SlidingMenu效果如何大家都比较熟悉,在这 ...
- android 自定义view之侧滑效果
效果图: 看网上的都是两个view拼接,默认右侧的不显示,水平移动的时候把右侧的view显示出来.但是看最新版QQ上的效果不是这样的,但给人的感觉却很好,所以献丑来一发比较高仿的. 知识点: 1.Vi ...
- Android自定义ViewGroup(四、打造自己的布局容器)
转载请标明出处: http://blog.csdn.net/xmxkf/article/details/51500304 本文出自:[openXu的博客] 目录: 简单实现水平排列效果 自定义Layo ...
- android自定义viewgroup初步之一----抽屉菜单
转载请注明出处 http://blog.csdn.net/wingichoy/article/details/47832151 几天前在慕课网上看到鸿洋老师的 自定义卫星菜单,感觉很有意思,于是看完视 ...
- Android 自定义ViewGroup手把手教你实现ArcMenu
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/37567907 逛eoe发现这样的UI效果,感觉很不错,后来知道github上有这 ...
随机推荐
- C#配置.INI文件
百度搜了一些资料,好多没给示例,只给了代码.让人看了直接懵逼,后来找了个靠谱的:http://www.jb51.net/article/118591.htm
- 通用网关接口 ruby perl web页面 文本处理 脚本语言
小结: 1.只要可以对标准输入输出进行操作,那么无论任何语言都可以编写CGI程序. <代码的未来> 在Ruby诞生的1993年,互联网还没有现在这样普及,因此Ruby也不是一开始就面向We ...
- 没有文件扩展js的脚本引擎
没有文件扩展js的脚本引擎 没有文件扩展js的脚本引擎怎么解决_百度经验 https://jingyan.baidu.com/article/ff42efa93a7ad9c19e2202f0.html
- 使用Dom4j的xPath解析xml文件------xpath语法
官方语法地址:http//www.w3school.com.cn/xpath/index.asp xpath使用路径表达式来选取xml文档中的节点或节点集.节点是通过沿着路径(path)或者步(ste ...
- 转:Redis 3.2.1集群搭建
Redis 3.2.1集群搭建 一.概述 Redis3.0版本之后支持Cluster. 1.1.redis cluster的现状 目前redis支持的cluster特性: 1):节点自动发现 2) ...
- MyBati__mapper 中取值(#{} 或${}) 以及 parameterType为(基本类型 或复杂类型)
参考资料: MyBatis学习笔记(三)——parameterType为基本类型时的使用方法 MyBatis的传入参数parameterType类型 1. MyBatis的传入参数parameterT ...
- IQ调制原理
现代通信中,IQ调制基本上属于是标准配置,因为利用IQ调制可以做出所有的调制方式. 但是IQ调制到底是怎么工作的,为什么需要星座映射,成型滤波又是用来干嘛的.这个呢,讲通信原理的时候倒是都会泛泛的提到 ...
- 怎样打开U盘最安全
为了避免电脑使用U盘时,通过双击,或者右击盘符时,导致把病毒感染至整个电脑,因此使用下面的方法,可使U盘病毒不被激活传播. 在取消了U盘自动运行的情况下(在组策略中一定要关闭自动运行功能,否则只要一插 ...
- jvm - 类的初始化过程
我们知道,我们写的java代码称为源码,想要能够被jvm执行首先需要编译成.class文件,那么编译完到使用又都经理的哪些阶段呢?主要分为以下三个阶段: 加载:查找并加载类的二进制数据(.class文 ...
- jsp fmt标签格式化double数字
<fmt:formatNumber value="${zjdl.ygdl }" pattern="0.00" />