I am trying to draw shadow on my drawable, but the shadow gets cropped, since the rectangle I am drawing is the size of canvas.getClipBounds().
In order to draw the shadow I set the hardware acceleration on the view
view.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
And I also set the Shadow Layer in my Custom Drawable
bgPaint.setShadowLayer(radius, 0, 0, Color.Black);
When I draw a small rectangle the shadow is rendered. So, I know my original problem is about the shadowing being discarded for being outside of the canvas.
I tried some approaches, such as:
Setting the Drawable's bounds,
Increasing the canvas' bounds,
Disable clipChildren on the parent view,
Overriding Drawable.getPadding()
But none of them helped me.
Related
When I set View's outline as follows it works just fine:
view.setClipToOutline(true);
view.setOutlineProvider(new ViewOutlineProvider() {
#Override
public void getOutline(View view, Outline outline) {
outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), 16);
}
});
However, using a Path has no effect. Replace setRoundRect with:
Path path = new Path();
path.addRoundRect(
0, 0, view.getWidth(), view.getHeight(), 16, 16, Path.Direction.CW);
outline.setConvexPath(path);
How to make the view clip to the Path provided?
I know it is not helping in clipping but still, we can take the advantage of outline.setConvexPath(path); in elevation shadow since it's working fine as an outline for the elevation shadow around the path.
I attached an image to make it even more clear in which:
First image: ImageView with [Image with shadow]
android:outlineProvider="bounds"
android:elevation="4dp"
Second image: CustomImageView with canvas.clipPath(getShapePath()) in onDraw() method [we will get the clipped image but shadow still show as a rectangle bound]
android:outlineProvider="bounds"
android:elevation="4dp"
Third image: CustomImageView with canvas.clipPath(getShapePath()) in onDraw() method and outline?.setConvexPath(getShapePath()) in ViewOutlineProvider. [we will get the clipped image and shadow will be aligned with the clipped image]
android:outlineProvider="bounds" // this will have no effect since it will be override by setConvexPath
android:elevation="4dp"
Download Source Code
It seems that a View can only be clipped to a rectangle, a rounded rectangle, or an oval Outline. Rather, a View can only be clipped to an Outline that is defined specifically with a setRect(), setRoundRect(), or setOval() method. An Outline set with a Path will not work, even though the Path itself may be one of the aforementioned shapes.
This is documented in several places, though not particularly well, in my opinion.
View#setClipToOutline():
Note that this flag will only be respected if the View's Outline returns true from canClip().
Outline#canClip():
Currently, only Outlines that can be represented as a rectangle, circle, or round rect support clipping.
Defining Shadows and Clipping Views - Clip Views:
Only rectangle, circle, and round rectangle outlines support clipping, as determined by the Outline.canClip() method.
At first look, this didn't seem consistent with Outline's own inner workings, as a convex Path is, in fact, used internally for non-circular ovals. However, it does make sense to simply disallow all externally defined Paths, rather than expend the resources necessary to determine if an arbitrary Path is valid.
I have a custom view wich contain some bitmaps and I want to set shadows for them, for that, I use this code:
shadowPaints=new Paint(Paint.ANTI_ALIAS_FLAG);
shadowPaints.setShadowLayer(10.0f, 3.0f, 2.0f, Color.BLACK);
canvas.drawBitmap(bmp, matrix, shadowPaints);
setLayerType(LAYER_TYPE_SOFTWARE, shadowPaints);
and my result is
as you can see my shadow actually is another bitmap with different x and y position but what I want is my shadow be a solid color
bitmap.
can anyone help me about this?
setShadowLayer is actually meant for putting shadows on text.
If you already know the bitmap you want to draw, you can just add a shadow in PhotoShop and draw the bitmap and shadow all at once.
If you don't want to do that, you could make a shadow by making a copy of the image, using a PorterDuff filter to make it all grey, use Renderscript to blur the image, and draw it on the canvas at an x,y offset before drawing the actual image on top of it.
Personally, I think PhotoShop is a lot easier.
Is there any way in Android (from API 15) to clip/subtract views like masking in photoshop?
See the example below:
https://s31.postimg.org/d18lktjq3/index.jpg
The Red view is just a bold funny V shape, while the blue one is something more complex.
Note that in the red view, the striped part is transparent.
The result view i would like to obtain, is something like the blue big view, with the V shape of the second view, and anything above cut away.
Note that, in the final result, the space "inside" the V shape must be transparent.
Currently i achieve this effect using the two views one on top of the other (filling the gap in the V shape view), but this is not optimal because i have to know exactly what the other view is, and the summed up view is bigger than the source.
Thank you
In Android, this is done using Porter-Duff Transfer Modes.
Your best best for this is to have two overlay bitmaps: One that has the red V-shape, and a complementary bitmap that represents everything you want to cut out of the lower layer.
With a custom view, you override onDraw() to do these steps:
Draw the base (blue) bitmap
Draw the red stripe using Porter-Duff mode SRC_OVER
Draw the top V cutout using Porter-Duff mode CLEAR
Code would look something like this (assuming you have created the bitmaps and computed the x,y coordinates where you want to draw them):
Paint paint = new Paint();
paint.setAntiAlias(true);
canvas.drawBitmap(blue_base, blueX, blueY, paint);
// draw the red v on top of the blue part
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));
canvas.drawBitmap(red_v, redX, redY, paint);
// erase the unwanted pixels from the blue part
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
canvas.drawBitmap(cut_out, redX, redY, paint);
Here's an interesting tutorial to get you started: Punch a hole in a bitmap by using Android's porter-duff Xfer - TechRepublic
If I set the rotation to a view, there will be some artifacts on the margin of the view.
How can I fix this?
I use view.setRotation.
You probably need to enable anti-aliasing. If you're using Paint to draw on a Canvas, you can achieve this by calling Paint.setAntiAlias(true). However, if you're drawing a Bitmap, you need to set a 1px transparent border around your Bitmap
In an Android project, I am using FrameLayout to contain a TextView. This TextView has a large shadow on it (to provide a glow). However, Android is detecting the TextView's bounding rect to be just that of the contained text, without the shadow, and as a result the compositor gets confused when objects animate around it.
As a workaround I tried forcing the TextView to have its own hardware surface (with setLayerType), but this too is closely cropped to the text and doesn't account for the shadow, so the shadow gets cut off.
I have tried adding padding to the FrameLayout, but that doesn't expand the surface - it only moves the TextView down and right by the padding amount.
If I set a background color on the FrameLayout, the surface does expand to cover its entire size, but unfortunately this background is visible, even if I set it to 0x01000000. If there were some way to force it to contain the entire background even if the background color is 0, that would be a suitable solution.
What is the easiest way to expand the hardware surface to include the text glow, ideally without affecting the position of the text itself?
The trick is to get Canvas to believe that every pixel has been touched, without actually having anything to paint. Simply drawing a ColorDrawable does not work, because every step along the way seems to detect that a fully-transparent color doesn't need to be rendered. And, apparently the render for the text shadow is also only rendering where the canvas thinks it's been invalidated – so, for example, only drawing a couple of background pixels will expand the surface but still does not cause the actual text shadow to be rendered.
However, there is an operation that will cause Canvas's clipping mask to get updated even with transparent pixels – a transparent Bitmap!
So, adding a background Drawable that renders out a transparent Bitmap has the desired effect. You can add a transparent bitmap as a resource (drawn using a BitmapDrawable), or you can do this:
final Bitmap bmp = Bitmap.createBitmap(2, 2, Bitmap.Config.ALPHA_8);
final Paint bmpPaint = new Paint();
wrappingFrameLayout.setBackground(new Drawable() {
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
public void draw(final Canvas c) {
c.drawBitmap(bmp, null, getBounds(), bmpPaint);
}
public void setAlpha(final int a) {}
public void setColorFilter(final ColorFilter cf) {}
});