android_基础_圆形imageView的实现_android 圆形imageview-程序员宅基地

技术标签: android  基础  

四种自定义圆形ImageView的方法


  1. BitmapShader: 使用着色器
  2. Xfermode:使用图层叠加
  3. ClipPath:通过对画布裁剪的方式
  4. 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行中BitmapTileMode,其实就是说如果Bitmap的内容没有铺满View时,如何填充形状里面剩余的部分,我们来看一下

  • ClAMPBitmap以其内容的最后一行像素填充剩余的高的空白或者最后一列像素填充剩余宽空白
  • MIRRORBitmap以其内容以镜像的方式填充剩余空白
  • REPEATBitmap以其内容以重复的方式填充剩余空白

在这里插入图片描述


现在来看一下抗锯齿的能力,上面的图都是设置了抗锯齿的,如果把尺寸设置小一点:
在这里插入图片描述

表现还不错,为了进行明显的对比,我们去掉抗锯齿,对比下

// 注释掉抗锯齿 // 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
实现方式难易 简单 简单 简单 最简单
空白的背景 不能设置 可以设置 可以设置 不能设置
抗锯齿 可以设置,有效 可以设置,有效 不可去除 可以设置,有效
推荐指数 推荐 推荐 不推荐 推荐

如果觉得有用,点个赞吧!!!

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_42420293/article/details/119345566

智能推荐

AndroidX是什么-程序员宅基地

文章浏览阅读1.8k次。AndroidX 是 Android 开发的一个重要组成部分,它是 Android Jetpack 的一部分。AndroidX 是 Android Support Library(支持库)的继承者,提供了更加模块化、更易于维护和测试的库。它包括一系列用于 Android 应用开发的库和工具,涵盖了 UI 设计、架构、数据绑定、网络通信等多个方面。_androidx

计算机科学技术专业发展分析,计算机科学与技术发展现况分析-程序员宅基地

文章浏览阅读3.1k次,点赞4次,收藏17次。摘要:在这个科技突飞猛进发展的时代,计算机网络已经家喻户晓,在日常生活中也起着不可忽视的作用,计算机的发展提高了人们的生活质量,加快了信息的传播,现如今,各个国家都比较重视计算机科学与技术的发展,使计算机科学与技术在全国综合国力竞争的作用逐渐加大。对于这种情况,加快计算机科学与技术的发展,不但有利于人们更快捷地了解如今的发展趋势和历史,且还推动计算机科学与技术的进步,方便人们的生活。本文首先阐述了..._大学计算机专业自设立以来在大时代背景下的变化

如何用ffmpeg截取视频片段&截取时间不准确的坑_ffmpeg -ss 时间不准-程序员宅基地

文章浏览阅读6.9k次,点赞8次,收藏22次。之前在工作中,有遇到需要程序化截取视频片段的场景,这里使用ffmpeg命令行就可以很容易实现,这里也记录下我们使用过程中遇到的坑,希望对大家也有所帮助。这里的参数-c:v copy指的是复用原始视频的编码格式,如果想切换视频编码也可以直接指定,比如(关于修改视频和音频编码的问题,后续会继续出一篇博客)。这里需要注意的是。_ffmpeg -ss 时间不准

vue点击按钮显示弹窗写法_vue点击按钮弹出提示框-程序员宅基地

文章浏览阅读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点击按钮弹出提示框

Ranger启动失败后,重新安装遇到MySQL异常_ranger连接mysql8 驱动失败-程序员宅基地

文章浏览阅读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 驱动失败

记录异常InvalidKeyOrParametersException: Key length not 128/192/256 bits_key length not 128/192/256 bits.-程序员宅基地

文章浏览阅读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.

随便推点

python 几何计算_计算几何-凸包算法 Python实现与Matlab动画演示-程序员宅基地

文章浏览阅读152次。标签:凸包算法是计算几何中的最经典问题之一了。给定一个点集,计算其凸包。凸包是什么就不罗嗦了本文给出了《计算几何——算法与应用》中一书所列凸包算法的Python实现和Matlab实现,并给出了一个Matlab动画演示程序。啊,实现谁都会实现啦╮(╯▽╰)╭,但是演示就不一定那么好做了。算法CONVEXHULL(P)输入:平面点集P输出:由CH(P)的所有顶点沿顺时针方向组成的一个列表1.根据..._凸包算法的jpython实现

Dockerfile_env base_dir是什么意思-程序员宅基地

文章浏览阅读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是什么意思

挂载的硬盘 unraid无法格式化_Linux 系统挂载数据盘-程序员宅基地

文章浏览阅读6.7k次。适用系统:Linux(Redhat , CentOS,Debian,Ubuntu)1.查看数据盘在没有分区和格式化数据盘之前,使用 “df –h”命令,是无法看到数据盘的,可以使用“fdisk -l”命令查看。如下图:2.对数据盘进行分区执行“fdisk -S 56 /dev/sdb”命令,对数据盘进行分区;根据提示,依次输入“n”,“p”“1”,两次回车,“wq”,分区就开始了,很快就会完成。3..._unraid格式化硬盘命令

Vault实战(一)-Vault介绍_vault.cf7-1it4z.workers.dev-程序员宅基地

文章浏览阅读1k次,点赞15次,收藏17次。Vault 是一个基于身份的秘密和加密管理系统。秘密是您想要严格控制访问的任何内容,例如 API 加密密钥、密码和证书。Vault 提供由身份验证和授权方法控制的加密服务。使用 Vault 的 UI、CLI 或 HTTP API,可以安全地存储和管理、严格控制(限制)和审核对机密和其他敏感数据的访问。_vault.cf7-1it4z.workers.dev

基于STM32的BMP图片解码_stm32f103 hal fatfs bmp解码-程序员宅基地

文章浏览阅读8.9k次,点赞5次,收藏44次。1. 硬件描述单片机:STM32F407VET6 TFT-LCD控制器:RA8875 SD卡:金士顿4GB2. 第三方模块文件系统:FATFS R0.113. BMP图片基础知识BMP(全称Bitmap)是Windows操作系统中的标准图像文件格式,可以分成两类:设备相关位图(DDB)和设备无关位图(DIB),使用非常广。它采用位映射存储格式,除了图像深度可选以外,不采用其他任何压缩,因此,_stm32f103 hal fatfs bmp解码

SpringBoot 3.2.1 + SpringSecurity 3.2.1 + JWT 实现登录鉴权_springboot3.2 jwt-程序员宅基地

文章浏览阅读530次,点赞7次,收藏7次。文档已验证, 非垃圾文, 欢迎各位大佬斧正._springboot3.2 jwt

推荐文章

热门文章

相关标签