BitmapShader
: 使用着色器Xfermode
:使用图层叠加ClipPath
:通过对画布裁剪的方式RoundedBitmapDrawable
: 系统API圆角类前面三种是通过继承
ImageView
重写onDraw()
方法实现
最后一种是系统API直接使用。
每一种方式都能实现显示圆形图片, 我们主要从以下几个方面来比较各个方式的不同
- 实现方式难易
- 空白的背景
- 抗锯齿的能力
第1种方式:BitmapShader
实现
通过对Paint
(画笔)设置着色器(Shader
)来实现,这里的Shader是指BitmapShader
,实际上有很多的Shader
,根据需要进行使用。
也就是说,当BitmapShader
设置在画笔上的时候,画笔画出来的内容就是一张bitmap
图,既然画的内容是Bitmap
,那么画的形状如果是圆形,这就是一个显示圆形的ImageView
了,当然也可以画圆角,画正方形。
//进行画笔初始化
private void init() {
mPaint = new Paint();
mPaint.setAntiAlias(true);//给画笔设置抗锯齿
mMatrix = new Matrix();
//该方法千万别放到onDraw()方法里面调用,否则会不停的重绘的,因为该方法调用了invalidate() 方法
setLayerType(View.LAYER_TYPE_SOFTWARE, null);//禁用硬加速
}
//重写onDraw()方法获取BitmapDrawable进行处理
@Override
protected void onDraw(Canvas canvas) {
Drawable drawable = getDrawable();
if (drawable instanceof BitmapDrawable) {
BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
Bitmap bitmap = bitmapDrawable.getBitmap();
//通过BitmapShader的方式实现
drawRoundByShaderMode(canvas, bitmap);
} else {
super.onDraw(canvas);
}
}
private void drawRoundByShaderMode(Canvas canvas, Bitmap bitmap) {
//获取到Bitmap的宽高
int bitmapWidth = bitmap.getWidth();
int bitmapHeight = bitmap.getHeight();
//获取到ImageView的宽高
int viewWidth = getWidth();
int viewHeight = getHeight();
//根据Bitmap生成一个BitmapShader,这里有个TileMode设置有三个参数可以选择:
//CLAMP, MIRROR, REPEAT 在后面会细说。
BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mMatrix.reset();
float minScale = Math.min(viewWidth / (float) bitmapWidth, viewHeight / (float) bitmapHeight);
mMatrix.setScale(minScale, minScale);//将Bitmap保持比列根据ImageView的大小进行缩放
bitmapShader.setLocalMatrix(mMatrix); //将矩阵变化设置到BitmapShader,其实就是作用到Bitmap
mPaint.setShader(bitmapShader);
//绘制圆形
canvas.drawCircle(bitmapWidth / 2, bitmapHeight / 2, Math.min(bitmapWidth / 2, bitmapHeight / 2), mPaint);
}
//RoundImageView在XML中的布局
<com.jabo.douban.demo.test.RoundImageViewTest
android:layout_width="90dp"
android:layout_height="90dp"
android:src="@drawable/test_view"
app:layout_constraintBottom_toTopOf="@+id/guideline6"
app:layout_constraintEnd_toStartOf="@+id/guideline5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
//以上就是全部关键代码,即可实现一个简单的圆形ImageView
原图是这样的:
效果图是这样的:
上面第33行中Bitmap
的TileMode
,其实就是说如果Bitmap
的内容没有铺满View
时,如何填充形状里面剩余的部分,我们来看一下
ClAMP
: Bitmap
以其内容的最后一行像素填充剩余的高的空白或者最后一列像素填充剩余宽空白MIRROR
:Bitmap
以其内容以镜像的方式填充剩余空白REPEAT
:Bitmap
以其内容以重复的方式填充剩余空白现在来看一下抗锯齿的能力,上面的图都是设置了抗锯齿的,如果把尺寸设置小一点:
表现还不错,为了进行明显的对比,我们去掉抗锯齿,对比下
// 注释掉抗锯齿 // mPaint.setAntiAlias(true);//给画笔设置抗锯齿
// 加上抗锯齿 mPaint.setAntiAlias(true);//给画笔设置抗锯齿
这就很明显了,表现的比较平滑,说明设置抗锯齿是有用的
所以关于BitmapShader
的实现方式的比较
第2种方式:Xfermode
实现
原理其实就是通过叠加显示的方式,设置不同的Xformode导致叠加的策略不同,最终显示不同,来一张图
PorterDuff.Mode
CLEAR
清除图像(源覆盖的目标像素被清除为0)
SRC
只显示源图像(源像素替换目标像素)
DST
只显示目标图像(源像素被丢弃,使目标保持完整)
SRC_OVER
将源图像放在目标图像上方
DST_OVER
将目标图像放在源图像上方(源像素是在目标像素后面绘制的。)
SRC_IN
只在源图像和目标图像相交的地方绘制【源图像】(保持源像素覆盖目标像素,丢弃剩余的源和目标像素)
DST_IN
只在源图像和目标图像相交的地方绘制【目标图像】,绘制效果受到源图像对应地方透明度影响
SRC_OUT
只在源图像和目标图像不相交的地方绘制【源图像】,相交的地方根据目标图像的对应地方的alpha进行过滤,目标图像完全不透明则完全过滤,完全透明则不过滤
DST_OUT
只在源图像和目标图像不相交的地方绘制【目标图像】,在相交的地方根据源图像的alpha进行过滤,源图像完全不透明则完全过滤,完全透明则不过滤(保持目标像素不被源像素所覆盖。丢弃由源像素覆盖的目标像素。丢弃所有源像素。)
SRC_ATOP
在源图像和目标图像相交的地方绘制【源图像】,在不相交的地方绘制【目标图像】,相交处的效果受到源图像和目标图像alpha的影响
DST_ATOP
在源图像和目标图像相交的地方绘制【目标图像】,在不相交的地方绘制【源图像】,相交处的效果受到源图像和目标图像alpha的影响
XOR
在源图像和目标图像相交的地方之外绘制它们,在相交的地方受到对应alpha和色值影响,如果完全不透明则相交处完全不绘制
DARKEN
变暗,较深的颜色覆盖较浅的颜色,若两者深浅程度相同则混合
LIGHTEN
变亮,与DARKEN相反,DARKEN和LIGHTEN生成的图像结果与Android对颜色值深浅的定义有关
MULTIPLY
正片叠底,源图像素颜色值乘以目标图像素颜色值除以255得到混合后图像像素颜色值
SCREEN
滤色,色调均和,保留两个图层中较白的部分,较暗的部分被遮盖(添加源和目标像素,然后减去源像素乘以目标。)
ADD
饱和相加,对图像饱和度进行相加,不常用
OVERLAY
叠加
现在来看代码
private void drawRoundByXfermode(Canvas canvas, Bitmap bitmap) {
int bitmapWidth = bitmap.getWidth();
int bitmapHeight = bitmap.getHeight();
int viewWidth = getWidth();
int viewHeight = getHeight();
float minScale = Math.min(viewWidth / (float) bitmapWidth, viewHeight / (float) bitmapHeight);
mMatrix.reset();
mMatrix.setScale(minScale, minScale);
//经过矩阵变换后的新的bitmap
Bitmap newBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmapWidth, bitmapHeight, mMatrix, true);
int layerId = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
canvas.drawCircle(viewWidth / 2, viewWidth / 2, Math.min(viewHeight / 2, viewHeight / 2), mPaint);
mPaint.reset();
mPaint.setAntiAlias(true);
mPaint.setColor(Color.WHITE);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(newBitmap, (viewWidth - newBitmap.getWidth()) / 2, (viewHeight - newBitmap.getHeight()) / 2, mPaint);
mPaint.setXfermode(null);
canvas.restoreToCount(layerId);
}
效果图
很明显XferMode
的方式能够留出空白,并且通过这种叠加的方式,能够实现任何形状的显示,扩展性可以说是极好的。
现在来看一下锯齿,
这是注释掉
XforMode
锯齿之后的效果
还是能够看到一些锯齿的印记
下面是
Xformode
加上去锯齿的效果
可以看到效果非常的平滑
所以关于Xfermode
的实现方式的比较
第3种方式:Canvas.clipPath()
实现
通过裁剪画布的方式实现,先将画布裁剪成圆形,在绘制bitmap
private void drawRoundByClipPath(Canvas canvas, Bitmap bitmap) {
int bitmapWidth = bitmap.getWidth();
int bitmapHeight = bitmap.getHeight();
int viewWidth = getWidth();
int viewHeight = getHeight();
float minScale = Math.min(viewWidth / (float) bitmapWidth, viewHeight / (float) bitmapHeight);
mMatrix.reset();
mMatrix.setScale(minScale, minScale);
//经过矩阵变换后的新的bitmap
Bitmap newBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmapWidth, bitmapHeight, mMatrix, true);
int layerId = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
//Path.Direction.CCW 这个参数表示path的方向,书顺时针还是逆时针,这里是逆时针
mPath.addCircle(viewWidth / 2, viewWidth / 2, Math.min(viewHeight / 2, viewHeight / 2), Path.Direction.CCW);
mPaint.reset();
mPaint.setAntiAlias(true);//对画笔设置抗锯齿
canvas.setDrawFilter(pdf);//对画布设置抗锯齿
canvas.clipPath(mPath);
//将Bitmap绘制到中心区域
canvas.drawBitmap(newBitmap, (viewWidth - newBitmap.getWidth()) / 2, (viewHeight - newBitmap.getHeight()) / 2, mPaint);
canvas.restoreToCount(layerId);
}
效果展示
通过这种方式也能实现圆形裁剪,我们可以在背后设置draw
一个circle
实现白色背景,但是这不重要。
重要的是我们代码中设置了抗锯齿,但是还是有比较明显锯齿。
也就是说,代码中设置了抗锯齿和不设置都有明显的锯齿。
所以关于Xfermode
的实现方式的比较
第4种方式:RoundedBitmapDrawable
实现(系统API)
系统为我们提供了一个类来实现圆角功能,这就不需要我们通过继承ImageView并且重新ondraw()来实现了。
private void initRoundedDrawable() {
ImageView view = findViewById(R.id.ivRoundedView);
Bitmap src = BitmapFactory.decodeResource(getResources(), R.drawable.test_view); //获取Bitmap图片
RoundedBitmapDrawable roundedBitmapDrawable = RoundedBitmapDrawableFactory.create(getResources(), src); //创建RoundedBitmapDrawable对象
roundedBitmapDrawable.setCornerRadius(500); //设置圆角Radius(根据实际需求)
roundedBitmapDrawable.setAntiAlias(true); //设置抗锯齿
view.setImageDrawable(roundedBitmapDrawable); //显示圆角
}
我们只需要将上面的代码在Activity
中设置即可
这种方式的话,实际上使用的是BitmapShader
的方式实现的,我们可以看一下他设置圆角的核心代码
public void setCornerRadius(float cornerRadius) {
if (mCornerRadius == cornerRadius) return;
mIsCircular = false;
if (isGreaterThanZero(cornerRadius)) {
mPaint.setShader(mBitmapShader); //这里设置了BitmapShader
} else {
mPaint.setShader(null);
}
mCornerRadius = cornerRadius;
invalidateSelf();
}
所以锯齿能力我们就不再分析了,参照BitmapShader
方式。
所以关于RoundedBitmapDrawable
的实现方式的比较
最后总结一下,其实每种方式都能实现圆形,但是需要我们根据自身的需求去选择。
BitmapShader |
Xfermode |
clipPath |
RoundedBitmapDrawable |
|
---|---|---|---|---|
实现方式难易 | 简单 | 简单 | 简单 | 最简单 |
空白的背景 | 不能设置 | 可以设置 | 可以设置 | 不能设置 |
抗锯齿 | 可以设置,有效 | 可以设置,有效 | 不可去除 | 可以设置,有效 |
推荐指数 | 推荐 | 推荐 | 不推荐 | 推荐 |
如果觉得有用,
点个赞吧
!!!
文章浏览阅读1.8k次。AndroidX 是 Android 开发的一个重要组成部分,它是 Android Jetpack 的一部分。AndroidX 是 Android Support Library(支持库)的继承者,提供了更加模块化、更易于维护和测试的库。它包括一系列用于 Android 应用开发的库和工具,涵盖了 UI 设计、架构、数据绑定、网络通信等多个方面。_androidx
文章浏览阅读3.1k次,点赞4次,收藏17次。摘要:在这个科技突飞猛进发展的时代,计算机网络已经家喻户晓,在日常生活中也起着不可忽视的作用,计算机的发展提高了人们的生活质量,加快了信息的传播,现如今,各个国家都比较重视计算机科学与技术的发展,使计算机科学与技术在全国综合国力竞争的作用逐渐加大。对于这种情况,加快计算机科学与技术的发展,不但有利于人们更快捷地了解如今的发展趋势和历史,且还推动计算机科学与技术的进步,方便人们的生活。本文首先阐述了..._大学计算机专业自设立以来在大时代背景下的变化
文章浏览阅读6.9k次,点赞8次,收藏22次。之前在工作中,有遇到需要程序化截取视频片段的场景,这里使用ffmpeg命令行就可以很容易实现,这里也记录下我们使用过程中遇到的坑,希望对大家也有所帮助。这里的参数-c:v copy指的是复用原始视频的编码格式,如果想切换视频编码也可以直接指定,比如(关于修改视频和音频编码的问题,后续会继续出一篇博客)。这里需要注意的是。_ffmpeg -ss 时间不准
文章浏览阅读1.1w次,点赞12次,收藏58次。源码如下<template> <div class="box"> <div> <div class="mask" v-if="showModal" @click="showModal=false"></div> <div class="pop" v-if="showModal"> <!-- 关闭 --> 内容区域 </._vue点击按钮弹出提示框
文章浏览阅读1.5k次,点赞3次,收藏2次。Ranger启动失败后,重新安装启动rangeradmin遇到MySQL异常 启动rangeradmin时报错: SQLException : SQL state: HY000 java.sql.SQLException: Operation CREATE USER failed for ‘rangeradmin’@’%’ ErrorCode: 1396 SQLException ..._ranger连接mysql8 驱动失败
文章浏览阅读1.1w次,点赞4次,收藏5次。Key length not 128/192/256 bits 一般是使用对称算法加密的时候出现的异常,它的意思是指 key的长度不是 128,192或者256 位,注意!!! 并不是不足,而是不是,这个意思指的是key的长度必须要是128,192或者256 位,知道了这个的话就很好去解决这个问题了 以AES 加密为例: aes.getSecretKey().getEncoded().length 能获取到aes 的 字节长度 (注意是字节)128 位 对应的是 16_key length not 128/192/256 bits.
文章浏览阅读152次。标签:凸包算法是计算几何中的最经典问题之一了。给定一个点集,计算其凸包。凸包是什么就不罗嗦了本文给出了《计算几何——算法与应用》中一书所列凸包算法的Python实现和Matlab实现,并给出了一个Matlab动画演示程序。啊,实现谁都会实现啦╮(╯▽╰)╭,但是演示就不一定那么好做了。算法CONVEXHULL(P)输入:平面点集P输出:由CH(P)的所有顶点沿顺时针方向组成的一个列表1.根据..._凸包算法的jpython实现
文章浏览阅读211次。DockerfileDockerfile简介Dockerfile使用流程Dockerfile基本语法认证流程认证流程认证流程Dockerfile简介dockerFile用来帮助我们构建自己的镜像Dockerfile解析过程:Dockerfile使用流程1.创建Dockerfile文件touch Dockerfile2.拉取centos镜像docker pull centos:centos7Dockerfile基本语法官方说明:https://docs.docker.com/eng_env base_dir是什么意思
文章浏览阅读6.7k次。适用系统:Linux(Redhat , CentOS,Debian,Ubuntu)1.查看数据盘在没有分区和格式化数据盘之前,使用 “df –h”命令,是无法看到数据盘的,可以使用“fdisk -l”命令查看。如下图:2.对数据盘进行分区执行“fdisk -S 56 /dev/sdb”命令,对数据盘进行分区;根据提示,依次输入“n”,“p”“1”,两次回车,“wq”,分区就开始了,很快就会完成。3..._unraid格式化硬盘命令
文章浏览阅读1k次,点赞15次,收藏17次。Vault 是一个基于身份的秘密和加密管理系统。秘密是您想要严格控制访问的任何内容,例如 API 加密密钥、密码和证书。Vault 提供由身份验证和授权方法控制的加密服务。使用 Vault 的 UI、CLI 或 HTTP API,可以安全地存储和管理、严格控制(限制)和审核对机密和其他敏感数据的访问。_vault.cf7-1it4z.workers.dev
文章浏览阅读8.9k次,点赞5次,收藏44次。1. 硬件描述单片机:STM32F407VET6 TFT-LCD控制器:RA8875 SD卡:金士顿4GB2. 第三方模块文件系统:FATFS R0.113. BMP图片基础知识BMP(全称Bitmap)是Windows操作系统中的标准图像文件格式,可以分成两类:设备相关位图(DDB)和设备无关位图(DIB),使用非常广。它采用位映射存储格式,除了图像深度可选以外,不采用其他任何压缩,因此,_stm32f103 hal fatfs bmp解码
文章浏览阅读530次,点赞7次,收藏7次。文档已验证, 非垃圾文, 欢迎各位大佬斧正._springboot3.2 jwt