SoFunction
Updated on 2025-03-01

Detailed explanation of how Android implements rounded corners of different sizes

Background rounded corners

Shape

For general background, we can use it directlyshapeThis method naturally supports setting different four cornersradius,for example:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:andro>
    <solid android:color="#8358FF" />
    <corners
        android:bottomLeftRadius="10dp"
        android:bottomRightRadius="20dp"
        android:topLeftRadius="30dp"
        android:topRightRadius="40dp" />
</shape>

Tips:shapeThe implementation at the code layer isGradientDrawable, you can directly build a rounded background in the code layer

Content rounded corners

In many cases, setting different rounded corners on the four sides of the background cannot satisfy us. In most cases, we need to cut rounded corners together with the content inside. Here we need to first correct an error on the Internet.

Someone posted that it can be passedMethod to achieve the difference between four cornersradius,as follows:

outline?.setConvexPath(
    Path().apply {
        addRoundRect(
            0f, 0f, (), (),
            floatArrayOf(
                topLeftRadius,
                topLeftRadius,
                topRightRadius,
                topRightRadius,
                bottomRightRadius,
                bottomRightRadius,
                bottomLeftRadius,
                bottomLeftRadius
            ),
            
        )
    }
)

After actual testing, writing like this is not possible. To be precise, it is not possible on most systems (it is OK on MIUI, I don’t know if it should praise its compatibility or complain about it. My test machine uses Xiaomi, which led to me discovering this problem in the final testing stage)

After pointing out the wrong method, let's see what are the correct solutions

CardView

When it comes to cutting content and rounded corners, we will naturally think of itCardView,actuallyCardViewThe rounded corners are also passedOutlineImplemented

Someone may ask,CardViewNot only the same four cornersradiusIs it? Don't worry, let's see the magical nesting method I came up with with a sudden idea

Magical nesting method

Since oneCardViewOnly oneradius, I'll use a few moreCardViewCan nesting solve the problem?

To give the simplest example, for example, the design wants the upper part to be12dpThe rounded corners, the lower half has no rounded corners, we need a helperView, align its top with the bottom of the parent layout, and then set it to the height of the rounded corner size ormargin, then useCardView, make the bottom of it align this auxiliaryViewat the bottom, set a rounded corner sizepadding, so, becauseCardViewThe boundary of the parent layout is exceeded, so the rounded corners at the bottom will not be displayed, and because we set it rightpadding,soCardViewThe contents inside can also be fully displayed, which is perfect, such as the following:

<
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <Space
        android:
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_marginTop="12dp"
        app:layout_constraintTop_toBottomOf="parent" />
    <
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:cardBackgroundColor="@android:color/transparent"
        app:cardCornerRadius="12dp"
        app:cardElevation="0dp"
        app:contentPaddingBottom="12dp"
        app:layout_constraintBottom_toBottomOf="@+id/guideline">
        <ImageView
            android:layout_width="match_parent"
            android:layout_height="300dp"
            android:adjustViewBounds="true"
            android:background="#8358FF" />
    </>
</>

The example above is not nested because there are no rounded corners on the other side, then if we need the upper half to be12dpThe lower part is6dpWe can operate the rounded corners like this

The technique is the same as the example above, but we nest one in the outermost layer.CardView, and set its rounded corner to the smaller rounded corner size6dp, put the insideCardViewThe rounded corners are set to the larger rounded corner size12dp, the specific implementation is as follows:

< xmlns:andro
    xmlns:app="/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:cardBackgroundColor="@android:color/transparent"
    app:cardCornerRadius="6dp"
    app:cardElevation="0dp"
    app:layout_constraintTop_toTopOf="parent">
    <
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <Space
            android:
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_marginTop="12dp"
            app:layout_constraintTop_toBottomOf="parent" />
        <
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:cardBackgroundColor="@android:color/transparent"
            app:cardCornerRadius="12dp"
            app:cardElevation="0dp"
            app:contentPaddingBottom="12dp"
            app:layout_constraintBottom_toTopOf="@+id/guideline">
            <ImageView
                android:layout_width="match_parent"
                android:layout_height="300dp"
                android:adjustViewBounds="true"
                android:background="#8358FF" />
        </>
    </>
</>

In essence, it is a large rounded corner with small rounded corners. The cutting range of large rounded corners is larger, covering the cutting range of small rounded corners. From a visual perspective, different rounded corners on both sides are realized.

So what if we want to further realize the rounded corners on three sides or the rounded corners on four sides? The principle is the same as above, except that nesting and placeholding will become more complicated. Remember one principle: small rounded corners are outside and large rounded corners are inside. I will directly paste the specific implementation below, and you can take it yourself:

  • Different rounded corners on three sides (6dp in the lower left, 12dp in the upper left, 24dp in the upper right)
