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!

Jon Ross
  • 701
  • 1
  • 5
  • 6

9 Answers9

30

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);
Marco
  • 405
  • 5
  • 10
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.

head in the codes
  • 1,159
  • 12
  • 24
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

duggu
  • 37,851
  • 12
  • 116
  • 113
kapil
  • 257
  • 4
  • 9
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 ;)

Simone Casagranda
  • 1,217
  • 17
  • 26
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...

Pēteris Caune
  • 43,578
  • 6
  • 59
  • 81
  • The first option wouldn't work, because it is in a widget, thus it must only use RemoteViews compatible Views. – Jon Ross May 28 '11 at 03:55
  • And for the second option, since it's a home screen widget, a clip mask would not be viable. – Jon Ross May 28 '11 at 03:57
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

hellaandrew
  • 823
  • 11
  • 23
1
Path p = new Path()

// define your clipping path...

canvas.clipPath(p);
Bondax
  • 3,143
  • 28
  • 35
0

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

blessanm86
  • 31,439
  • 14
  • 68
  • 79
-4

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

Hassan Jawed
  • 1,650
  • 1
  • 12
  • 19