一.遇到的问题

    最近在开发中会遇到Android布局切圆角的需求,大多数是对一个layout布局切下圆角。这里和图片切圆角有一些雷同的地方,可以相互借鉴,但是也不全一样。图片切圆角的一些总结和实践准备下次有空再写出来。

    假设我们要对一种LinearLayout布局切圆角,不知道你能想出来哪些办法。我这里先提供下我的思路,亲自实践过的主要包括下面五种:

   1.利用xml背景文件配置shape属性实现切圆角

   2.利用GradientDrawable实现切圆角

   3.利用clipPath实现切圆角

   4.利用CardView实现切圆角

    5.利用ViewOutlineProvider实现切圆角

下面分别对这几种方式进行实践与优缺点分析,原理讲解较少,感兴趣可以自己再深入了解下。

 

二.利用xml背景文件配置shape属性实现

2.1方案实现

    这种方式相比大家都比较熟悉了,就是在drawable文件夹下新建一个App Action XML File,然后作为background属性添加到Layout XML File中。

    如果要生成四个角都为10dp圆角的形状我们可以在drawable文件夹下新建如下shape_10dp_corners.xml文件

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <corners android:radius="10dp" />
    <solid android:color="@color/colorAccent" />
</shape>

    如果仅要生成上方两个10dp圆角的形状我们可以在drawable文件夹下新建如下shape_10dp_top_corners.xml文件

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="@color/colorPrimary"/>
    <corners
        android:topLeftRadius="10dp"
        android:topRightRadius="10dp" />
</shape>

    如果仅要生成上方两个10dp圆角的形状我们可以在drawable文件夹下新建如下shape_10dp_bottom_right_corners.xml文件

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <corners android:bottomRightRadius="10dp"/>
    <solid android:color="@color/colorAccent"/>
</shape>

    然后在XmlShapeActivity的布局XML文件中为对应的三个LinearLayout分别设置background属性如下

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".xmlshape.XmlShapeActivity">

    <LinearLayout
        android:id="@+id/lly1"
        android:layout_width="0dp"
        android:layout_height="100dp"
        android:layout_marginStart="16dp"
        android:layout_marginLeft="16dp"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="16dp"
        android:layout_marginRight="16dp"
        android:background="@drawable/shape_10dp_corners"
        android:orientation="vertical"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="四个圆角10dp"
            android:textSize="24sp"
            android:textColor="#FFFFFF" />
    </LinearLayout>

    <LinearLayout
        android:id="@+id/lly2"
        android:layout_width="0dp"
        android:layout_height="100dp"
        android:layout_marginStart="16dp"
        android:layout_marginLeft="16dp"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="16dp"
        android:layout_marginRight="16dp"
        android:background="@drawable/shape_10dp_top_corners"
        android:orientation="vertical"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/lly1" >
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="上方圆角10dp"
            android:textSize="24sp"
            android:textColor="#FFFFFF"/>
    </LinearLayout>

    <LinearLayout
        android:id="@+id/lly3"
        android:layout_width="0dp"
        android:layout_height="100dp"
        android:layout_marginStart="16dp"
        android:layout_marginLeft="16dp"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="16dp"
        android:layout_marginRight="16dp"
        android:background="@drawable/shape_10dp_bottom_right_corners"
        android:orientation="vertical"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/lly2" >
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="右下圆角10dp"
            android:textSize="24sp"
            android:textColor="#FFFFFF"/>
    </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

    然后勉为其难地贴一下打酱油的XmlShapeActivity代码

package com.openld.roundcornerdemmo.xmlshape;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;

import com.openld.roundcornerdemmo.R;

public class XmlShapeActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_xml_shape);
    }
}

2.1运行效果

android 圆角 button 动态干煸颜色 android圆角_圆角

2.3优缺点

优点:

1.最简单

2.四个圆角可以单独定制,也可以统一处理

3.还可以一并设置背景颜色等属性

4.圆角无锯齿

 

缺点:

1.圆角属性是提前确定的并写在客户端本地的drawable中,如果是动态下发再去配置的话这种方式无法支持。

2.如果子View长宽都match_parent并且设置一个有色background属性的话,圆角就消失了。

 

三.利用GradientDrawable实现切圆角

3.1 方案实现

    有没有想过动态地在Java代码中为layout设置圆角,那就想到了GradientDrawable。其实是和第一种XML方式呼应的,只不过没有办法让你你动态去配置XML,那就用GradientDrawable吧,XML shape中能设置的属性这里也能哦!

    这里先贴一下GradientDrawableActivity的布局XML

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".gradientdrawable.GradientDrawableActivity">

    <LinearLayout
        android:id="@+id/lly1"
        android:layout_width="0dp"
        android:layout_height="100dp"
        android:layout_marginStart="16dp"
        android:layout_marginLeft="16dp"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="16dp"
        android:layout_marginRight="16dp"
        android:orientation="vertical"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="四个圆角10dp"
            android:textSize="24sp" />
    </LinearLayout>

    <LinearLayout
        android:id="@+id/lly2"
        android:layout_width="0dp"
        android:layout_height="100dp"
        android:layout_marginStart="16dp"
        android:layout_marginLeft="16dp"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="16dp"
        android:layout_marginRight="16dp"
        android:orientation="vertical"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/lly1">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="左边两个10dp圆角"
            android:textSize="24sp" />
    </LinearLayout>

    <LinearLayout
        android:id="@+id/lly3"
        android:layout_width="0dp"
        android:layout_height="100dp"
        android:layout_marginStart="16dp"
        android:layout_marginLeft="16dp"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="16dp"
        android:layout_marginRight="16dp"
        android:orientation="vertical"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/lly2">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="左上角10dp圆角"
            android:textSize="24sp" />
    </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

    再贴一下GradientDrawableActivity的代码