< xmlns:andro
    xmlns:app="/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <Space
        android:
        android:layout_width="6dp"
        android:layout_height="match_parent"
        app:layout_constraintStart_toEndOf="parent" />
    <
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:cardBackgroundColor="@android:color/transparent"
        app:cardCornerRadius="6dp"
        app:cardElevation="0dp"
        app:contentPaddingRight="6dp"
        app:layout_constraintEnd_toEndOf="@+id/guideline"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">
        <
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <Space
                android:
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_marginTop="12dp"
                app:layout_constraintTop_toBottomOf="parent" />
            <
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:cardBackgroundColor="@android:color/transparent"
                app:cardCornerRadius="12dp"
                app:cardElevation="0dp"
                app:contentPaddingBottom="12dp"
                app:layout_constraintBottom_toTopOf="@+id/guideline2">
                <
                    android:layout_width="match_parent"
                    android:layout_height="match_parent">
                    <Space
                        android:
                        android:layout_width="0dp"
                        android:layout_height="match_parent"
                        android:layout_marginEnd="24dp"
                        app:layout_constraintEnd_toStartOf="parent" />
                    <Space
                        android:
                        android:layout_width="match_parent"
                        android:layout_height="0dp"
                        android:layout_marginTop="24dp"
                        app:layout_constraintTop_toBottomOf="parent" />
                    <
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        app:cardBackgroundColor="@android:color/transparent"
                        app:cardCornerRadius="24dp"
                        app:cardElevation="0dp"
                        app:contentPaddingBottom="24dp"
                        app:contentPaddingLeft="24dp"
                        app:layout_constraintBottom_toBottomOf="@+id/guideline4"
                        app:layout_constraintEnd_toEndOf="parent"
                        app:layout_constraintStart_toStartOf="@+id/guideline3">
                        <ImageView
                            android:layout_width="match_parent"
                            android:layout_height="300dp"
                            android:adjustViewBounds="true"
                            android:background="#8358FF" />
                    </>
                </>
            </>
        </>
    </>
</>
  • Different rounded corners on four sides (6dp lower left, 12dp upper left, 24dp upper right, 48dp lower right)
< xmlns:andro
    xmlns:app="/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <Space
        android:
        android:layout_width="6dp"
        android:layout_height="match_parent"
        app:layout_constraintStart_toEndOf="parent" />
    <
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:cardBackgroundColor="@android:color/transparent"
        app:cardCornerRadius="6dp"
        app:cardElevation="0dp"
        app:contentPaddingRight="6dp"
        app:layout_constraintEnd_toEndOf="@+id/guideline"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">
        <
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <Space
                android:
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_marginTop="12dp"
                app:layout_constraintTop_toBottomOf="parent" />
            <
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:cardBackgroundColor="@android:color/transparent"
                app:cardCornerRadius="12dp"
                app:cardElevation="0dp"
                app:contentPaddingBottom="12dp"
                app:layout_constraintBottom_toTopOf="@+id/guideline2">
                <
                    android:layout_width="match_parent"
                    android:layout_height="match_parent">
                    <Space
                        android:
                        android:layout_width="0dp"
                        android:layout_height="match_parent"
                        android:layout_marginEnd="24dp"
                        app:layout_constraintEnd_toStartOf="parent" />
                    <Space
                        android:
                        android:layout_width="match_parent"
                        android:layout_height="0dp"
                        android:layout_marginTop="24dp"
                        app:layout_constraintTop_toBottomOf="parent" />
                    <
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        app:cardBackgroundColor="@android:color/transparent"
                        app:cardCornerRadius="24dp"
                        app:cardElevation="0dp"
                        app:contentPaddingBottom="24dp"
                        app:contentPaddingLeft="24dp"
                        app:layout_constraintBottom_toBottomOf="@+id/guideline4"
                        app:layout_constraintEnd_toEndOf="parent"
                        app:layout_constraintStart_toStartOf="@+id/guideline3">
                        <
                            android:layout_width="match_parent"
                            android:layout_height="match_parent">
                            <Space
                                android:
                                android:layout_width="0dp"
                                android:layout_height="match_parent"
                                android:layout_marginEnd="48dp"
                                app:layout_constraintEnd_toStartOf="parent" />
                            <Space
                                android:
                                android:layout_width="match_parent"
                                android:layout_height="0dp"
                                android:layout_marginBottom="48dp"
                                app:layout_constraintBottom_toTopOf="parent" />
                            <
                                android:layout_width="0dp"
                                android:layout_height="wrap_content"
                                app:cardBackgroundColor="@android:color/transparent"
                                app:cardCornerRadius="48dp"
                                app:cardElevation="0dp"
                                app:contentPaddingLeft="48dp"
                                app:contentPaddingTop="48dp"
                                app:layout_constraintEnd_toEndOf="parent"
                                app:layout_constraintStart_toStartOf="@+id/guideline5"
                                app:layout_constraintTop_toTopOf="@+id/guideline6">
                                <ImageView
                                    android:layout_width="match_parent"
                                    android:layout_height="300dp"
                                    android:adjustViewBounds="true"
                                    android:background="#8358FF" />
                            </>
                        </>
                    </>
                </>
            </>
        </>
    </>
