在工作中遇到了一个开发的需求是将多选的图片聚合起来,类似于微信群聊那种九宫格的头像的那种。当然遇到这个需求首先肯定会从网上查找一些资料,发现大部分的实现类似于通过定义九宫格的ImageView控件来实现,这样一来,实际上图片没有被压缩,而且还生成了一大堆的控件,对内存性能效果是很大的。 既然网上没有合适好的算法方案,那不如自己来实现一个吧,通过分析头像的的形成的原理主要有如下的几个规律:

1、当图片只有一个的时候,就直接显示这样大图

2、当图片在2-4个的时候,图片就会被分成2列,图片的尺寸大致是控件宽度的一半

3、当图片数量 >4 个是收,图片主要分为三列显示

这样一来就可以根据这样的规律来进行计算了。

我的算法原理是将这些图片通过算法进行组合到一起,形成一个新的Bitmap,这样你需要用的时候直接拿到这个Bitmap使用就可以了。

主要的核心算法如下:

//计算九宫格的图片
public Bitmap formatNineCellBitmap(List<Bitmap> bitmapList) {if (bitmapList == null || bitmapList.size() == 0) {return null;}int length = bitmapList.size();//最多显示9张if (length > 9) {length = 9;}int bitmapSize = builder.bitmapSize;//图片画板的内间距int paddingSize = builder.paddingSize;//每张图片之间的间距int itemMargin = builder.itemMargin;//每张需要绘制图片的宽高int cellSize;switch (length) {case 1:cellSize = bitmapSize - paddingSize * 2;break;case 2:case 3:case 4:cellSize = (bitmapSize - paddingSize * 2 - itemMargin) / 2;break;default: //默认是三列的图标展示cellSize = (bitmapSize - paddingSize * 2 - itemMargin * 2) / 3;}//画布Bitmap outBitmap = Bitmap.createBitmap(bitmapSize, bitmapSize, Bitmap.Config.ARGB_8888);Canvas canvas = new Canvas(outBitmap);//先画合成之后的背景颜色,默认是白色canvas.drawColor(builder.backgroundColor);//这个主要是用来计算绘制图片的起始位置int left = paddingSize, top = paddingSize;int moveSize = cellSize + itemMargin;for (int i = 0; i < length; i++) {Bitmap dealBitmap = scaleAndCenterInsideBitmap(bitmapList.get(i), cellSize);if (dealBitmap != null) {switch (length) {case 1:left = paddingSize;top = paddingSize;break;case 2:left = paddingSize + moveSize * i;top = (bitmapSize - cellSize) / 2;break;case 3:if (i == 0) {left = (bitmapSize - cellSize) / 2;} else {left = paddingSize + moveSize * (i % 2);}top = paddingSize + moveSize * ((i + 1) / 2);break;case 4:left = paddingSize + moveSize * (i % 2);top = paddingSize + moveSize * (i / 2);break;case 5:if (i <= 1) {left = (bitmapSize - cellSize * 2 - paddingSize * 2) / 2 + moveSize * (i % 2);} else {left = paddingSize + moveSize * (i % 3);}top = paddingSize + (bitmapSize - cellSize * 2) / 2 + moveSize * ((i + 1) / 3);break;case 6:left = paddingSize + moveSize * (i % 3);top = paddingSize + (bitmapSize - cellSize * 2) / 2 + moveSize * (i / 3);break;case 7:if (i == 0) {left = (bitmapSize - cellSize - paddingSize * 2) / 2;} else if (i <= 3) {left = paddingSize + moveSize * ((i - 1) % 3);} else {left = paddingSize + moveSize * ((i - 1) % 3);}top = paddingSize + moveSize * ((i + 2) / 3);break;case 8:if (i <= 1) {left = (bitmapSize - cellSize * 2 - paddingSize * 2) / 2 + moveSize * (i % 3);} else if (i <= 4) {left = paddingSize + moveSize * ((i - 2) % 3);} else {left = paddingSize + moveSize * ((i - 2) % 3);}top = paddingSize + moveSize * ((i + 1) / 3);break;case 9:left = paddingSize + moveSize * (i % 3);top = paddingSize + moveSize * (i / 3);break;}canvas.drawBitmap(dealBitmap, left, top, null);}}return outBitmap;
}复制代码

