Search code examples
androidxmlwidget

Rounded Corners in an ImageView in Android v31 and below


I'm trying to put make an ImageView's corners rounded with pure xml in an Home Screen widget. I am using android:clipToOutline="true" in v31 and higher but as my app supports lower, I couldn't use it all the time. This is my code:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="9dp"
    android:background="@drawable/widget_background"
    android:padding="9dp"
    android:id="@+id/widget_container"
    android:orientation="horizontal">

    <TextView
        android:id="@+id/default_text"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:textSize="25sp"
        android:textStyle="bold"
        android:gravity="center"
        android:textColor="#E9E9E9"
        tools:text="Nothing yet!" />

    <ImageView
        android:id="@+id/scribb_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="fitXY"
        android:background="@drawable/image_background"
        android:contentDescription="@string/image" />
</LinearLayout>

image_background.xml:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <corners android:radius="9dp"/>
</shape>

This is run with a Flutter project so I try not to use too many dependencies.


Solution

  • Note : Given approach is kind of a hack, use if you don't find a better solution.

    Create a frame drawable with required corner radius and transparent background. The color of the frame should match the background of the root parent. Then apply this frame as an overlay to your image view.

    Since the frame background is transparent, you will be able to see the bottom image, but since the frame border color matches you screen color, it hides the image at that part making image to appear as curved edges.

    Drawable Code : I created a required image in image editor and then converted it to vector using this site.

    <vector xmlns:android="http://schemas.android.com/apk/res/android" 
        xmlns:aapt="http://schemas.android.com/aapt"
        android:viewportWidth="474"
        android:viewportHeight="469"
        android:width="200dp"
        android:height="200dp">
        <path
            android:pathData="M0 0L474 0L474 469L0 469L0 0ZM66 3Q64 6 58 4L45 8Q33 13 25 22Q9 36 5 61L5 410L8 424L19 443Q33 462 61 467L414 467Q432 464 444 454Q455 444 463 431L467 421L469 410L469 61L464 43Q459 32 451 24Q442 14 430 8L417 4Q410 6 409 3L66 3Z"
            android:fillColor="#000000"
            android:strokeColor="#000000"
            android:strokeWidth="1" />
    </vector>
    

    XML Code : You can use backgroundTint property to change color of your drawable. You may also want to use RelativeLayout or ConstraintLayout to properly align the overlay view on top of the image view.

    <RelativeLayout 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"
        android:background="@color/background_color">
    
        <ImageView
            android:id="@+id/image_view"
            android:layout_width="180dp"
            android:layout_height="180dp"
            android:layout_centerVertical="true"
            android:layout_marginStart="16dp"
            android:layout_marginEnd="16dp"
            android:src="@drawable/ic_launcher_background" />
    
        <View
            android:id="@+id/circular_overlay"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignStart="@id/image_view"
            android:layout_alignTop="@id/image_view"
            android:layout_alignEnd="@id/image_view"
            android:layout_alignBottom="@id/image_view"
            android:background="@drawable/bg"
            android:backgroundTint="@color/background_color" />
    
        <ImageView
            android:id="@+id/image_view_without_overlay"
            android:layout_width="180dp"
            android:layout_height="180dp"
            android:layout_centerVertical="true"
            android:layout_toEndOf="@id/image_view"
            android:src="@drawable/ic_launcher_background" />
    </RelativeLayout>
    

    Drawable:

    Drawable Output

    Sample Output:

    Sample Output

    Drawbacks: The above approach is has some drawbacks, like you cannot customize the radius of the curve easily. And, if you want this frequently you will end up with double the number of views for every image view, making the layout complex.

    Better Approach : A better approach will be to create a custom view, with the same idea as above, extend the base ImageView class and draw a border outside it using Paint and Canvas. With custom view you can easily customize it later, including radius and color. Also, you layout will become simpler since instead of ImageView with you can use your custom view. You can read about custom views here.