</>

Custom ImageView

Since most of the needs of cropping content, the contents are pictures, we can also directly crop the pictures, and at this time we can customize themImageViewTo cut the image into rounded corners of different sizes

clipPath

Let’s talk about the disadvantage of this method first, that is, the inability to use anti-aliasing, which is destined to be unable to be used formally, but let’s take a look at how it is implemented.

First, we need to rewriteImageViewofonSizeChangedMethod for oursPathDetermine the route

override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
    (w, h, oldw, oldh)
    ()
    //The radii here is our customized array of rounded corners with four sides (size is 8, clockwise from the upper left to the lower left)    (0f, 0f, (), (), radii, )
}

Then we rewriteonDrawmethod

override fun onDraw(canvas: Canvas) {
    (path)
    (rawBitmapCanvas)
}

Some tutorials on the Internet say you want to set upPaintFlagsDrawFilter, but in fact, it's for thisPaintFlagsDrawFilterSet upPaint.ANTI_ALIAS_FLAGAnti-aliasing attributes are useless, anti-aliasing is only usedPaintOnly effective can be taken if

PorterDuff

now thatclipPathIf you can't use anti-aliasing, then we can change the route curve to save the country, that is,PorterDuff

Of course, this method also has its disadvantage, that is, it cannot use hardware acceleration, but compared to the inability to use anti-aliasing, this disadvantage is nothing

First, we want to disable hardware acceleration in the constructor

init {
    setLayerType(LAYER_TYPE_SOFTWARE, null)
}

Then rewriteonSizeChangedMethod, in this method, we need to determinePath, construct the corresponding sizeBitmapandCanvas, these two are used to obtain the original rounded corners.Bitmapof

override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
    (w, h, oldw, oldh)
    ()
    (0f, 0f, (), (), radii, )
    rawBitmap = (w, h, .ARGB_8888)
    rawBitmapCanvas = Canvas(rawBitmap!!)
}

Then we rewriteonDrawmethod

private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
private val xfermode = PorterDuffXfermode(.SRC_IN)
override fun onDraw(canvas: Canvas) {
    val rawBitmap = rawBitmap ?: return
    val rawBitmapCanvas = rawBitmapCanvas ?: return
    (rawBitmapCanvas)
    (path, paint)
     = xfermode
    (rawBitmap, 0f, 0f, paint)
     = null
}

Here, we call the parent classonDrawMethod to obtain the original rounded cornersBitmap, then drawPath, pass againPorterDuffThe overlay effect draws the original we just gotBitmap,because.SRC_INThe effect is to draw the intersection of two layers and display the upper layer, so we finally get a picture with rounded corners

Screenshot problem

If you want toViewScreenshot intoBitmap,existAndroid 8.0We can use it in the above systemsPixelCopy, use it at this timeCardVieworOutlineThere will be no problem with the cropped rounded corners, but inAndroid 8.0In the following systems, we usually build aBitmapofCanvas, and then the screenshotViewCalldrawMethod to achieve screenshot effect, and in this case, useCardVieworOutlineThe cropped rounded corners will be invalid (screenshotBitmapThe rounded corners are gone), and the occurrence of this situation seems to be related to hardware acceleration. In response to this problem, my personal idea is to prepare two sets of layouts.8.0Used aboveCardVieworOutline, use screenshotPixelCopy8.0Use the followingPorterDuffThe solution directly trims the picture to avoid performance losses to the greatest extent

Summarize

The above is what I am currently doingAndroidSome ideas and problems encountered in implementing rounded corners of different sizes, as forCardViewI have not verified whether nesting will cause any performance problems. I have no verified any better solutions. Please point out in the comment section and let me brainstorm together.

This is the end of this article about how Android implements rounded corners of different sizes. For more related content on Android implementing rounded corners of different sizes, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!