//将图片缩放换成指定宽高,并且CenterInside模式
private Bitmap scaleAndCenterInsideBitmap(Bitmap sourceBitmap, int size) {float sourceWidth = sourceBitmap.getWidth();float sourceHeight = sourceBitmap.getHeight();float rate = sourceWidth / sourceHeight;float destRate = 1;Bitmap outBitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);Canvas canvas = new Canvas(outBitmap);Bitmap resizeBitmap;Matrix matrix = new Matrix();if (rate < destRate) {//图片过高,需要裁掉部分高度float scale = size / sourceWidth;matrix.setScale(scale, scale);resizeBitmap = Bitmap.createBitmap(sourceBitmap, 0, 0, (int) sourceWidth, (int) sourceHeight, matrix, true);float cropHeight = (sourceHeight - sourceWidth) * scale;canvas.drawBitmap(resizeBitmap, 0, -cropHeight / 2, null);} else {//图片过宽,需要裁掉部分宽度float scale = size / sourceHeight;matrix.setScale(scale, scale);resizeBitmap = Bitmap.createBitmap(sourceBitmap, 0, 0, (int) sourceWidth, (int) sourceHeight, matrix, true);float cropWidth = (sourceWidth - sourceHeight) * scale;canvas.drawBitmap(resizeBitmap, -cropWidth / 2, 0, null);}return outBitmap;
}复制代码

上述是主要的核心算法,主要根据传入图片的数量进行计算来确定每张图片显示和摆放的位置,这个使用起来简单高效。

