Android 仿钉钉圈子多图展示效果自定义View

目录

1.需求分析

2.功能实现

2.1 确定ImageView宽高

2.2 确定ImageView摆放位置

2.3 设置间距及圆角

2.4 完整源码

3.功能说明

4.注意事项

5.最后


1.需求分析

首先来看钉钉--发现--圈子,实际多图展示效果

                                                          钉钉--发现--圈子 多图展示效果

 

  • 单图时限定最大高度及最大宽度,按比例缩放宽高,四周有圆角,ImageView的ScaleType为默认值

  • 2-9张图片时,为大小图混合排列,父view四周有圆角,ImageView的ScaleType为centerCrop

 

2.功能实现

实现效果

仿钉钉圈子多图展示效果

 

实现思路

自定义 NineGridView 继承于 CardView ,添加10个ImageView(其中一个为单图专用ImageView)到该View中,接下来

1)确定ImageView宽高

     重写onMeasure方法:获取当前View可用宽高,根据图片数量设置子View大小,根据图片数量及子View大小确定当前View高度,通过setMeasuredDimension存储测量的宽度和测量的高度

2)确定ImageView摆放位置

     重写onLayout方法:根据图片数量设置子view摆放位置

3)设置间距及圆角

 

2.1 确定ImageView宽高

  设当前View可展示宽高: width , height
            图片间间隔: space
 
  由钉钉圈子实际展示效果可知,当图片数量大于1时,小图宽高均为1:1,且小图宽高等于九张图时的宽高
  可得小图边长为
  baseLength = ( width - 2*space ) / 3
 
  下面根据图片数量确定ImageView宽高:
 
  1张图片 (限定最大高度及最大宽度,宽高按比例缩放)
   imageViewWidth = wrap_content
   imageViewHeight = wrap_content
   imageViewMaxWidth = width
   imageViewMaxHeight = 设定值/默认值
 
  2张图片 (横向排列,对分父view宽度)
   imageViewWidth = ( width - space ) / 2
   imageViewHeight = imageViewWidth
 
  3张图片 (大图在左,两张小图在右竖向排列)
   两张小图宽高
   imageViewWidth = baseLength
   imageViewHeight = baseLength
   大图宽高
   maxImageViewWidth = width - space - baseLength
   maxImageViewHeight = 2*baseLength + space
 
  4张图片 (大图在上,三张小图在下横向排列)
   大图宽高
   maxImageViewWidth = width
   maxImageViewHeight = width - space - baseLength
   三张小图宽高
   imageViewWidth = baseLength
   imageViewHeight = baseLength
 
  5张图片 (两张大图片在上横向排列,三张小图在下横向排列)
   两张大图宽高
   maxImageViewWidth = ( width - space ) / 2
   maxImageViewHeight = maxImageViewWidth
   三张小图宽高
   imageViewWidth = baseLength
   imageViewHeight = baseLength
 
  6张图片 (大图在左上角,五张图片围绕大图排列)
   五张小图宽高
   imageViewWidth = baseLength
   imageViewHeight = baseLength
   大图宽高
   maxImageViewWidth = width - space - baseLength
   maxImageViewHeight = 2*baseLength + space
 
  7张图片 (大图在上,六张小图分两行在下横向排列)
   六张小图宽高
   imageViewWidth = baseLength
   imageViewHeight = baseLength
   大图宽高
   maxImageViewWidth = width
   maxImageViewHeight = 1.5*baseLength
 
  8张图片 (两张大图片在上横向排列,六张小图分两行在下横向排列)
   六张小图宽高
   imageViewWidth = baseLength
   imageViewHeight = baseLength
   两张大图宽高
   maxImageViewWidth = ( width - space ) / 2
   maxImageViewHeight = maxImageViewWidth
 
  9张图片
   imageViewWidth = baseLength
   imageViewHeight = baseLength

具体代码实现为NineGridView中的measureChildView方法

2.2 确定ImageView摆放位置

