Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android: how to clip only top rounded corners

Tags:

android

clip

I'm creating a ScrollView with a FrameLayout inside. I want to design it so that only the top corners are rounded on the ScrollView. I've created a drawable shape as follows

<shape>
    <solid android:color="@color/white"/>
    <corners
        android:bottomLeftRadius="0dp"
        android:bottomRightRadius="0dp"
        android:topLeftRadius="16dp"
        android:topRightRadius="16dp"/>
    <padding android:padding="0dp"/>
</shape>

I've then set the following on the ScrollView

 scrollView.setOutlineProvider(ViewOutlineProvider.BACKGROUND);
 scrollView.setClipToOutline(true);

When i try scrolling, the elements in my FrameLayout end up protruding through the outline of my scrollview

Excuse the drawing, but what i'm looking to achieve

However if i instead create a shape like so

<shape>
    <solid android:color="@color/white"/>
    <corners
        android:radius="16dp"/>
    <padding android:padding="0dp"/>
</shape> 

It clips it just fine.

So how would i clip it if i only want the top to be cornered.

like image 976
Jankers Avatar asked May 02 '19 19:05

Jankers


People also ask

How do I round the corners of an image in Android?

Create a layout and set its background to your shape drawable. Wrap that layout around your ImageView (with no padding) The ImageView (including anything else in the layout) will now display with rounded layout shape.

How to make the corners of a button round in Android?

This example demonstrates how to make the corners of a button round in Android. Step 1 − Create a new project in Android Studio, go to File ⇒ New Project and fill all required details to create a new project. Step 2 − Add the following code to res/layout/activity_main.xml. Step 3 − Add the following code to src/MainActivity.java

How to make textview corner rounded in Android?

First of all, we need to create a drawable resource file, which has a proper definition to make TextView corners rounded, and then we have to add a background attribute to that special TextView object. Let’s do it in steps! Step 1: Create a new android studio project, and select an empty activity.

How to get rounded corners on default screen of mobile device?

Select your mobile device as an option and then check your mobile device which will display your default screen − In the above result, it shown initial screen. Now click on button it will open custom dialog with rounded corners as shown below -

How to set the corner radius to 50 percent in Android?

Create a Project in Android Studio with Empty Compose Activity template, and modify MainActivity.kt file as shown in the following. import androidx.compose.foundation.layout.* override fun onCreate (savedInstanceState: Bundle?) { Now, let us set the corner radius to 50 percent. Pass integer value of 50 to RoundedCornerShape ().


Video Answer


3 Answers

This is a Kotlin variation of @Jankers answer and answers to @Tony's question also. Explains how you can set only one rounded corner or two adjacent ones.

  fun setCorners() {
        
        val outlineProvider = object : ViewOutlineProvider() {
            override fun getOutline(view: View, outline: Outline) {

                val left = 0
                val top = 0
                val right = view.width
                val bottom = view.height
                val cornerRadiusDP = 16f
                val cornerRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, cornerRadiusDP, resources.displayMetrics).toInt()

                // all corners
                outline.setRoundRect(left, top, right, bottom, cornerRadius.toFloat())

                /* top corners
                outline.setRoundRect(left, top, right, bottom+cornerRadius, cornerRadius.toFloat())*/

                /* bottom corners
                outline.setRoundRect(left, top - cornerRadius, right, bottom, cornerRadius.toFloat())*/

                /* left corners
                outline.setRoundRect(left, top, right + cornerRadius, bottom, cornerRadius.toFloat())*/

                /* right corners
                outline.setRoundRect(left - cornerRadius, top, right, bottom, cornerRadius.toFloat())*/

                /* top left corner
                outline.setRoundRect(left , top, right+ cornerRadius, bottom + cornerRadius, cornerRadius.toFloat())*/

                /* top right corner
                outline.setRoundRect(left - cornerRadius , top, right, bottom + cornerRadius, cornerRadius.toFloat())*/

                /* bottom left corner
                outline.setRoundRect(left, top - cornerRadius, right + cornerRadius, bottom, cornerRadius.toFloat())*/

                /* bottom right corner
                outline.setRoundRect(left - cornerRadius, top - cornerRadius, right, bottom, cornerRadius.toFloat())*/

            }
        }

        myView.outlineProvider = outlineProvider
        myView.clipToOutline = true

    }
like image 69
Dragan Stojanov Avatar answered Oct 21 '22 15:10

Dragan Stojanov


I've managed to get this working by creating a custom ViewOutlineProvider and using that instead of a background value