下面是算法的封装,将这个类直接复制到你的项目中就可以使用了。

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;import java.util.ArrayList;
import java.util.List;import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;/**** @date 创建时间 2018/9/18 14:40* @author 作者: W.YuLong* @description 将图片聚合成9宫格*/
public class NineCellBitmapUtil {private Builder builder;private NineCellBitmapUtil(Builder builder) {this.builder = builder;}public int getBitmapSize() {return builder.bitmapSize;}public static Builder with() {return new Builder();}/*这里用到了RXJava来将网络传入的图片URL或者Path转换层Bitmap,如果你还没有导入RXJava的话,可以将这个方法删除* 或者用你自己的方式将URL转换为Bitmap*/public <T> void collectBitmap(List<T> dataList, BitmapCallBack callBack) {Observable.create(new ObservableOnSubscribe() {@Overridepublic void subscribe(ObservableEmitter e) throws Exception {for (T t : dataList) {e.onNext(t);}e.onComplete();}}).map(new Function<T, Bitmap>() {@Overridepublic Bitmap apply(T ts) throws Exception {return ImageLoadTool.transferBitmap(ts);}}).observeOn(AndroidSchedulers.mainThread()).subscribeOn(Schedulers.newThread()).subscribe(new Observer<Bitmap>() {private List<Bitmap> resultList;@Overridepublic void onSubscribe(Disposable d) {if (resultList == null) {resultList = new ArrayList<>();}}@Overridepublic void onNext(Bitmap bitmap) {resultList.add(bitmap);}@Overridepublic void onComplete() {callBack.onLoadingFinish(formatNineCellBitmap(resultList));}@Overridepublic void onError(Throwable e) {}});}//计算九宫格的图片public Bitmap formatNineCellBitmap(List<Bitmap> bitmapList) {if (bitmapList == null || bitmapList.size() == 0) {return null;}int length = bitmapList.size();//最多显示9张if (length > 9) {length = 9;}int bitmapSize = builder.bitmapSize;//图片画板的内间距int paddingSize = builder.paddingSize;//每张图片之间的间距int itemMargin = builder.itemMargin;//每张需要绘制图片的宽高int cellSize;switch (length) {case 1:cellSize = bitmapSize - paddingSize * 2;break;case 2:case 3:case 4:cellSize = (bitmapSize - paddingSize * 2 - itemMargin) / 2;break;default: //默认是三列的图标展示cellSize = (bitmapSize - paddingSize * 2 - itemMargin * 2) / 3;}//画布Bitmap outBitmap = Bitmap.createBitmap(bitmapSize, bitmapSize, Bitmap.Config.ARGB_8888);Canvas canvas = new Canvas(outBitmap);//先画合成之后的背景颜色,默认是白色canvas.drawColor(builder.backgroundColor);//这个主要是用来计算绘制图片的起始位置int left = paddingSize, top = paddingSize;int moveSize = cellSize + itemMargin;for (int i = 0; i < length; i++) {Bitmap dealBitmap = scaleAndCenterInsideBitmap(bitmapList.get(i), cellSize);if (dealBitmap != null) {switch (length) {case 1:left = paddingSize;top = paddingSize;break;case 2:left = paddingSize + moveSize * i;top = (bitmapSize - cellSize) / 2;break;case 3:if (i == 0) {left = (bitmapSize - cellSize) / 2;} else {left = paddingSize + moveSize * (i % 2);}top = paddingSize + moveSize * ((i + 1) / 2);break;case 4:left = paddingSize + moveSize * (i % 2);top = paddingSize + moveSize * (i / 2);break;case 5:if (i <= 1) {left = (bitmapSize - cellSize * 2 - paddingSize * 2) / 2 + moveSize * (i % 2);} else {left = paddingSize + moveSize * (i % 3);}top = paddingSize + (bitmapSize - cellSize * 2) / 2 + moveSize * ((i + 1) / 3);break;case 6:left = paddingSize + moveSize * (i % 3);top = paddingSize + (bitmapSize - cellSize * 2) / 2 + moveSize * (i / 3);break;case 7:if (i == 0) {left = (bitmapSize - cellSize - paddingSize * 2) / 2;} else if (i <= 3) {left = paddingSize + moveSize * ((i - 1) % 3);} else {left = paddingSize + moveSize * ((i - 1) % 3);}top = paddingSize + moveSize * ((i + 2) / 3);break;case 8:if (i <= 1) {left = (bitmapSize - cellSize * 2 - paddingSize * 2) / 2 + moveSize * (i % 3);} else if (i <= 4) {left = paddingSize + moveSize * ((i - 2) % 3);} else {left = paddingSize + moveSize * ((i - 2) % 3);}top = paddingSize + moveSize * ((i + 1) / 3);break;case 9:left = paddingSize + moveSize * (i % 3);top = paddingSize + moveSize * (i / 3);break;}canvas.drawBitmap(dealBitmap, left, top, null);}}return outBitmap;}//将图片缩放换成指定宽高,并且CenterInside模式private Bitmap scaleAndCenterInsideBitmap(Bitmap sourceBitmap, int size) {float sourceWidth = sourceBitmap.getWidth();float sourceHeight = sourceBitmap.getHeight();float rate = sourceWidth / sourceHeight;float destRate = 1;Bitmap outBitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);Canvas canvas = new Canvas(outBitmap);Bitmap resizeBitmap;Matrix matrix = new Matrix();if (rate < destRate) {//图片过高,需要裁掉部分高度float scale = size / sourceWidth;matrix.setScale(scale, scale);resizeBitmap = Bitmap.createBitmap(sourceBitmap, 0, 0, (int) sourceWidth, (int) sourceHeight, matrix, true);float cropHeight = (sourceHeight - sourceWidth) * scale;canvas.drawBitmap(resizeBitmap, 0, -cropHeight / 2, null);} else {//图片过宽,需要裁掉部分宽度float scale = size / sourceHeight;matrix.setScale(scale, scale);resizeBitmap = Bitmap.createBitmap(sourceBitmap, 0, 0, (int) sourceWidth, (int) sourceHeight, matrix, true);float cropWidth = (sourceWidth - sourceHeight) * scale;canvas.drawBitmap(resizeBitmap, -cropWidth / 2, 0, null);}return outBitmap;}public static class Builder {//画布宽度和高度private int bitmapSize = 300;//聚合后的图片内间距private int paddingSize = 10;//每张图片的间距private int itemMargin = 15;//聚合后图片的背景色private int backgroundColor = Color.WHITE;public Builder() {}public NineCellBitmapUtil build() {return new NineCellBitmapUtil(this);}public int getBackgroundColor() {return backgroundColor;}public Builder setBackgroundColor(int backgroundColor) {this.backgroundColor = backgroundColor;return this;}public int getBitmapSize() {return bitmapSize;}public Builder setBitmapSize(int bitmapSize) {this.bitmapSize = bitmapSize;return this;}public int getPaddingSize() {return paddingSize;}public Builder setPaddingSize(int paddingSize) {this.paddingSize = paddingSize;return this;}public int getItemMargin() {return itemMargin;}public Builder setItemMargin(int itemMargin) {this.itemMargin = itemMargin;return this;}}/****@date 创建时间 2018/9/18 11:50*@author 作者: W.YuLong*@description 图片聚合完成之后的回调*/public interface BitmapCallBack {/*处理完成*/void onLoadingFinish(Bitmap bitmap);}
}
复制代码