package com.openld.roundcornerdemmo.gradientdrawable;

import android.graphics.drawable.GradientDrawable;
import android.os.Bundle;
import android.widget.LinearLayout;

import androidx.appcompat.app.AppCompatActivity;

import com.openld.roundcornerdemmo.R;
import com.openld.roundcornerdemmo.utils.DisplayUtils;

public class GradientDrawableActivity extends AppCompatActivity {
    private LinearLayout mLly1;

    private LinearLayout mLly2;

    private LinearLayout mLly3;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_gradient_drawable);

        initWidgets();
    }

    private void initWidgets() {
        mLly1 = findViewById(R.id.lly1);
        GradientDrawable gradientDrawable1 = new GradientDrawable();
        gradientDrawable1.setShape(GradientDrawable.RECTANGLE);
        gradientDrawable1.setCornerRadius(DisplayUtils.dp2px(this, 10F));
        gradientDrawable1.setColor(getResources().getColor(R.color.colorPrimary));
        mLly1.setBackground(gradientDrawable1);

        mLly2 = findViewById(R.id.lly2);
        GradientDrawable gradientDrawable2 = new GradientDrawable();
        gradientDrawable2.setShape(GradientDrawable.RECTANGLE);
        gradientDrawable2.setColor(getResources().getColor(R.color.colorAccent));
        float[] radii = new float[]{
                DisplayUtils.dp2px(this, 10F), DisplayUtils.dp2px(this, 10F),
                0F, 0F,
                0F, 0F,
                DisplayUtils.dp2px(this, 10F), DisplayUtils.dp2px(this, 10F)
        };
        gradientDrawable2.setCornerRadii(radii);
        mLly2.setBackground(gradientDrawable2);

        mLly3 = findViewById(R.id.lly3);
        GradientDrawable gradientDrawable3 = new GradientDrawable();
        gradientDrawable3.setShape(GradientDrawable.RECTANGLE);
        gradientDrawable3.setColor(getResources().getColor(R.color.colorPrimary));
        float[] radii1 = new float[]{
                DisplayUtils.dp2px(this, 10F), DisplayUtils.dp2px(this, 10F),
                0F, 0F,
                0F, 0F,
                0F, 0F
        };
        gradientDrawable3.setCornerRadii(radii1);
        mLly3.setBackground(gradientDrawable3);
    }
}

    核心代码都在initWidgets()方法中,里面实例化一个GradientDrawable对象,设置一下对应的属性。setCornerRadii()或者se'tCornerRadius()方法即为设置圆角,具体可以看下API,这里不再赘述。

 

3.2 运行效果

android 圆角 button 动态干煸颜色 android圆角_安卓_02

 

3.3 优缺点

优点:

1.支持动态配置啊

2.四个圆角可以单独定制,也可以统一处理

3.还可以一并设置背景颜色等属性

4.圆角无锯齿

 

缺点

1.如果子View长宽都match_parent并且设置一个有色background属性的话,圆角就消失了。

 

四. 利用clipPath实现切圆角

4.1 方案实现

    继承现有的布局,在draw()方法中使用clipPath()方法切割你想要的形状,只要切割出一个圆角矩形即可。

比如新建一个继承LinearLayout的CornersLinearLayout

package com.openld.roundcornerdemmo.clippath;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.widget.LinearLayout;

import androidx.annotation.Nullable;

import com.openld.roundcornerdemmo.R;

/**
 * author: lllddd
 * created on: 2020/6/9 10:55
 * description:
 */
public class CornersLinearLayout extends LinearLayout {
    private Context mContext;

    private float mCorners;
    private float mLeftTopCorner;
    private float mRightTopCorner;
    private float mLeftBottomCorner;
    private float mRightBottomCorner;

    private int mWidth;
    private int mHeight;

    public CornersLinearLayout(Context context) {
        this(context, null);
    }

