Android 圆角图片RoundImageView(Canvas.drawDoubleRoundRect)

简介

实现圆角图片的方法有如下几种,其中的最第三种是参考的Android圆角图片和圆形图片实现总结java

  1. 使用Google官方提供的控件CardView卡片式布局,这个控件提供了圆角半径设置和阴影效果。不过使用须要注意系统版本。
  2. 使用Glide图片加载框架,利用其提供的RoundedCorners能够设置图片的圆角半径,代码大体以下:
val option = RequestOptions()
    		.error(R.mipmap.ic_launcher_round)
    		.transform(RoundedCorners(300))//设置圆角半径
   
        Glide.with(userInfoAvatar)
            	.applyDefaultRequestOptions(option)
            	.load(imgUrl)
            	.into(img)
复制代码
  1. 图层覆盖、以及从新绘制(BitmapShader、Xfermode、RoundedBitmapDrawable)具体细节见Android圆角图片和圆形图片实现总结文章写的很好,很详细。
  2. OutLine
  3. material包下的ShapeableImageView,这个简直不要太好用哦。能够实现圆角图片甚至是异形图片,固然也有边框绘制。

第四第五两个方法是评论区大佬给补充的知识,谢谢大佬!android

正文

强烈建议使用ShapeImageView,不只能够实现四个角的控制还能控制四条边,就能够画出任何想要的异形。具体怎么实现能够看一下个人这篇文章Android 实现布局凹陷(MaterialShapeDrawable)canvas

OutLine

ShapeableImageView

只须要给这个View设置shapeAppearance属性就能够实现对角形状的控制,以及边框,先看一下效果:数组

左边是圆角,右边是“切角”markdown

<!-- layout.xml 就是在布局中定义-->
<com.google.android.material.imageview.ShapeableImageView android:layout_width="200dp" android:layout_height="wrap_content" android:scaleType="fitXY" android:strokeWidth="10dp" android:strokeColor="@color/purple" android:src="@drawable/ic_launcher_background" app:shapeAppearance="@style/RoundAndCutImageStyle" />
复制代码
<!-- style.xml 能够单独给每一个角设置属性-->
    <style name="RoundAndCutImageStyle"> <item name="cornerFamilyTopLeft">rounded</item> <item name="cornerFamilyBottomLeft">rounded</item> <item name="cornerFamilyTopRight">cut</item> <item name="cornerFamilyBottomRight">cut</item> <item name="cornerSize">50%</item> <item name="cornerSizeBottomLeft">20dp</item> <item name="cornerSizeTopLeft">20dp</item> </style>
复制代码

这里须要解释如下几个属性:app

  • app:sharedAppearance 给view设置style
  • app:sharedAppearanceOverlay 覆盖view的style;用途:在系统主题下能够设置通用的属性,这个时候咱们须要本身定义一个特殊的属性,就能够经过app:sharedAppearanceOverlay来覆盖,通用属性分别有三个对应大中小控件的形状控制,三个属性以下:
<!-- 系统主题,使用这个主题的activity包含有material design的控件就会被如下属性修饰形状 -->
    <style name="MyAppTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar"> <item name="shapeAppearanceLargeComponent">@style/ShapeAppearance.MyApp.LargeComponent</item> <item name="shapeAppearanceMediumComponent">@style/ShapeAppearance.MyApp.MediumComponent</item> <item name="shapeAppearanceSmallComponent">@style/ShapeAppearance.MyApp.SmallComponent</item> </style>
    
    <style name="ShapeAppearance.MyApp.MediumComponent" parent="ShapeAppearance.MaterialComponents.MediumComponent"> <item name="cornerFamily">cut</item> <item name="cornerSize">8dp</item> </style>
复制代码
  • cornerFamily 这个属性有两个值
    • rounded 圆角
    • cut 切角
  • cornerSize 控制角的大小,能够为如下这些值
    • 百分比 这个不用解释吧,就是长宽的一半,改变view大小会使得半径随之改变
    • dimension 就是dp、px、sp等等属性;注意在view大小可能发生改变时尽可能不要使用百分比,这样会让你的view形状跟着改变

这两个属性能够单独给每一个角设置,cornerFamilyTopLeft、cornerSizeTopLet这样的。框架

  • strokeWidth 边框的宽度
  • strokeColor 边框颜色

经过BitmapShader从新绘制

我这里我这里一样是使用的BitmapShader来进行从新绘制,大体思路是和那篇文章写得同样,可是使用了不一样的绘制方法,而且也实现了每一个角单独绘制不过相对上面的那篇文章会简单一点。开始以前建议先阅读这篇文章哦Android圆角图片和圆形图片实现总结👍ide