具体使用如下:

import android.graphics.Bitmap;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;import com.hwariot.android.R;
import com.hwariot.android.tools.util.NineCellBitmapUtil;
import com.hwariot.lib.base.BaseActivity;import java.util.ArrayList;
import java.util.List;import butterknife.BindView;
import butterknife.ButterKnife;/**** @date 创建时间 2018/9/19 11:30* @author 作者: W.YuLong* @description*/
public class TestActivity extends BaseActivity implements View.OnClickListener {@BindView(R.id.test_ImageView) ImageView imageView;@BindView(R.id.test_add_Button) Button addButton;@BindView(R.id.test_remove_Button) Button removeButton;NineCellBitmapUtil nineCellBitmapUtil;String[] urlArray = {"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1538451017&di=dd252d5e4594f786d34891fb6be826ff&imgtype=jpg&er=1&src=http%3A%2F%2Fimg5.duitang.com%2Fuploads%2Fitem%2F201311%2F28%2F20131128101128_JZUaM.jpeg","https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1537856300317&di=e4aebfba49e34aa5bd8de8346b268229&imgtype=0&src=http%3A%2F%2Fs9.knowsky.com%2Fbizhi%2Fl%2F35001-45000%2F20095294542896291195.jpg","https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1537856300315&di=8def4a51ac362ffa7602ca768d76c982&imgtype=0&src=http%3A%2F%2Fg.hiphotos.baidu.com%2Fzhidao%2Fpic%2Fitem%2Ff9198618367adab44ce126ab8bd4b31c8701e420.jpg","https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1537345745067&di=d51264f2b354863c865a1da4b6672d90&imgtype=0&src=http%3A%2F%2Fpic40.nipic.com%2F20140426%2F6608733_175243397000_2.jpg","https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=3394638573,2701566035&fm=26&gp=0.jpg","https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=2758635669,3034136689&fm=26&gp=0.jpg","http://t.cn/EvHONPF","http://t.cn/EvHOTa9","http://t.cn/EvHO38y",};private List<String> imgList = new ArrayList<>();@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_test_layout);ButterKnife.bind(this);// 实例化这个这个工具类,默认聚合的图片尺寸是1000像素,每张图的间距是20像素nineCellBitmapUtil = NineCellBitmapUtil.with().setBitmapSize(1000).setItemMargin(20).setPaddingSize(20).build();addButton.setOnClickListener(this);removeButton.setOnClickListener(this);}private int i = 0;@Overridepublic void onClick(View v) {if (v == addButton) {if (i < urlArray.length){imgList.add(urlArray[i]);i++;//调用这个方法进行聚合nineCellBitmapUtil.collectBitmap(imgList, new NineCellBitmapUtil.BitmapCallBack() {@Overridepublic void onLoadingFinish(Bitmap bitmap) {imageView.setImageBitmap(bitmap);}});}} else if (v == removeButton) {if (i > 0){i--;imgList.remove(i);nineCellBitmapUtil.collectBitmap(imgList, new NineCellBitmapUtil.BitmapCallBack() {@Overridepublic void onLoadingFinish(Bitmap bitmap) {imageView.setImageBitmap(bitmap);}});}}}
}复制代码

使用就是先实例化这个工具类

// 实例化这个这个工具类,默认聚合的图片尺寸是1000像素,每张图的间距是20像素nineCellBitmapUtil = NineCellBitmapUtil.with().setBitmapSize(1000).setItemMargin(20).setPaddingSize(20).build();复制代码

然后调用这个聚合后的算法

//调用这个方法进行聚合
nineCellBitmapUtil.collectBitmap(imgList, new NineCellBitmapUtil.BitmapCallBack() {@Overridepublic void onLoadingFinish(Bitmap bitmap) {imageView.setImageBitmap(bitmap);}
});           复制代码

这样就完成了,是不是使用起来非常简单方便高效!

下图就是展示的效果

转载于:https://juejin.im/post/5ba9ae2fe51d450e462840e5

