失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > android 清空canvas部分内容_Android自定义View实现圆形头像效果

android 清空canvas部分内容_Android自定义View实现圆形头像效果

时间:2023-11-08 04:19:44

相关推荐

android 清空canvas部分内容_Android自定义View实现圆形头像效果

在我们的APP中通常会遇到,展示圆形头像的需求,一般通过Glide就能实现,但是让我们做一个圆形头像,如果让我们自定义实现这种效果,该怎样做呢?

好,接下来本文通过三种方式来实现这种效果!

注意:这是一个练手的Demo

1.通过本文可以学到的知识点

canvas.clipPath API的使用Xfermode的使用Paint的Xfermode和ShaderAPIMatrix的平移和Canvans的平移(源码中,为了在一个View同时展示三种效果,所以对Canvas坐标进行了平移)总结三种实现方式的优缺点

2.通过自定义View制作圆形头像

通过三种方式来实现这种效果,是哪三种方式呢?

通过canvas.clipPath()通过paint.xfermode= porterDuffXfermode通过paint.shader= bitmapShader

3.第一种实现方式

利用 canvas的 clip方法

private val AVATAR_SIZE = 240.dp //图片的大小private val RADIUS = AVATAR_SIZE / 2 //裁剪圆形的半径class CircleAvatarView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : View(context, attrs, defStyleAttr) {private val paint = Paint()private var avatar = getAvatar(R.drawable.my_avatar, AVATAR_SIZE.toInt())//图片的bitmapprivate val circlePath = Path()//圆形的路径override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {/*** 在圆心 x:View宽度的一半 y:View高度的一半 半径为图片尺寸的一半 的位置上画圆*/circlePath.addCircle(width / 2f, height / 2f, RADIUS, Path.Direction.CW)}override fun onDraw(canvas: Canvas) {super.onDraw(canvas)//ktx的扩展方法,会自动保存恢复Canvascanvas.withSave {//重置画笔paint.reset()//利用Canvas来裁切去要画的范围,也就是那个圆形canvas.clipPath(circlePath)//在裁切后画上声明的Bitmapcanvas.drawBitmap(avatar, width / 2 - RADIUS, height / 2 - RADIUS, paint)}}}

解释一下代码,当然看代码的注释也是一样的

定义包级别的图片的宽度为240dp和圆形的半径获取一个要展示的图片的Bitmap声明一个要裁剪的圆形的PathonSizeChanged方法中添加一个圆形利用Canvas来裁切去要画的范围,也就是那个圆形在裁切后画上声明的Bitmap

以上就是绘制圆形头像的第一种方法,主要使用的是canvas.clipPath(circlePath)方法,注意Canvas的保存和恢复

4.第二种实现方式

通过paint.xfermode= porterDuffXfermode

PorterDuff.Mode

private val AVATAR_SIZE = 240.dp //图片的大小private val RADIUS = AVATAR_SIZE / 2 //裁剪圆形的半径class CircleAvatarView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : View(context, attrs, defStyleAttr) {private val paint = Paint()private var avatar = getAvatar(R.drawable.my_avatar, AVATAR_SIZE.toInt())//图片的bitmapprivate val circlePath = Path()//圆形的路径private val bounds = RectF()//离屏缓冲的 boundsprivate val porterDuffXfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {/*** 在圆心 x:View宽度的一半 y:View高度的一半 半径为图片尺寸的一半 的位置上画圆*/circlePath.addCircle(width / 2f, height / 2f, RADIUS, Path.Direction.CW)/*** 设置离屏缓冲的 bounds最好不要太大,影响性能*/bounds.set(width / 2f - RADIUS, height / 2f - RADIUS,width / 2f + RADIUS, height / 2f + RADIUS)}override fun onDraw(canvas: Canvas) {super.onDraw(canvas) //开启离屏缓冲val count = canvas.saveLayer(bounds, null)paint.reset()canvas.drawPath(circlePath, paint)//设置paint的 xfermode 为PorterDuff.Mode.SRC_INpaint.xfermode = porterDuffXfermode//以当前的Paint来画Bitmapcanvas.drawBitmap(avatar, width / 2 - RADIUS, height / 2 - RADIUS, paint)//清空paint 的 xfermodepaint.xfermode = null//把离屏缓冲的内容,绘制到View上去canvas.restoreToCount(count)}}

