类似微信朋友圈中的图片展示大家肯定很熟悉了,这篇文章讲述的自定义View就是类似这个展示方式的View了。

先看效果图:

展示规则

1、如果只有1张图片,则图片宽度占父控件总宽度的2/3(图片高度和宽度相同)

2、如果超出1张图片(不为4张的情况),则按照每行3列的方式排列图片

3、如果正好有4张图片,则用2*2的方式排列,如图:

图片之间的间隙可以在布局文件中进行调整

实现思路

实现自定义控件的方式有很多,比如:继承View(比较适合不包含子控件的场景)

继承ViewGroup(适合包含有子控件的场景)

继承特定的View的子类,比如Button等(适合在已有的控件上扩展功能的场景)

很显然,咱们要实现的这个是有自己的子控件的,最多情况有9个ImageView子控件,所以我们需要继承ViewGroup。

继承ViewGroup需要我们自己测量控件的大小以及控制子控件的位置。

假设有LGNineGrideView继承了ViewGroup,那么我们面临的问题有:如果确定LGNineGrideView的大小(宽,高)

如果确定LGNineGrideView的子控件的大小和位置

如果处理LGNineGrideView在类似ListView这样的列表视图中的复用问题

实际前两个问题还是比较简单的:

确定父控件大小

如上图:假设父控件LGNineGrideView是红色框标示的,其子控件用每个小的黑色框标示(每个黑色框的宽高相同,绘图有点偏差请见谅)。

pl:即paddingleft, pr:paddingright, pt:paddingtop, pb:padingbottom, sp:space(子控件之间间隙)

那么:

LGNineGrideView的高度为:rows * grideHeight + (rows - 1) * sp + pt + pb。rows:表示子控件的行数

LGNineGrideView的宽度需要在布局文件中定义

子控件的位置和大小如何确定:确定大小

很显然子控件的宽度需要根据父控件(LGNineGrideView)来确定,假定其值为:totalWidth,则子控件的宽度需要根据列数colums来计算如下:

availableWidth:totalWidth - pl - pr

grideWidth = (availableWidth - sp * (colums - 1)) / 3;

注意特殊情况子控件只有1个的时候:

grideWidth = availableWidth * 2 / 3

grideHeight = grideWidth

确定位置核心代码如下: for (int i = 0; i < size; ++i) {

x = i % colums * (grideWidth + sp) + pl;

y = i / colums * (grideHeight + sp) + pt;

r = x + tmpWidth;

b = y + tmpHeight;

view.layout(x, y, r, b);//布局子控件

} size:子控件的个数,理解代码并不难,只要把每个子控件标上0~size的编号如上图,再找到控件是落在3*3的矩阵中的行号和列号。举例:假设编号为7的控件,再矩阵中的行号为:i / colums ==> 7 / 3 = 2,列号为:i % colums ==> 7 % 3 = 1,也就是说其落在矩阵的2行1列的位置,再计算坐标就不难了。

子控件的复用问题

处理这个问题时,我也是在网上找了些参考的,但是大部分都是删除再重新创建来实现,有些更加粗暴直接删除全部子控件,再根据数据重新创建新的控件。

举个例子:假设现在A有6个子控件,如果其在类似listview的视图中难免会出现对A的复用,假设现在只需要A显示2个子控件,为了不出现脏区现象,直接将之前6个全部删除,再重新创建2个添加到A里也是可行的。但我个人认为如果用户滑动的很频繁,那么会出现频繁的删除和添加操作,内存抖动会比较频繁影响了性能同时还会出现视图闪动。

解决方案:如果A需要显示的控件大于已有的控件则创建并添加子控件,否则将多余的控件隐藏,核心代码:int childSize = getChildCount();

for (int i = 0; i < size; ++i) {

ImageView view = (ImageView) getChildAt(i);

if (view == null) {

view = new ImageView();

}

view.setVisibility(VISIBLE);

...

}

if (size < childSize) {

for (int i = size; i < childSize; ++i) {

ImageView view = (ImageView) getChildAt(i);

view.setVisibility(GONE);

}

}

以上讲述了实现的总体核心思路,最后总结代码如下:

LGNineGrideView.java//测量控件的大小及子控件的大小

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

if (urls == null) {

setVisibility(GONE);

return;

}

int size = urls.size();

int sugMinWidth = getSuggestedMinimumWidth();

int minWidth = getPaddingLeft() + getPaddingRight() + sugMinWidth;

int totalWidth = resolveSizeAndState(minWidth, widthMeasureSpec, 0);

int availableWidth = totalWidth - getPaddingLeft() - getPaddingRight();

if (size == 1) {

grideWidth = availableWidth * 2 / 3;

grideHeight = grideWidth;

} else {

grideWidth = (availableWidth - space * (colums - 1)) / 3;

grideHeight = grideWidth;

}

int height = rows * grideHeight + (rows - 1) * space + getPaddingTop() + getPaddingBottom();

setMeasuredDimension(totalWidth, height);

}

//处理子控件的位置和显示逻辑

@Override

protected void onLayout(boolean changed, int l, int t, int r, int b) {

int size = urls.size();

...

int childSize = getChildCount();

for (int i = 0; i < size; ++i) {

final String url = urls.get(i);

ImageView view = (ImageView) getChildAt(i);

if (view == null) {

if (imageCreator == null) {

imageCreator = DefaultImageCreator.getInstance();

}

view = imageCreator.createImageView(context);

imageCreator.loadImage(context, url, view);

addView(view);

...

}

view.setVisibility(VISIBLE);

l = i % colums * (tmpWidth + space) + getPaddingLeft();

t = i / colums * (tmpHeight + space) + getPaddingTop();

r = l + tmpWidth;

b = t + tmpHeight;

view.layout(l, t, r, b);

}

if (size < childSize) {

for (int i = size; i < childSize; ++i) {

ImageView view = (ImageView) getChildAt(i);

view.setVisibility(GONE);

}

}

}

//计算行数和列数

private void initRowAndColum(int size) {

rows = (size - 1) / 3 + 1;

colums = (size - 1) % 3 + 1;

if (size == 4) {

rows = 2;

colums = 2;

return;

} else {

colums = 3;

}

}

//this.urls:存储ImageView的url地址

public void setImageDatas(Listurls) {

...

this.urls.clear();

this.urls.addAll(urls);

initRowAndColum(urls.size());

requestLayout();//数据变化,重新布局

}

代码说明

由于ImageView的创建和加载是因项目而定的,现在的加载框架有UIL,Picaso,Fresco,xUtil等等,所以在onLayout中创建并加载图片的代码如下:if (imageCreator == null) {

imageCreator = DefaultImageCreator.getInstance();

}

view = imageCreator.createImageView(context);

imageCreator.loadImage(context, url, view);

通过简单的工厂模式来进行解耦,你可以自己实现ImageCreator来决定如何生成ImageView并使用自己的加载框架加载图片,否则用的是我编写的默认工厂实现的创建和加载(使用了UIL加载框架),整个项目的代码结构如下图(包含了model:ninegrideview以及测试Demo.)

写在最后

demo开源github地址如下:

