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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 详解Android应用开发中Scroller类的屏幕滑动功能运用

    今天给大家介绍下Android中滑屏功能的一个基本实现过程以及原理初探,最后给大家重点讲解View视图中scrollTo 与scrollBy这两个函数的区别 .   首先 ,我们必须明白在Android View视图是没有边界的,Canvas是没有边界的,只不过我们通过绘制特定的View时对Canvas对象进行了一定的操作,例如 : translate(平移).clipRect(剪切)等,以便达到我们的对该Canvas对象绘制的要求 ,我们可以将这种无边界的视图称为"视图坐标"----

  • Android 触摸事件监听(Activity层,ViewGroup层,View层)详细介绍

    Android不同层次的触摸事件监听 APP开发中,经常会遇到有关手势处理的操作,比如向右滑动返回上一个页面.关于触摸事件的处理,我们可以大概处理在不同的层次上. Activity层:可以看做触摸事件获取的最顶层 ViewGroup层:ViewGroup层可以自主控制是否让子View获取触摸事件 View层:可以决定自己是否真正的消费触摸事件,如果不消费抛给上层ViewGroup Activity级别的手势监听:(右滑动返回上层界面) Activity层手势监听的使用场景:一般用于当前页面中没有

  • Android HorizontalScrollView左右滑动效果

    本文实例为大家分享了Android HorizontalScrollView左右滑动的具体代码,供大家参考,具体内容如下 效果图 一.什么是HorizontalScrollView HorizontalScrollView实际上是一个FrameLayout ,这意味着你只能在它下面放置一个子控件 ,这个子控件可以包含很多数据内容.有可能这个子控件本身就是一个布局控件,可以包含非常多的其他用来展示数据的控件.这个布局控件一般使用的是一个水平布局的LinearLayout.TextView也是一个可

  • Android中实现监听ScrollView滑动事件

    时候我们需要监听ScroView的滑动情况,比如滑动了多少距离,是否滑到布局的顶部或者底部.可惜的是SDK并没有相应的方法,不过倒是提供了一个 复制代码 代码如下: protected void onScrollChanged(int x, int y, int oldx, int oldy) 方法,显然这个方法是不能被外界调用的,因此就需要把它暴露出去,方便使用.解决方式就是写一个接口, 复制代码 代码如下: package com.example.demo1;    public inter

  • Android自定义ViewGroup实现带箭头的圆角矩形菜单

    本文和大家一起做一个带箭头的圆角矩形菜单,大概长下面这个样子: 要求顶上的箭头要对准菜单锚点,菜单项按压反色,菜单背景色和按压色可配置. 最简单的做法就是让UX给个三角形的图片往上一贴,但是转念一想这样是不是太low了点,而且不同分辨率也不太好适配,干脆自定义一个ViewGroup吧! 自定义ViewGroup其实很简单,基本都是按一定的套路来的. 一.定义一个attrs.xml 就是声明一下你的这个自定义View有哪些可配置的属性,将来使用的时候可以自由配置.这里声明了7个属性,分别是:箭头宽

  • Android自定义ViewGroup之实现FlowLayout流式布局

    整理总结自鸿洋的博客:http://blog.csdn.net/lmj623565791/article/details/38352503/  一.FlowLayout介绍  所谓FlowLayout,就是控件根据ViewGroup的宽,自动的往右添加,如果当前行剩余空间不足,则自动添加到下一行.有点像所有的控件都往左飘的感觉,第一行满了,往第二行飘~所以也叫流式布局.Android并没有提供流式布局,但是某些场合中,流式布局还是非常适合使用的,比如关键字标签,搜索热词列表等,比如下图: git

  • Android ScrollView滑动实现仿QQ空间标题栏渐变

    今天来研究的是ScrollView-滚动视图,滚动视图又分横向滚动视图(HorizontalScrollView)和纵向滚动视图(ScrollView),今天主要研究纵向的.相信大家在开发中经常用到,ScrollView的功能已经很强大了,但是仍然满足不了我们脑洞大开的UI设计师们,所以我们要自定义-本篇文章主要讲监听ScrollView的滑动实现仿QQ空间标题栏渐变,先看一下效果图: 好了我们切入主题. 有可能你不知道的那些ScrollView属性  •android:scrollbars 设

  • Android编程重写ViewGroup实现卡片布局的方法

    本文实例讲述了Android编程重写ViewGroup实现卡片布局的方法.分享给大家供大家参考,具体如下: 实现效果如图: 实现思路 1. 重写onMeasure(int widthMeasureSpec, int heightMeasureSpec)设置每个子View的大小 2. 重写onLayout(boolean changed, int l, int t, int r, int b) 设置每个子View的位置 第一步:新建FlowLayout继承ViewGroup package com

  • Android App开发中自定义View和ViewGroup的实例教程

    View Android所有的控件都是View或者View的子类,它其实表示的就是屏幕上的一块矩形区域,用一个Rect来表示,left,top表示View相对于它的parent View的起点,width,height表示View自己的宽高,通过这4个字段就能确定View在屏幕上的位置,确定位置后就可以开始绘制View的内容了. View绘制过程 View的绘制可以分为下面三个过程: Measure View会先做一次测量,算出自己需要占用多大的面积.View的Measure过程给我们暴露了一个

  • Android继承ViewGroup实现Scroll滑动效果的方法示例

    本文实例讲述了Android继承ViewGroup实现Scroll滑动效果的方法.分享给大家供大家参考,具体如下: extends ViewGroup需要重写onMeasure和onLayout方法 onMeasure方法是去测量ViewGroup需要的大小以及包含的子View需要的大小. 执行完上面的方法后,再执行onLayout方法去设置子View的摆放位置. 实现Scroll滑动效果需要去检测滑动速率,即要知道每个单位时间滑动了多少像素值,根据这个像素值去判断Scroll滑动到下一页还是上

  • Android中ScrollView实现滑动距离监听器的方法

    前言 众所周知ScrollView是我们经常使用的一个UI控件,也许你在使用ScrollView的过程中会发现,当你想监听ScrollView滑动的距离时却没有合适的监听器!当然在API 23中有setOnScrollChangeListener(View.OnScrollChangeListener l)可以使用,但是并不兼容低版本的API.那怎么办呢?只好重写ScrollView来实现对滑动距离的监听了. 话不多说,直接上代码: public class MyScrollView exten

  • Android中Toolbar随着ScrollView滑动透明度渐变效果实现

    Android中Toolbar随着ScrollView滑动透明度渐变效果实现 一.思路:监听ScrollView的滑动事件 不断的修改Toolbar的透明度 二.注意 1.ScrollView 6.0以前没有scrollView.setOnScrollChangeListener(l)方法  所以要自定义ScrollView 在onScrollChanged()中监听 2.ScrollView 6.0(23)以前没有scrollView.setOnScrollChangeListener()方法

随机推荐