Android自定义ViewGroup实现堆叠头像的点赞Layout

所属分类: 软件编程 / Android 阅读数: 52
收藏 0 赞 0 分享

简介

这里写图片描述

这样的点赞列表怎么样?之前做社区的时候也有类似的点赞列表,但是没有这样重叠,一个小小的改变,个人感觉逼格提高不少。

这个很有规则,就是后一个头像会覆盖一部分到前一个头像上,头像多了就像一串糖葫芦了。

这个实现起来不难,自定义ViewGroup,关键重写onLayout方法。

关于自定义控件的基础知识可以看一看这个,整理的很详细: https://github.com/GcsSloop/AndroidNote

实现

自定义属性

属性名 说明 默认值
vertivalSpace 行距 4dp
pileWidth 重叠宽度 10dp

onMeasure方法,每行的宽度不再是child的宽度和了,而是要减掉重叠部分的宽度和

@Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
    int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
    int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
    int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);

    //AT_MOST
    int width = 0;
    int height = 0;
    int rawWidth = 0;//当前行总宽度
    int rawHeight = 0;// 当前行高

    int rowIndex = 0;//当前行位置
    int count = getChildCount();
    for (int i = 0; i < count; i++) {
      View child = getChildAt(i);
      if(child.getVisibility() == GONE){
        if(i == count - 1){
          //最后一个child
          height += rawHeight;
          width = Math.max(width, rawWidth);
        }
        continue;
      }

      //这里调用measureChildWithMargins 而不是measureChild
      measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
      MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

      int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
      int childHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
      if(rawWidth + childWidth - (rowIndex > 0 ? pileWidth : 0)> widthSpecSize - getPaddingLeft() - getPaddingRight()){
        //换行
        width = Math.max(width, rawWidth);
        rawWidth = childWidth;
        height += rawHeight + vertivalSpace;
        rawHeight = childHeight;
        rowIndex = 0;
      } else {
        rawWidth += childWidth;
        if(rowIndex > 0){
          rawWidth -= pileWidth;
        }
        rawHeight = Math.max(rawHeight, childHeight);
      }

      if(i == count - 1){
        width = Math.max(rawWidth, width);
        height += rawHeight;
      }

      rowIndex++;
    }

    setMeasuredDimension(
        widthSpecMode == MeasureSpec.EXACTLY ? widthSpecSize : width + getPaddingLeft() + getPaddingRight(),
        heightSpecMode == MeasureSpec.EXACTLY ? heightSpecSize : height + getPaddingTop() + getPaddingBottom()
    );
  }

onLayout 每一行,第一个正常放,之后的重叠放

@Override
  protected void onLayout(boolean changed, int l, int t, int r, int b) {
    int viewWidth = r - l;
    int leftOffset = getPaddingLeft();
    int topOffset = getPaddingTop();
    int rowMaxHeight = 0;
    int rowIndex = 0;//当前行位置
    View childView;
    for( int w = 0, count = getChildCount(); w < count; w++ ){
      childView = getChildAt(w);
      if(childView.getVisibility() == GONE) continue;

      MarginLayoutParams lp = (MarginLayoutParams) childView.getLayoutParams();
      // 如果加上当前子View的宽度后超过了ViewGroup的宽度,就换行
      int occupyWidth = lp.leftMargin + childView.getMeasuredWidth() + lp.rightMargin;
      if(leftOffset + occupyWidth + getPaddingRight() > viewWidth){
        leftOffset = getPaddingLeft(); // 回到最左边
        topOffset += rowMaxHeight + vertivalSpace; // 换行
        rowMaxHeight = 0;

        rowIndex = 0;
      }

      int left = leftOffset + lp.leftMargin;
      int top = topOffset + lp.topMargin;
      int right = leftOffset+ lp.leftMargin + childView.getMeasuredWidth();
      int bottom = topOffset + lp.topMargin + childView.getMeasuredHeight();
      childView.layout(left, top, right, bottom);

      // 横向偏移
      leftOffset += occupyWidth;
      // 试图更新本行最高View的高度
      int occupyHeight = lp.topMargin + childView.getMeasuredHeight() + lp.bottomMargin;
      if(rowIndex != count - 1){
        leftOffset -= pileWidth;//这里控制重叠位置
      }
      rowMaxHeight = Math.max(rowMaxHeight, occupyHeight);
      rowIndex++;
    }
  }

效果图

这里写图片描述

因为这个一般只会显示一行,所以暂时没有通过setAdapter方式去设置数据源。

下载

https://github.com/LineChen/PileLayout

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

更多精彩内容其他人还在看

Android网络编程之获取网络上的Json数据实例

这篇文章主要介绍了Android网络编程之获取网络上的Json数据实例,本文用完整的代码实例讲解了在Android中读取网络中Json数据的方法,需要的朋友可以参考下
收藏 0 赞 0 分享

Android中的windowSoftInputMode属性详解

这篇文章主要介绍了Android中的windowSoftInputMode属性详解,本文对windowSoftInputMode的9个属性做了详细总结,需要的朋友可以参考下
收藏 0 赞 0 分享

Android网络编程之UDP通信模型实例

这篇文章主要介绍了Android网络编程之UDP通信模型实例,本文给出了服务端代码和客户端代码,需要的朋友可以参考下
收藏 0 赞 0 分享

Android中使用ListView实现漂亮的表格效果

这篇文章主要介绍了Android中使用ListView实现漂亮的表格效果,本文用详细的代码实例创建了一个股票行情表格,需要的朋友可以参考下
收藏 0 赞 0 分享

Android中刷新界面的二种方法

这篇文章主要介绍了Android中刷新界面的二种方法,本文使用Handler、postInvalidate两种方法实现界面刷新,需要的朋友可以参考下
收藏 0 赞 0 分享

Android SDK三种更新失败及其解决方法

这篇文章主要介绍了Android SDK三种更新失败及其解决方法,需要的朋友可以参考下
收藏 0 赞 0 分享

Android学习笔记——Menu介绍(一)

Android3.0(API level 11)开始,Android设备不再需要专门的菜单键。随着这种变化,Android app应该取消对传统6项菜单的依赖。取而代之的是提供anction bar来提供基本的用户功能
收藏 0 赞 0 分享

Android学习笔记——Menu介绍(二)

这次将继续上一篇文章没有讲完的Menu的学习,上下文菜单(Context menu)和弹出菜单(Popup menu)
收藏 0 赞 0 分享

Android学习笔记——Menu介绍(三)

今天继续昨天没有讲完的Menu的学习,主要是Popup Menu的学习,需要的朋友可以参考下
收藏 0 赞 0 分享

Android显示网络图片实例

这篇文章主要介绍了Android显示网络图片的方法,以实例形式展示了Android程序显示网络图片的方法,非常具有实用价值,需要的朋友可以参考下
收藏 0 赞 0 分享
查看更多