android 9图工具位子,Android自定义9宫格图片视图相关推荐

  1. android自定义9宫格图片视图

    类似微信朋友圈中的图片展示大家肯定很熟悉了,这篇文章讲述的自定义View就是类似这个展示方式的View了.先看效果图: 展示规则: 1,如果只有1张图片,则图片宽度占父控件总宽度的2/3(图片高度和宽 ...

  2. 【Android 命令行工具】Android 命令行工具简介 ( 官方文档 | SDK 命令行工具 | SDK 构建工具 | SDK 平台工具 | 模拟器工具 | Jetifier 工具 )

    文章目录 一.官方文档 二.Android 命令行工具简介 1.SDK 命令行工具 2.SDK 构建工具 3.SDK 平台工具 4.模拟器工具 5.Jetifier 工具 一.官方文档 Android ...

  3. Android宫格动态列,Android实现宫格图片连续滑动效果

    本文给大家介绍如何在Android中实现宫格图片连续滑动效果. 在这之前,写过几篇关于在Android中实现滑动的效果,毕竟滑动效果在Andriod开发中也使用得比较频繁,有兴趣的朋友请查看我以前的文 ...

  4. android9 关闭点击动画,在Android app中实现九(n)宫格图片连续滑动效果

    今天写这篇文章的缘由是前一段时间一个网友在我的博客上面留言,想要实现在GridLayout(相当于九宫格)中点击每项可左右滑动显示该宫格的图片,当该宫格的图片显示完以后,接着显示下一宫格的图片.那么看 ...

  5. Android宫格动态列,在Android app中实现九(n)宫格图片连续滑动效果

    今天写这篇文章的缘由是前一段时间一个网友在我的博客上面留言,想要实现在GridLayout(相当于九宫格)中点击每项可左右滑动显示该宫格的图片,当该宫格的图片显示完以后,接着显示下一宫格的图片.那么看 ...

  6. 不一样的 9 宫格图片展示,仿 Nice 首页图片列表 9 图样式,并实现拖拽效果

    ImageNice9Layout 项目地址:wobiancao/ImageNice9Layout  简介:不一样的 9 宫格图片展示,仿 Nice 首页图片列表 9 图样式,并实现拖拽效果 写在开头: ...

  7. 1分钟制作朋友圈9宫格图片,不用任何P图软件

    目录 前言 生成9宫格图片 生成9宫格动图 短视频生成动图9宫格 GIF直接生成动图9宫格 生成9宫格视频 使用pyqt5打包成exe界面 前言 我们很多人每天都有用手机刷朋友圈的习惯,可能大家也注意 ...

  8. html编写四宫格展示图片,七彩色图片排版工具一键制作四宫格图片教程

    七彩色图片排版工具是一款好用的图片排版工具.我们可以使用七彩色图片排版工具将电脑中的图片进行一键一键排版: 进入下载七彩色图片排版工具 2.1 官方版 大小:3.36 MB 日期:2019/2/25 ...

  9. 拼写检查工具是android,拼写检查工具框架  |  Android 开发者  |  Android Developers...

    Android 平台提供了拼写检查工具框架,可让您在应用中实现和使用拼写检查功能.该框架是 Android 平台提供的文本服务 API 之一. 如需在您的应用中使用该框架,您需要创建一个特殊类型的 A ...

最新文章

  1. SAP QM启用了Physical Sample Management后检验批有哪些特殊地方?
  2. 小白该如何学习Linux操作系统(2)
  3. shareSDK 微信分享的时候只能分享jpg格式的图片大于32k时为什么分享不出去?
  4. Lync常识之Lync客户端有哪些
  5. hdu 2448 Mining Station on the Sea(最短路+费用流)
  6. lis最长上升子序列o(nlogn)优化
  7. AndroidOS体系结构
  8. Bootstrap入门(二十九)JS插件6:弹出框
  9. shell 脚本学习(一)
  10. 获取浏览器高度_鸡哥解读FILECOIN浏览器之幸运值
  11. 汇编 一星题目字符串合集:输入字符,操作,在屏幕上显示
  12. debounce函数的实现
  13. nginx 配置反向代理
  14. 人工鱼群算法及其python实现过程
  15. Xilinx 7系列FPGA DDR3硬件设计规则
  16. 显微镜下的大明——第二个故事
  17. 黑客教父郭盛华:11个IDA Pro反汇编程序的替代品
  18. MD5算法如何被破解
  19. 华为举办首届用户精英论坛,携手共创数据存储新未来
  20. java实现微信企业付款到个人零钱(微信红包)

热门文章

  1. hbuilder创建app并利用真机运行调试
  2. MySQL 测试数据(附开源网站)
  3. 聚观早报 | 羊了个羊幕后推手月流水曾破亿;雷军卸任小米董事长
  4. 如何给图片加水印?这三个图片加水印方法,帮你添加花式水印
  5. 再给大家推荐一些小游戏
  6. Linux内核 eBPF基础:perf(2):perf性能管理单元PMU的注册
  7. 实现内网穿透,个人电脑秒变服务器
  8. 本科毕设-基于C8051单片机的身份识别系统设计
  9. 微信公众号关注渠道来源
  10. YOLOv5电车识别 电瓶车识别