Android自定义ViewGroup实现标签浮动效果
作者:lijiao 发布时间:2021-12-28 20:20:29
标签:Android,ViewGroup,标签
前面在学习鸿洋大神的一些自定义的View文章,看到了自定义ViewGroup实现浮动标签,初步看了下他的思路以及结合自己的思路完成了自己的浮动标签的自定义ViewGroup。目前实现的可以动态添加标签、可点击。效果图如下:
1、思路
首先在onMeasure方法中测量ViewGroup的宽和高,重点是处理当我们自定义的ViewGroup设置为wrap_content的情况下,如何去测量其大小的问题。当我们自定义的ViewGroup设置为wrap_content时,我们需要让子View先去测量自己,当子View测量完后,再通过子View的getMeasuredWidth和getMeasureHeight方法获得其子View的宽和高。每次在测量一个子View之前,都需要判断如果加入该子View,当前行是否能够容纳下该子View,如果不能,则需要新开一行,并记录下当前行的最大高度。
在onLayout方法中,核心人物是给每个子View摆放位置,也就是为该ViewGroup中每个子View找到盒子模型上面的两个点也就是左上角和右下角,即点(l,t)和点(r,b),确定了两个点,子View的位置也就确定了。
2、实现
基本思路有了就可以尝试实现了,代码如下:
自定义的ViewGroup:
/**
* 流式标签(动态的,根据传入的数据动态添加标签)
*/
public class DynamicTagFlowLayout extends ViewGroup {
private List<String> mTags = new ArrayList<String>();
public DynamicTagFlowLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public DynamicTagFlowLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public DynamicTagFlowLayout(Context context) {
super(context);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
//当前ViewGroup的总高度
int totalHeight= 0;
//所有行中的最大宽度
int maxLineWidth = 0;
//当前行的最大高度
int lineMaxHeight = 0;
//当前行的总宽度
int currentLineWidth = 0;
//每个childView所占用的宽度
int childViewWidthSpace = 0;
//每个childView所占用的高度
int childViewHeightSpace = 0;
int count = getChildCount();
MarginLayoutParams layoutParams;
for(int i = 0; i < count; i++){
View child = getChildAt(i);
if(child.getVisibility() != View.GONE){//只有当这个View能够显示的时候才去测量
//测量每个子View,以获取子View的宽和高
measureChild(child, widthMeasureSpec, heightMeasureSpec);
layoutParams = (MarginLayoutParams) child.getLayoutParams();
childViewWidthSpace = child.getMeasuredWidth() + layoutParams.leftMargin + layoutParams.rightMargin;
childViewHeightSpace = child.getMeasuredHeight() + layoutParams.topMargin + layoutParams.bottomMargin;
if(currentLineWidth + childViewWidthSpace > widthSize){//表示如果当前行再加上现在这个子View,就会超出总的规定宽度,需要另起一行
totalHeight += lineMaxHeight;
if(maxLineWidth < currentLineWidth){//如果行的最长宽度发生了变化,更新保存的最长宽度
maxLineWidth = currentLineWidth;
}
currentLineWidth = childViewWidthSpace;//另起一行后,需要重置当前行宽
lineMaxHeight = childViewHeightSpace;
}else{//表示当前行可以继续添加子元素
currentLineWidth += childViewWidthSpace;
if(lineMaxHeight < childViewHeightSpace){
lineMaxHeight = childViewHeightSpace;
}
}
}
}
setMeasuredDimension(widthMode == MeasureSpec.EXACTLY ? widthSize : maxLineWidth, heightMode == MeasureSpec.EXACTLY ? heightSize : totalHeight);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
//当前是第几行
int currentLine = 1;
//存放每一行的最大高度
List<Integer> lineMaxHeightList = new ArrayList<Integer>();
//每个childView所占用的宽度
int childViewWidthSpace = 0;
//每个childView所占用的高度
int childViewHeightSpace = 0;
//当前行的最大高度
int lineMaxHeight = 0;
//当前行的总宽度
int currentLineWidth = 0;
int count = getChildCount();
MarginLayoutParams layoutParams;
for(int i = 0; i < count; i++){
int cl= 0, ct = 0, cr = 0, cb = 0;
View child = getChildAt(i);
if(child.getVisibility() != View.GONE){//只有当这个View能够显示的时候才去测量
layoutParams = (MarginLayoutParams) child.getLayoutParams();
childViewWidthSpace = child.getMeasuredWidth() + layoutParams.leftMargin + layoutParams.rightMargin;
childViewHeightSpace = child.getMeasuredHeight() + layoutParams.topMargin + layoutParams.bottomMargin;
System.out.println("getWidth()---->"+getWidth());
if(currentLineWidth + childViewWidthSpace > getWidth()){//表示如果当前行再加上现在这个子View,就会超出总的规定宽度,需要另起一行
lineMaxHeightList.add(lineMaxHeight);//此时先将这一行的最大高度加入到集合中
//新的一行,重置一些参数
currentLine++;
currentLineWidth = childViewWidthSpace;
lineMaxHeight = childViewHeightSpace;
cl = layoutParams.leftMargin;
if(currentLine > 1){
for(int j = 0; j < currentLine - 1; j++){
ct += lineMaxHeightList.get(j);
}
ct += layoutParams.topMargin ;
}else{
ct = layoutParams.topMargin;
}
}else{//表示当前行可以继续添加子元素
cl = currentLineWidth + layoutParams.leftMargin;
if(currentLine > 1){
for(int j = 0; j < currentLine - 1; j++){
ct += lineMaxHeightList.get(j);
}
ct += layoutParams.topMargin;
}else{
ct = layoutParams.topMargin;
}
currentLineWidth += childViewWidthSpace;
if(lineMaxHeight < childViewHeightSpace){
lineMaxHeight = childViewHeightSpace;
}
}
cr = cl + child.getMeasuredWidth();
cb = ct + child.getMeasuredHeight();
child.layout(cl, ct, cr, cb);
}
}
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(), attrs);
}
public void setTags(List<String> tags){
if(tags!= null){
mTags.clear();
mTags.addAll(tags);
for(int i = 0; i < mTags.size(); i++){
TextView tv = new TextView(getContext());
MarginLayoutParams lp = new MarginLayoutParams(MarginLayoutParams.WRAP_CONTENT, MarginLayoutParams.WRAP_CONTENT);
lp.setMargins(15, 15, 15, 15);
// lp.width = MarginLayoutParams.WRAP_CONTENT;
// lp.height = MarginLayoutParams.WRAP_CONTENT;
tv.setLayoutParams(lp);
tv.setBackgroundResource(R.drawable.tv_bg);
/*
* setPadding一定要在setBackgroundResource后面使用才有效!!!
* http://stackoverflow.com/questions/18327498/setting-padding-for-textview-not-working
*/
tv.setPadding(15, 15, 15, 15);
tv.setTextColor(Color.WHITE);
tv.setText(mTags.get(i));
tv.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if(listener != null){
listener.onClick(v);
}
}
});
addView(tv);
}
requestLayout();
}
}
private OnTagItemClickListener listener;
public interface OnTagItemClickListener{
public void onClick(View v);
}
public void setOnTagItemClickListener(OnTagItemClickListener l){
listener = l;
}
}
MainActivity:
public class MainActivity extends Activity {
private DynamicTagFlowLayout dynamicTagFlowLayout;
List<String> tags = new ArrayList<String>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dynamic_tagflowlayout);
dynamicTagFlowLayout = (DynamicTagFlowLayout) findViewById(R.id.dynamic_tag);
dynamicTagFlowLayout.setOnTagItemClickListener(new OnTagItemClickListener() {
@Override
public void onClick(View v) {
TextView tv = (TextView) v;
Toast.makeText(MainActivity.this, tv.getText().toString(), Toast.LENGTH_SHORT).show();
}
});
initData();
dynamicTagFlowLayout.setTags(tags);
}
private void initData() {
tags.add("阳哥你好!");
tags.add("Android开发");
tags.add("新闻热点");
tags.add("热水进宿舍啦!");
tags.add("I love you");
tags.add("成都妹子");
tags.add("新余妹子");
tags.add("仙女湖");
tags.add("创新工厂");
tags.add("孵化园");
tags.add("神州100发射");
tags.add("有毒疫苗");
tags.add("顶你阳哥阳哥");
tags.add("Hello World");
tags.add("闲逛的蚂蚁");
tags.add("闲逛的蚂蚁");
tags.add("闲逛的蚂蚁");
tags.add("闲逛的蚂蚁");
tags.add("闲逛的蚂蚁");
tags.add("闲逛的蚂蚁");
}
}
源码下载:Android流式标签可动态添加FlowLayout