解释一下代码,当然看代码的注释也是一样的

在onSizeChanged方法中设置离屏缓冲的范围,是圆形头像的外切矩形的范围onDraw方法中开启离屏缓冲Canvas先画了一个圆形(相当于PorterDuff.Mode中的Destination image)设置paint的 xfermode 为PorterDuff.Mode.SRC_IN以当前的Paint来画Bitmap(注意此时相当于PorterDuff.Mode中的Source image),因为我们选择的模式为PorterDuff.Mode.SRC_IN所以就画出我们想要的效果把离屏缓冲的内容,绘制到View上去

注意:一定要开启离屏缓冲,不然结果可能不是你所预期的,离屏缓冲相当于拿出一块透明的View来绘制,Canvas要绘制的图形

5.第三种实现方式

通过paint.shader= bitmapShader

private val AVATAR_SIZE = 240.dp //图片的大小private val RADIUS = AVATAR_SIZE / 2 //裁剪圆形的半径class CircleAvatarView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : View(context, attrs, defStyleAttr) {private val paint = Paint()private var avatar = getAvatar(R.drawable.my_avatar, AVATAR_SIZE.toInt())//图片的bitmapprivate val circlePath = Path()//圆形的路径private val bitmapShader = BitmapShader(avatar, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT)override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {/*** 在圆心 x:View宽度的一半 y:View高度的一半 半径为图片尺寸的一半 的位置上画圆*/circlePath.addCircle(width / 2f, height / 2f, RADIUS, Path.Direction.CW)}override fun onDraw(canvas: Canvas) {super.onDraw(canvas)paint.reset()//给paint设置Shaderpaint.shader = bitmapShader//以Shader的形式来画圆形canvas.drawPath(circlePath, paint)}}

解释一下代码,当然看代码的注释也是一样的

这种方式就比较简单了

声明一个BitmapShader,把头像的bitmap填入BitmapShader的构造中,并填入参数Shader.TileMode的值,这个具体看这篇文章对Paint做了详细解释在onDraw方法中给paint设置Shader以Shader的形式来画圆形,结果就是一个圆角头像了

6.总结

总结一下三种方式的优缺点

1.canvas.clipPath():

利用 canvas的 clip方法没有抗锯齿效果,会有毛边,因为是精确的切像素点

2.paint.xfermode= porterDuffXfermode

利用 paint的 xfermode 方法有抗锯齿效果,没有毛边,效果好,做了抗锯齿的处理,填充和周边类似的半透明色等

3.paint.shader= bitmapShader

利用 设置 paint 的 shader 方法有抗锯齿效果,没有毛边,效果好但是图片结果会受到Shader.TileMode影响,可能结果不是你所预期的

综上总结:最好使用paint.xfermode= porterDuffXfermode这种方式,因为我们需要的是一个显示完美的头像

此外还要注意使用离屏缓冲

7.源码地址

CircleAvatarView.kt

8.原文地址

注意:我的源码是把三种方式统一画在了一个View中,并通过Matrix的平移或者Canvans的平移来实现向下排列的效果,顺便先练了Matrix的平移和Canvans的平移

Android自定义View实现圆形头像效果

9.参考文章

hencoder

PorterDuff.Mode

推荐一下我开源的项目 WanAndroid 客户端

WanAndroidJetpack 架构图

一个纯 Android 学习项目,WanAndroid 客户端。项目采用MVVM架构,用Kotlin语音编写。Android Jetpack 的大量使用包括但不限于LifecycleLiveDataViewModelDatabindingRoomConstraintLayout等,未来可能会更多。采用RetrofitKotlin-Coroutine协程进行网络交互。加载图片Glide主流加载图片框架。数据存储主要用到了Room和腾讯的MMKV
Kotlin + MVVM + Jetpack + Retrofit + Glide 的综合运用,是学习 MMVM 架构的不错的项目。

此项目本身也是一个专门学习 Android 相关知识的 APP,欢迎下载体验!

源码地址(附带下载链接)

WanAndroidJetpack

APP 整体概览

喜欢的点个 Stars,有问题的请提 Issues。

如果觉得《android 清空canvas部分内容_Android自定义View实现圆形头像效果》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。