    public CornersLinearLayout(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CornersLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        mContext = context;

        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CornersLinearLayout);
        mCorners = typedArray.getDimension(R.styleable.CornersLinearLayout_corner, 0F);
        mLeftTopCorner = typedArray.getDimension(R.styleable.CornersLinearLayout_leftTopCorner, 0F);
        mRightTopCorner = typedArray.getDimension(R.styleable.CornersLinearLayout_rightTopCorner, 0F);
        mRightBottomCorner = typedArray.getDimension(R.styleable.CornersLinearLayout_rightBottomCorner, 0F);
        mLeftBottomCorner = typedArray.getDimension(R.styleable.CornersLinearLayout_leftBottomCorner, 0F);
        typedArray.recycle();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mWidth = getMeasuredWidth();
        mHeight = getMeasuredHeight();
        setMeasuredDimension(mWidth, mHeight);
    }

    @Override
    public void draw(Canvas canvas) {
        canvas.save();

        Path path = new Path();
        RectF rectF = new RectF(0, 0, mWidth, mHeight);
        if (mCorners > 0F) {
            path.addRoundRect(rectF, mCorners, mCorners, Path.Direction.CCW);
        } else {
            float[] radii = new float[]{
                    mLeftTopCorner, mLeftTopCorner,
                    mRightTopCorner, mRightTopCorner,
                    mRightBottomCorner, mRightBottomCorner,
                    mLeftBottomCorner, mLeftBottomCorner
            };
            path.addRoundRect(rectF, radii, Path.Direction.CCW);
        }
        canvas.clipPath(path);

        super.draw(canvas);
    }
}

    相关的属性声明在style.xml文件中配置,这里不涉及。draw()方法是在onDraw()之前的一个方法,在这里可以使用clipPath()裁剪出一个圆角矩形轮廓。调用canvas.clipPath(path)生效,之后再执行super方法即可。

 

4.2 运行效果

android 圆角 button 动态干煸颜色 android圆角_安卓_03

 

4.3 优缺点

优点:

1.整个外层轮廓都切成圆角矩形,子View的background不会影响到圆角

2.四个角可以单独配置也可以统一配置

 

缺点:

1.无法抗锯齿,在一些老的手机上面效果可能差强人意

2.需要实现自己的自定义布局

 

五.利用CardView实现切圆角

5.1 方案实现

    CardView是v7包中的组件(ViewGroup),主要用来设置布局的边框为圆角、z轴的偏移量(这个是5.0以后才有的概念,也就是阴影的效果)。这里我们仅仅使用圆角功能。

    实现一个带CardView的布局

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".cardview.CardViewActivity">

    <androidx.cardview.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginStart="16dp"
        android:layout_marginLeft="16dp"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="16dp"
        android:layout_marginRight="16dp"
        android:layout_marginBottom="16dp"
        app:cardBackgroundColor="@color/colorAccent"
        app:cardCornerRadius="10dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="10dp的圆角"
            android:textColor="#FFFFFF"
            android:textSize="24sp" />
    </androidx.cardview.widget.CardView>

</androidx.constraintlayout.widget.ConstraintLayout>

    对应activity的代码就没必要贴了,XML布局中

app:cardCornerRadius="10dp"

    即配置四个角为10dp的圆角

 

5.2 运行效果

android 圆角 button 动态干煸颜色 android圆角_xml_04

 

5.3 优缺点

优点:

1.Google的控件,效果和稳定性都是杠杠的,还支持阴影等其他的配置

2.抗锯齿

 

缺点:

1.四个角要一起配置,不支持其中若干个角单独配置

2.使用时外层要嵌套CardView

 

六.利用ViewOutlineProvider实现切圆角

6.1 方案实现

    利用ViewOutlineProvider设置轮廓切圆角。

    对应ViewOutlineProviderActivity的布局XML如下

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".viewoutlineprovider.ViewOutlineProviderActivity">

    <LinearLayout
        android:id="@+id/lly"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginStart="16dp"
        android:layout_marginLeft="16dp"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="16dp"
        android:layout_marginRight="16dp"
        android:layout_marginBottom="16dp"
        android:orientation="vertical"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:background="@color/colorAccent">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="10dp的圆角"
            android:textSize="24sp" />
    </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

    ViewOutlineProviderActivity代码如下

package com.openld.roundcornerdemmo.viewoutlineprovider;

import android.graphics.Outline;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.view.ViewOutlineProvider;
import android.widget.LinearLayout;

import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;

import com.openld.roundcornerdemmo.R;
import com.openld.roundcornerdemmo.utils.DisplayUtils;

public class ViewOutlineProviderActivity extends AppCompatActivity {
    private LinearLayout mLly;

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_view_outline_provider);

        initWidgets();
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    private void initWidgets() {
        mLly = findViewById(R.id.lly);

        mLly.setOutlineProvider(new ViewOutlineProvider() {
            @Override
            public void getOutline(View view, Outline outline) {
                outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), DisplayUtils.dp2px(ViewOutlineProviderActivity.this, 10F));
            }
        });
        mLly.setClipToOutline(true);
    }
}

    关键就是mLly.setOutlineProvider()与mLly.setClipToOutline()两个方法的使用。

 

6.2 运行效果

android 圆角 button 动态干煸颜色 android圆角_xml_05

 

6.3 优缺点

优点:

1.支持动态下发与配置

 

缺点:

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

 

七.源码位置

链接地址