圆角效果实现方式汇总

1,531 阅读3分钟

我们开发中会经常遇到实现圆角的需求,本文汇总不同场景下常见的一些方案,欢迎补充。

1.使用shape标签

xml里面利用shape元素实现圆角

<shape xmlns:android="http://schemas.android.com/apk/res/android"
		android:shape="rectangle">

		<corners android:radius="5dp"/>   <!-- 设置圆角弧度 -->
		//<corners android:topRightRadius="8dp" android:topLeftRadius="8dp"/> //各个角不同弧度
		<solid android:color="@color/bg_color"/>   <!-- 设置背景颜色 -->
		<stroke android:color="@color/divider_color" android:width="@dimen/y2"/> <!-- 设置边框颜色以及宽度 -->
		
	</shape>

优点:

:方便简洁直观

缺点:

:每个xml只有一种样式

:静态图片作为背景适用,如果动态切割就不合适 

2.使用 CardView 、GradientDrawable等现有的图片控件

GradientDrawable方式

	private GradientDrawable gradientDrawable = new GradientDrawable();
		gradientDrawable.setShape(GradientDrawable.RECTANGLE);
		float[] radii = new float[]{//支持各个角设置不同的弧度
                DisplayUtils.dp2px(this, 10F), DisplayUtils.dp2px(this, 10F),
                0F, 0F,
                0F, 0F,
                DisplayUtils.dp2px(this, 10F), DisplayUtils.dp2px(this, 10F)
        };
        gradientDrawable.setCornerRadii(radii);
	gradientDrawable.setCornerRadius(radius);
	gradientDrawable.setColor(normalColor);
	setBackground(gradientDrawable);

CardView方式

<androidx.cardview.widget.CardView
            android:id="@+id/icon"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="@dimen/card_store_normal_margin"
            android:layout_marginBottom="@dimen/card_store_normal_margin"
            app:cardCornerRadius="@dimen/card_store_icon_radius"
            app:cardElevation="0dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:srcCompat="@tools:sample/avatars">

            <ImageView
                android:id="@+id/app_icon"
                android:layout_width="@dimen/card_store_icon_height"
                android:layout_height="@dimen/card_store_icon_height"
                android:scaleType="centerCrop"
                tools:srcCompat="@tools:sample/avatars" />

 </androidx.cardview.widget.CardView>

优点:

:可以动态设置角弧度

:同时支持xml或者代码方式定义

缺点:

:灵活度依然略差

3.使用ViewOutlineProvider裁剪view

ViewOutlineProvider是Android 5.x引入的新特性,用于实现View的阴影和轮廓

itemView.outlineProvider = object : ViewOutlineProvider() {
		 override fun getOutline(view: View, outline: Outline) {
			outline.setRoundRect(0, 0, view.width, view.height, 5.dp.toFloat())
		 }
	}
// 打开开关
itemView.clipToOutline = true

outline 还可以画其他的一些内容:

outline.setRect(xxx)// 画矩形

outline.setRoundRect(xxx)// 画圆角矩形

outline.setOval(xxx) // 画椭圆

优点:

:比使用了设置drawable的 xml 少了一层过度绘制。因为省去了设置的 background

:Outline方式是直接作用于RenderNode,然后再渲染流程中进行过滤,不是对Canvas做切割,因此效率相对于其他几种方式可以更高?

缺点:

:只能四个角同时配置不支持每个角单独配置

4.Canvas切割

可以是通过canvas.clipPath切割后绘制目标View,也可是先绘制目标View后使用Xfermode设置混合模式 绘制很自由,可以按照自己的需求切出不同形状,比如可以切出四个圆角不等的效果

clipPath方式:

整个原理就是用Path划出一个圆角矩形区域,调用super.onDraw(canvas)就可以让Drawable 落在那个区域。

@Override

protected void onDraw(Canvas canvas) {

	//设置外框的矩形区域,不可再init()初始化,构造器中width和height还未确定,可在onMesure()中获取并设置

	mRectF = new RectF(0,0, getWidth(),getHeight());

	//path划出一个圆角矩形,容纳图片,图片矩形区域设置比红色外框小,否则会覆盖住外框,随意控制

	mPath.addRoundRect(new RectF(10, 10, mRectF.right-10,mRectF.bottom-10), 50, 50, Path.Direction.CW);

	canvas.drawRoundRect(mRectF, 50, 50, mPaint); //画出红色外框圆角矩形

	canvas.clipPath(mPath);//将canvas裁剪到path设定的区域,往后的绘制都只能在此区域中,

	//这一句应该放在canvas.clipPath(path)之后,canvas.clipPath(path)只对裁剪之后的绘制起作用,

	// 这个方法在ImageView中会画出xml设置的Drawable,落在刚才设置的path中

	super.onDraw(canvas);

}

Xfermode方式:

利用Xfermode的图片渲染合成原理,使用Path构造我们需要的效果范围,在onDraw的时候,将范围外的所有部分过滤掉

    @Override
    protected void dispatchDraw(Canvas canvas) {
        if (clipRect == null || getMeasuredWidth() == 0) {
            clipRect = new RectF(padding, 0, getMeasuredWidth() - padding, getMeasuredHeight());
 
            maskBitmap = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_4444);
            Canvas c = new Canvas(maskBitmap);
            //在蒙版上画需要覆盖的图形
            c.drawRoundRect(clipRect, cornerSize, cornerSize, clipPaint);
        }
 
        //保存还没有绘制之前的图层
        int layerId = canvas.saveLayer(clipRect, clipPaint, Canvas.ALL_SAVE_FLAG);
        //绘制底部图层
        super.dispatchDraw(canvas);
 
        //设置混合模式,实现view的四个圆角
        clipPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
        canvas.drawBitmap(maskBitmap, 0, 0, clipPaint);
        clipPaint.setXfermode(null);
        //恢复之前的图层,要不然背景是黑色的
        canvas.restoreToCount(layerId);
    }

5.其他第三方库

Glide

	Glide.with(this).load("https://placehoder.com/100")
                .apply(RequestOptions.bitmapTransform(RoundedCorners(20)))
                .into(iv)

fresco

...