须要用到的各种方法

官网的中文翻译 函数

  • BitmapShader 构造方法
public BitmapShader(Bitmap bitmap,TileMode tileX, TileMode tileY) 复制代码

paint设置shader以后,当发生绘制时,就会把shader中的bitmap的内容绘制到指定的位置,大体这么理解,有错误欢迎大佬指出,谢谢!oop

  • drawable转bitmap 使用BitmapShader固然要先把drawable转成bitmap
private fun drawableToBitmap(drawable: Drawable): Bitmap {
        val bitmap = Bitmap.createBitmap(getTrulyWidth(), getTrulyHeight(), Bitmap.Config.ARGB_8888)
        val canvas = Canvas(bitmap)//将画布目标设置为bitmap,这样画布的内容就会直接画到bitmap上
        //这里传入的宽高就是减去padding的值
        drawable.setBounds(0, 0, getTrulyWidth(), getTrulyHeight())
        drawable.draw(canvas)
        return bitmap
    }
    
    //其实在kotlin中存在一个扩展函数,就是使用kotlin的话直接drawable.toBitmap(...)
    fun Drawable.toBitmap( @Px width: Int = intrinsicWidth, @Px height: Int = intrinsicHeight, config: Config? = null): Bitmap
复制代码
  • Canvas.drawDoubleRoundRect,我就是经过这个方法实现了四个角的不一样半径绘制以及边框绘制。
    • 方法入口,详细注释都在里面
    /** * 这个方法经过内外边框来完成绘制,且内外边框的四个角的圆角半径均可控 * @param outer 外边框的范围 * @param inner 内边框的范围 * @param innerRadii、outerRadii * 这两个float数组分别保存了内外边框的四个角的圆角半径 * 每一个数组须要传入8个float,每两个为一组 rx,ry即圆角半径在x,y方向的值 * @param paint 这里我传入的paint里面保存着bitmapShader * 此外须要注意设置Style: * FILL将会绘制到两个矩形(RectF)中间的内容 * STROKE,就是会把两个边框给绘制出来。 */
        public void drawDoubleRoundRect(@NonNull RectF outer, @NonNull float[] outerRadii, @NonNull RectF inner, @NonNull float[] innerRadii, @NonNull Paint paint) 复制代码
    这个方法的灵活性很高,由于内外边框均可控。

注意 内边框不能在外边框的外面,边界超过外边框会致使不绘制。边界能够相等

实践

内容绘制

这里为了简便没有给画笔设置shader,就是单纯的黑色。

  1. 给外边框设置圆角,内边框不设置

  1. 外圆内方

  1. 外圆内圆

  1. 外边框三圆角,内边框很小

(这个能看见个人内边框吗,内边框位于view的正中间,宽高都为0,看不见吧,这样就已经实现了对四个圆角的控制。)

  1. 在4的基础上添加了shader,能够看到当内容超过原始边界时,不断的赋值边缘的颜色,说明使用了TileMode.CLAMP

这里给出最后一个的绘制代码

canvas.drawDoubleRoundRect(
            outRect,
            floatArrayOf(
            topLeftRadius,
            topLeftRadius,//左上角
            topRightRadius,
            topRightRadius,//右上角
            bottomRightRadius,
            bottomRightRadius,//右下角
            0f,
            0f//左下角
            ),
            //内边框,恰好是位于中间的0X0的矩形,其实只须要保证内边框大小为0就好了
            RectF(outWidth/2f,outHeight/2f,outWidth/2f,outHeight/2f),
            //内边框四角半径
            floatArrayOf(
                0f,//rx
                0f,//ry
                0f,
                0f,
                0f,
                0f,
                0f,
                0f
            ),
             bitmapPaint//style = Paint.Style.FILL 绘制两个矩形之间的内容;
             // bitmap.shader = BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
        )
复制代码

拓展

探索

已知画笔设置Paint.Style.FILL时,将会绘制两个矩形之间的内容。那么两个边框有部份内容不重合时会发生什么呢?(虽然内边框必须小于等于外边框,可是当外边框设置了圆角半径时,就可能出现两者存在部份内容不能重合。)

先看一下绘制的边框:

Paint.Style.STROKE

Paint.Style.FILL

结论

Canvas.drawDoubleRoundRect 这个方法绘制两个边框的非交集部分的内容,正好对应PorterDuff.Mode.Xor

详细内容能够查看PorterDuff.Mode

结语

内容不免会有错误,很是欢迎你们指出,对于你们的建议和错误指正,我会及时修改。谢谢!

相关文章
相关标签/搜索