ViewOutlineProvider mViewOutlineProvider = new ViewOutlineProvider() {
    @Override
    public void getOutline(final View view, final Outline outline) {
        float cornerRadiusDP = 16f;
        float cornerRadius = TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, cornerRadiusDP, getResources().getDisplayMetrics());
            outline.setRoundRect(0, 0, view.getWidth(), (int)(view.getHeight() + cornerRadius), cornerRadius);
        }
};
scrollView.setOutlineProvider(mViewOutlineProvider);
scrollView.setClipToOutline(true);
like image 28
Jankers Avatar answered Oct 21 '22 15:10

Jankers


I took the solution from @Jankers and @Dragan and condensed it, and added data binding so that it can be done from xml.

NOTE: Clipping with outline does not support corners to be different sizes!

OutlineProviders:

class RoundedCornersOutlineProvider(
    val radius: Float? = null,
    val topLeft: Float? = null,
    val topRight: Float? = null,
    val bottomLeft: Float? = null,
    val bottomRight: Float? = null,
) : ViewOutlineProvider() {

    private val topCorners = topLeft != null && topLeft == topRight
    private val rightCorners = topRight != null && topRight == bottomRight
    private val bottomCorners = bottomLeft != null && bottomLeft == bottomRight
    private val leftCorners = topLeft != null && topLeft == bottomLeft
    private val topLeftCorner = topLeft != null
    private val topRightCorner = topRight != null
    private val bottomRightCorner = bottomRight != null
    private val bottomLeftCorner = bottomLeft != null

    override fun getOutline(view: View, outline: Outline) {
        val left = 0
        val top = 0
        val right = view.width
        val bottom = view.height

        if (radius != null) {
            val cornerRadius = radius //.typedValue(resources).toFloat()
            outline.setRoundRect(left, top, right, bottom, cornerRadius)
        } else {
            val cornerRadius = topLeft ?: topRight ?: bottomLeft ?: bottomRight ?: 0F

            when {
                topCorners -> outline.setRoundRect(left, top, right, bottom + cornerRadius.toInt(), cornerRadius)
                bottomCorners -> outline.setRoundRect(left, top - cornerRadius.toInt(), right, bottom, cornerRadius)
                leftCorners -> outline.setRoundRect(left, top, right + cornerRadius.toInt(), bottom, cornerRadius)
                rightCorners -> outline.setRoundRect(left - cornerRadius.toInt(), top, right, bottom, cornerRadius)
                topLeftCorner -> outline.setRoundRect(
                    left, top, right + cornerRadius.toInt(), bottom + cornerRadius.toInt(), cornerRadius
                )
                bottomLeftCorner -> outline.setRoundRect(
                    left, top - cornerRadius.toInt(), right + cornerRadius.toInt(), bottom, cornerRadius
                )
                topRightCorner -> outline.setRoundRect(
                    left - cornerRadius.toInt(), top, right, bottom + cornerRadius.toInt(), cornerRadius
                )
                bottomRightCorner -> outline.setRoundRect(
                    left - cornerRadius.toInt(), top - cornerRadius.toInt(), right, bottom, cornerRadius
                )
            }
        }
    }
}

class CircleOutlineProvider : ViewOutlineProvider() {
    override fun getOutline(view: View, outline: Outline) {
        val size = view.run { min(width, height) }
        outline.setRoundRect(0, 0, size, size, (size).toFloat())
    }
}

Data Binding (@BindingAdapter):

@BindingAdapter("clipCircle")
fun View.bindClipCircle(clipCircle: Boolean?) {
    outlineProvider = CircleOutlineProvider()
    clipToOutline = true
}

@BindingAdapter("clipRadius", "clipTopLeft", "clipTopRight", "clipBottomLeft", "clipBottomRight", requireAll = false)
fun View.bindClipCorners(radius: Float?, topLeft: Float?, topRight: Float?, bottomLeft: Float?, bottomRight: Float?) {
    this.outlineProvider = RoundedCornersOutlineProvider(radius, topLeft, topRight, bottomLeft, bottomRight)
    this.clipToOutline = true
}

Clipping in xml

<androidx.constraintlayout.widget.ConstraintLayout
    clipCircle="@{@bool/const_true}"
    ...

<ImageView
    clipBottomLeft="@{@dimen/green_tab_corner_radius}"
    clipBottomRight="@{@dimen/green_tab_corner_radius}"
    ...
like image 26
Yokich Avatar answered Oct 21 '22 14:10

Yokich