/**
     * 子View位置摆放
     */
    private void layoutChildView() {
        //布局时要考虑 padding
        switch (mUrls.size()) {
            default:
            case 0:
            case 1:
                break;
            case 2:
                for (int i = 0; i < getChildCount() - 1; i++) {
                    View childView = getChildAt(i);
                    if (i < 2) {
                        int width = childView.getMeasuredWidth();
                        childView.layout(mPaddingLeft + (i * (width + mSpace + mOverSpace)),
                                mPaddingTop,
                                mPaddingLeft + (i * (width + mSpace + mOverSpace)) + width,
                                mPaddingTop + width);
                    } else {
                        childView.layout(0, 0, 0, 0);
                    }
                }
                break;
            case 3:
                for (int i = 0; i < getChildCount() - 1; i++) {
                    View childView = getChildAt(i);
                    if (i < 3) {
                        int width = childView.getMeasuredWidth();
                        int height = childView.getMeasuredHeight();
                        if (i == 0) {
                            childView.layout(mPaddingLeft, mPaddingTop, width + mPaddingLeft, mPaddingTop + height);
                        } else {
                            int count = i - 1;
                            childView.layout(mPaddingLeft + mMeasuredShowWidth - width,
                                    mPaddingTop + (count * mSpace) + (count * height),
                                    mPaddingLeft + mMeasuredShowWidth,
                                    mPaddingTop + ((count + 1) * height) + (count * mSpace));
                        }
                    } else {
                        childView.layout(0, 0, 0, 0);
                    }
                }
                break;
            case 4:
                for (int i = 0; i < getChildCount() - 1; i++) {
                    View childView = getChildAt(i);
                    if (i < 4) {
                        int width = childView.getMeasuredWidth();
                        int height = childView.getMeasuredHeight();
                        if (i == 0) {
                            childView.layout(mPaddingLeft, mPaddingTop, width + mPaddingLeft, mPaddingTop + height);
                        } else {
                            int count = i - 1;
                            childView.layout(mPaddingLeft + (count * mSpace) + (count * width),
                                    mMeasuredShowWidth - mBaseLength + mPaddingTop,
                                    mPaddingLeft + (count * mSpace) + ((count + 1) * width),
                                    mMeasuredShowWidth + mPaddingTop);
                        }
                    } else {
                        childView.layout(0, 0, 0, 0);
                    }
                }
                break;
            case 5:
                for (int i = 0; i < getChildCount() - 1; i++) {
                    View childView = getChildAt(i);
                    if (i < 5) {
                        int width = childView.getMeasuredWidth();
                        if (i < 2) {
                            childView.layout(mPaddingLeft + (i * (width + mSpace + mOverSpace)),
                                    mPaddingTop,
                                    mPaddingLeft + (i * (width + mSpace + mOverSpace)) + width,
                                    mPaddingTop + width);
                        } else {
                            int count = i - 2;
                            childView.layout(mPaddingLeft + (count * mSpace) + (count * width),
                                    mPaddingTop + ((mMeasuredShowWidth - mSpace) / 2) + mSpace,
                                    mPaddingLeft + (count * mSpace) + ((count + 1) * width),
                                    mPaddingTop + ((mMeasuredShowWidth - mSpace) / 2) + mSpace + width);
                        }
                    } else {
                        childView.layout(0, 0, 0, 0);
                    }
                }
                break;
            case 6:
                for (int i = 0; i < getChildCount() - 1; i++) {
                    View childView = getChildAt(i);
                    if (i < 6) {
                        int width = childView.getMeasuredWidth();
                        int height = childView.getMeasuredHeight();
                        if (i == 0) {
                            childView.layout(mPaddingLeft, mPaddingTop, mPaddingLeft + width, height + mPaddingTop);
                        } else if (i < 3) {
                            int count = i - 1;
                            childView.layout(mPaddingLeft + mMeasuredShowWidth - width,
                                    mPaddingTop + (count * mSpace) + (count * height),
                                    mPaddingLeft + mMeasuredShowWidth,
                                    mPaddingTop + ((count + 1) * height) + (count * mSpace));
                        } else {
                            int count = i - 3;
                            childView.layout(mPaddingLeft + (count * mSpace) + (count * width),
                                    mPaddingTop + (2 * mSpace) + (2 * mBaseLength),
                                    mPaddingLeft + (count * mSpace) + ((count + 1) * width),
                                    mPaddingTop + mMeasuredShowWidth);
                        }
                    } else {
                        childView.layout(0, 0, 0, 0);
                    }
                }
                break;
            case 7:
                for (int i = 0; i < getChildCount() - 1; i++) {
                    View childView = getChildAt(i);
                    if (i < 7) {
                        int width = childView.getMeasuredWidth();
                        int height = childView.getMeasuredHeight();
                        if (i == 0) {
                            childView.layout(mPaddingLeft, mPaddingTop, mPaddingLeft + width, height + mPaddingTop);
                        } else if (i < 4) {
                            int count = i - 1;
                            childView.layout(mPaddingLeft + (count * mSpace) + (count * width),
                                    mPaddingTop + (int) (1.5 * height) + mSpace,
                                    mPaddingLeft + (count * mSpace) + ((count + 1) * width),
                                    mPaddingTop + (int) (2.5 * height) + mSpace);
                        } else {
                            int count = i - 4;
                            childView.layout(mPaddingLeft + (count * mSpace) + (count * width),
                                    mPaddingTop + (int) (2.5 * height) + (2 * mSpace),
                                    mPaddingLeft + (count * mSpace) + ((count + 1) * width),
                                    mPaddingTop + (int) (3.5 * height) + (2 * mSpace));
                        }
                    } else {
                        childView.layout(0, 0, 0, 0);
                    }
                }
                break;
            case 8:
                for (int i = 0; i < getChildCount() - 1; i++) {
                    View childView = getChildAt(i);
                    if (i < 8) {
                        int width = childView.getMeasuredWidth();
                        if (i < 2) {
                            childView.layout(mPaddingLeft + (i * (width + mSpace + mOverSpace)),
                                    mPaddingTop,
                                    mPaddingLeft + (i * (width + mSpace + mOverSpace)) + width,
                                    mPaddingTop + width);
                        } else if (i < 5) {
                            int count = i - 2;
                            childView.layout(mPaddingLeft + (count * mSpace) + (count * width),
                                    mPaddingTop + ((mMeasuredShowWidth - mSpace) / 2) + mSpace,
                                    mPaddingLeft + (count * mSpace) + ((count + 1) * width),
                                    mPaddingTop + ((mMeasuredShowWidth - mSpace) / 2) + mSpace + width);
                        } else {
                            int count = i - 5;
                            childView.layout(mPaddingLeft + (count * mSpace) + (count * width),
                                    mPaddingTop + ((mMeasuredShowWidth - mSpace) / 2) + width + (2 * mSpace),
                                    mPaddingLeft + (count * mSpace) + ((count + 1) * width),
                                    mPaddingTop + ((mMeasuredShowWidth - mSpace) / 2) + (2 * width) + (2 * mSpace));
                        }
                    } else {
                        childView.layout(0, 0, 0, 0);
                    }
                }
                break;
            case 9:
                for (int i = 0; i < getChildCount() - 1; i++) {
                    View childView = getChildAt(i);
                    int width = childView.getMeasuredWidth();
                    int height = childView.getMeasuredHeight();
                    if (i < 3) {
                        childView.layout(mPaddingLeft + (i * mSpace) + (i * width),
                                mPaddingTop,
                                mPaddingLeft + (i * mSpace) + ((i + 1) * width),
                                mPaddingTop + height);
                    } else if (i < 6) {
                        int count = i - 3;
                        childView.layout(mPaddingLeft + (count * mSpace) + (count * width),
                                mPaddingTop + height + mSpace,
                                mPaddingLeft + (count * mSpace) + ((count + 1) * width),
                                mPaddingTop + (2 * height) + mSpace);
                    } else {
                        int count = i - 6;
                        childView.layout(mPaddingLeft + (count * mSpace) + (count * width),
                                mPaddingTop + (2 * height) + (2 * mSpace),
                                mPaddingLeft + (count * mSpace) + ((count + 1) * width),
                                mPaddingTop + (3 * height) + (2 * mSpace));
                    }
                }
                break;
        }
    }

