Android View Clipping
Asked Answered
C

9

70

Is there any way to define the clip region of a ViewGroup in android (Honeycomb)? For example, I have a ListView with an image background that has rounded corners. As I scroll through the list, the children stick out past the corners of the background - I would prefer them to clip within the rounded corners. Left: unclipped, Right: clipped

The left image is what it is currently doing, and the right is what I'd like.

I was looking at ClipDrawable, but it seems that this may only be used for progress bars?

Also, I'm trying to do this in a widget. So I cannot use a custom view and override onDraw for masking.

Thank you!

Complexion answered 6/4, 2011 at 23:33 Comment(1)
Did you figure out a solution? I am also trying to make this happen in a widget.Wreckful
D
31

Make sure layout has a background with rounded corners.

Kotlin

layout.outlineProvider = ViewOutlineProvider.BACKGROUND
layout.clipToOutline = true

Java

layout.setOutlineProvider(ViewOutlineProvider.BACKGROUND);
layout.setClipToOutline(true);
Douceur answered 15/1, 2019 at 16:9 Comment(1)
Note: Requires API Level 21Unlade
G
26

Try subclassing ViewGroup and overriding the OnDraw method as follows, substituting in values for RADIUS_IN_PIXELS:

@Override
protected void onDraw(Canvas canvas) {
    Path clipPath = new Path();
    clipPath.addRoundRect(new RectF(canvas.getClipBounds()), RADIUS_IN_PIXELS, RADIUS_IN_PIXELS, Path.Direction.CW);
    canvas.clipPath(clipPath);
    super.onDraw(canvas);
}

...and also create a custom drawable like this called something like 'rounded', substituting in YOUR_BACKGROUND_COLOR and RADIUS_IN_DP, making sure to match the rounding of the rectangle in DP to your previous clipping radius in PX:

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

Then you can use that subclass in your layout, adding the lines

android:background="@drawable/rounded"
android:clipChildren="true"

All children will clip to the bounds you specify in the OnDraw() override, and a background will be added based on your 'rounded' drawable.

Garibull answered 29/1, 2015 at 3:30 Comment(2)
NEVER instantiate objects inside onDraw, it'll lead to lags.Ivanovo
the original question is asking for something that works with RemoteViews.Sequoia
N
6

Create custom layout with overridden onDraw(Canvas canvas)

call canvas.clipRect(0, 0, mCanvasWidth, mCanvasHeight);

this will clip all views that goes out of layout boundary.

And don't forget to call setWillNotDraw(false) in constructor, so your onDraw will execute

Novel answered 27/11, 2013 at 10:32 Comment(0)
P
1

How about this: How to render an Android view to a bitmap, then clip that bitmap.

Or, another idea, use FrameLayout, stack your clip mask on top of your ViewGroup. Clip mask would have transparent middle, opaque borders.

In both cases, I guess it will be tricky to handle user input...

Plaza answered 24/5, 2011 at 19:41 Comment(2)
The first option wouldn't work, because it is in a widget, thus it must only use RemoteViews compatible Views.Complexion
And for the second option, since it's a home screen widget, a clip mask would not be viable.Complexion
S
1
Path p = new Path()

// define your clipping path...

canvas.clipPath(p);
Successive answered 26/3, 2012 at 18:23 Comment(0)
D
1

If you take a look to WhatsUp you can see that in the contact picture there is an image rounded by a rounded border. To do this I have put the ImageView inside a FrameLayout (FrameLayout because I use ProgressBar in front of picture to notify loading [actually invisible]) that has rounded border. The picture has a square shape but to refine it, you need to work with canvas.

Take another look at this link that will solve your problem ;)

Deerskin answered 10/4, 2012 at 23:3 Comment(0)
A
1

To add onto the answer by @Marco, you can also provide a custom View.outlineProvider. I ran into a similar issue using Google's newer MaterialCardView. For some reason, that view does not clip its children with the rounded corners. Child views would just draw on top of the rounded corner.

Kotlin:

val cornerRadius = 16.dp()
materialCardView.apply {
    clipToOutline = true
    outlineProvider = object : ViewOutlineProvider() {
        override fun getOutline(view: View?, outline: Outline?) {
            view?.apply {
                outline?.setRoundRect(0, 0, width, (height + cornerRadius).toInt(), cornerRadius)
            }
        }
    }
}

enter image description here

Amora answered 1/7, 2021 at 4:50 Comment(0)
F
0

May be you can used the Canvas.clipRegion method to clip the differences of the to views.

Frictional answered 23/8, 2011 at 5:28 Comment(0)
S
-4

Give android:clipToPadding a try. It will solve your problem.

Showoff answered 10/4, 2012 at 7:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.