类似微信群聊九宫格头像的算法实现相关推荐

  1. 使用伪类来实现类似微信群聊的头像样式

    最近看了一篇张鑫旭的博客,觉得人家的想法特别奇特,很值得学习.遂借鉴学习并记录一下,以备不时之需. 原文地址:伪类匹配列表数目实现微信群头像CSS布局的技巧 简介 我们在使用微信群聊的时候,群聊的头像 ...

  2. Android自定义控件---仿微信群聊(九宫格)头像

    这几天找九宫格头像的源码,发现网上很少有人写(不知道是不是我的搜索关键词不准确).终于在github发现有一哥们写了一份源码,download了下来研究了一段时间,不太符合公司的产品需求 ,无奈只能放 ...

  3. Android 仿钉钉、微信 群聊组合头像 CombineBitmap

    前段时间,产品提出群组头像改为类似微信的九宫格图像,于是在网上找了一个非常好用的控件CombineBitmap,在这里记录一下. 作者的GitHub项目地址:https://github.com/Ot ...

  4. Android 仿钉钉、微信 群聊组合头像

    CombineBitmap 项目地址:SheHuan/CombineBitmap 简介: Android 仿钉钉.微信 群聊组合头像 更多:作者   提 Bug 标签: 效果预览   功能 生成类似钉 ...

  5. Android 仿钉钉、微信 群聊组合头像,Android插件化入门指南

    简介: Android 仿钉钉.微信 群聊组合头像 更多:作者   提 Bug 标签: 效果预览 | | | |   | | - | - | - | - | | | | | | | | | | | 功 ...

  6. Android 仿钉钉、微信 群聊组合头像,大厂面试题汇总

    效果预览 | | | |   | | - | - | - | - | | | | | | | | | | | 功能 生成类似钉钉.微信 群聊组合头像 Bitmap 可使用图片资源 id.bitmap ...

  7. Android仿微信视频群聊,Android 仿钉钉、微信 群聊组合头像

    功能生成类似钉钉.微信 群聊组合头像Bitmap 可使用图片资源id.bitmap或者使用url从网络加载,传入对应数组即可 网络加载时支持线程池 支持磁盘缓存.内存缓存.(记得申请磁盘缓存需要的文件 ...

  8. Android 仿微信群聊组合头像

    转载:http://blog.csdn.net/tiantianshangcha/article/details/9836809 大概原理是先设想一个要合成的图片的大小,然后根据要合成的图片的个数以及 ...

  9. 使用Socket实现类似微信群聊的功能

    1.前言 套接字(Sockets)是双向通信信道的端点.套接字可以在一个进程内,在同一机器上的进程之间,或者在不同主机的进程之间进行通信,主机可以是任何一台有连接互联网的机器. 套接字可以通过多种不同 ...

最新文章

  1. 从李开复的Google看管理的文化和背景
  2. iOS9网络适配 info.plist配置
  3. python处理excel教程实例-python 读写excel文件操作示例【附源码下载】
  4. LeetCode Multiply Strings(大整数相乘)
  5. is 和 == 区别 编码和解码
  6. Android插件化原理解析——概要
  7. Shell——从hello world和echo命令开始
  8. HDU 4028 The time of a day STL 模拟题
  9. Lotus,协作领域的常青树
  10. python批量生成word报告_Python操作Word批量生成合同的实现示例
  11. Sendmail在企业网中的应用
  12. 对php的感受100字_【php实训心得】php心得体会
  13. python的paramiko模块
  14. 玩转CAD格式,CAD转PDF,CAD转DWF,只需四个步骤高效完成
  15. 社会工程学主要学习内容及书籍推荐
  16. C# 红色警戒2无限金钱+电力负载 外挂源码示例
  17. zabbix3.4接入微信报警
  18. 论文阅读:Retrieval-augmented Generation across Heterogeneous Knowledge
  19. 元宇宙,一场有趣好玩的大冒险已经瞧瞧开始了……
  20. Windows 程序设计基础

热门文章

  1. CVPR2022论文集锦 | CVPR2022最新论文 | CVPR2022审稿结果 | CVPR2022录取结果
  2. interProScan的使用
  3. python如何安装spyder?
  4. Minecraft 1.18.1、1.18.2模组开发 02.方块和物品
  5. 凹凸技术揭秘·羚珑页面可视化·成长蜕变之路
  6. 蛙蛙推荐:蛙蛙教你发明一种新语言之一--词法分析和语法分析
  7. 牛客每日练习----あなたの蛙が帰っています,おみやげをまらいました,写真がとどいています
  8. linux java 缓存服务器,linux服务器缓存环境memcached筹建及应用(java)
  9. QTP数据驱动和关键字驱动
  10. vasp计算压电系数_求助DFTP算出来的压电系数 - 第一原理 - 小木虫 - 学术 科研 互动社区...