2.3 设置间距及圆角

间距:通过自定义属性 pictureSpace 获取间距值,在onLayout和onMeasure时对子view进行相应调整

圆角:通过继承于 CardView 沿用其设置圆角的功能

2.4 完整源码

 

3.功能说明

  • 该View继承于CardView以达到实现圆角的目的(也就是通过cardCornerRadius属性设置圆角值)

  • 圆角及实现效果因为不依赖于图片加载框架实现,所以可自行选用替换图片加载框架

  • 图片数量为一张时,按比例缩放图片,圆角不会随着图片宽高不同而变形

  • 提供自定义属性pictureMaxHeight:图片数量只有一张时该图最大显示高度

  • 提供自定义属性pictureSpace:图片之间的间隔距离

  • 填充网络图片:setUrls(List urls)

  • 监听图片点击事件:setCallback(NineGridView.Callback callback)

 

4.注意事项

  • 需在NineGridView中的TODO标注里自行替换当前项目所使用的图片加载框架

  • 该View中的ImageView,除了图片数量为1时,采用的是按比例自适应宽高(有最大高度及宽度限制),其余情况下ImageView的宽高都是固定值。具体值由NineGridView能展示的最大宽度决定

  • 该View用于RecyclerView时,为了解决单图item被回收后,重新出现在屏幕时由于高度变化造成item跳动的问题,需要牺牲部分recyclerView的复用特性。具体表现为重写适配器getItemViewType方法,判断图片数量是否为1,是则将position返回作为itemViewType(写法参考)(实则最优解应该是为单图情况专门写一个itemLayout并赋予指定itemViewType)

 

5.最后

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值