猜你喜欢
- 首先,要添加图片列,绑定数据的时候会触发CellFormatting事件,在事件中取出图片路径,读取图片赋值给当前单元格。private v
- 在【解决方案资源管理器】中找到Form1.cs,单击,快捷键F2重命名为“Login.cs”(命名很
- 通过路径从磁盘直接读取图片这段时间在做Springboot和Vue的例子,读取图片给出路径直接可以读,太方便了,一直么有搞懂为什么。后面看到
- 本文实例为大家分享了MVPXlistView上拉下拉展示的具体代码,供大家参考,具体内容如下抽基类package com.gs.gg.day
- 带搜索的ComboBox就是给ComboBox一个依赖属性的ItemSource,然后通过数据源中是否包含要查询的值,重新给ComboBox
- 本文实例讲述了c#与js随机数生成方法。分享给大家供大家参考。具体如下:1. C#产生随机数方法:Random rd = new Rando
- 有时候我们做Android开发,需要弹一个用户提示,但是有时候设计的提示弹窗是带有图片的,我们每次写一个特别麻烦。所以我特地封装了一个工具类
- 重载,继承,重写和多态的区别:1)继承是子类获得父类的成员。2)重写是继承后重新实现父类的方法。 3)重载是在一个类里一系列参数不同名字相同
- 中国科学院开源协会镜像站地址:IPV4/IPV6: http://mirrors.opencas.cn 端口:80IPV4/IPV6: ht
- 一、项目运行环境配置:Jdk1.8 + Tomcat8.5 + mysql + Eclispe(IntelliJ IDEA,Eclispe,
- 默认路径在Spring Boot 2.7.2版本中,查看默认静态资源路径,在WebProperties.class中如下private st
- 读取自定义properties注入到bean在使用springboot项目时,可使用@value的方式直接读取application.pro
- 如何使用struts2 * ,或者自定义 * 。特别注意,在使用 * 的时候,在Action里面必须最后一定要引用struts2自带的 *
- 概述对List进行分组是日常开发中,经常遇到的,在JDK 8中对List按照某个属性分组的代码,超级简单。package test;impo
- 概述什么是动态编程?动态编程解决什么问题?Java中如何使用?什么原理?如何改进?(需要我们一起探索,由于自己也是比较菜,一般深入不到这个程
- /** * Gets the number of cores available in this device, across all pr
- 在线扫描相机的调试过程中,需要开辟调试界面来进行位置的配置。调试结束后,一种常用的方式是将调试参数保存并在下次启动时加载。另一种简单方式是直
- 最近做的一个项目涉及到文件上传与下载。前端上传采用百度webUploader插件。有关该插件的使用方法还在研究中,日后整理再记录。本文主要介
- 本文较为详细的讲述了Android下Activity全屏显示实现方法。分享给大家供大家参考。具体方法如下:方法一:使用xml的方法,在该项目
- 最近在做学校的课程设计,java编程需要用到对话框弹出,第一反应是js中的alert和confirm,java的话瞬间懵,查阅学习总结如下,