I am transforming child views in my viewgroup when I'm swiping through the viewgroup (kind of a cover-flow effect). I am using matrix.setPolyToPoly for transformation. The views have borders which need antialiasing severely.
So far I've tried to use
canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG));
in my children's onDraw, but it doesn't seem to change anything. Also, I'm aware of BitmapDrawable.setAntiAlias(true), but my children are not drawables, but full-blown viewgroups with views inside, so I cannot use that.
Override
protected void dispatchDraw(Canvas canvas)
in your custom layout and set filter there.
canvas.setDrawFilter(new PaintFlagsDrawFilter(1, Paint.ANTI_ALIAS_FLAG));
super.dispatchDraw(canvas);
I encountered a similar issue in my cover flow inspired Gallery. This solved my issue.
Related
I'm implementing a small library for easy adding custom view swiping actions on a RecyclerView. During my implementation I've hit a issue when rendering views on a canvas. I've attached a gif here to showcase the problem:
When drawing the swipe view on the Canvas I do the follow:
private fun renderSwipeView(
view: View,
c: Canvas,
width: Int,
height: Int,
transX: Float,
transY: Float
) {
view.measure(
View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY)
)
view.layout(0, 0, width, height)
c.withTranslation(transX, transY) {
view.draw(c)
}
}
This generally works fine, but I'm seeing issues rendering with view types. In the GIF i
The green layout is a square which is centered to the top of the layout. So I know the layout is rendered in the correct size and the layouting of elements is done correctly.
Issue 1:
The blue boxes are TextView's with singleLine=true since I want the text to be neatly revealed by the element on top. Swipe right basically works fine, however on swipe left the text is not rendered at all. I tried to render this same case without rending it on a Canvas which works fine. Am I missing some part when rendering my view on the canvas?
Issue 2:
Not really a big problem, but the swipe view (red / orange container) will be rendered out side it's layout. I could easily fix this by using canvas.clipRect() on the canvas, but I would like to solve the root cause of this rather than do a workaround.
Entire source code with this demo can be seen here.
Thanks
Best regards
Regarding issue 1: I just managed to first issue. It seems to be related to a bug in TextView, by switching to AppCompatTextView the issue was solved.
I've created a custom view which extends TextView. I want to use a BlurMaskFilter on an oval, however, unless I use LAYER_TYPE_SOFTWARE, the oval isn't blurred as expected. Using LAYER_TYPE_SOFTWARE will blur the view correctly, however now it is clipped to its bounds which I want to avoid as I am trying to create a colored shadow effect.
Bizarrely I only have this issue on API's 21-27 (min API is 21). On API's 28+ BlurMaskFilter works correctly with hardware acceleration and also has no issues drawing outside the view bounds.
LAYER_TYPE_HARDWARE
LAYER_TYPE_SOFTWARE
LAYER_TYPE_HARDWARE API 28+ (Desired effect)
I've tried setting clipChildren=false on all the views in the hierarchy, with no luck.
I also tried increasing the canvas.clipRect using negative values as per this post: https://stackoverflow.com/a/15506277/5752426 however this only seemed to work when using positive values to decrease the drawing area, and had no effect when using negative values to draw outside the view bounds.
I can somewhat get around the issue by increasing the view bounds in onMeasure() and then only painting inside those view bounds, however, this isn't ideal as I now have to account for this extra space when positioning my views in the layouts. Furthermore, the problem becomes inconsistent to account for as different views might have different elevations and shadow/blur sizes.
init block for the custom view:
init {
this.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
setWillNotDraw(false)
...
onDraw for the custom view:
override fun onDraw(canvas: Canvas?) {
...
if (elevation > 0f) {
circleShadowBackgroundPaint.maskFilter = circleBlurMaskFilter
canvas?.drawOval(circleBlurBackground, circleShadowBackgroundPaint)
}
...
Any help would be appreciated!
If you set this View's LayerType to LAYER_TYPE_SOFTWARE,the clipCHildren and clipToPadding will no longer has anly effect on this View.You should setLayerType on its Parent(or parent's parent ,if the parent is as large as the size of this View).
You can try (View (getParent())).setLayerType(View.LAYER_TYPE_SOFTWARE, null)
I'm having a requirement of draw a border around a parent layout in Xamarin.Android it can be any layout like FrameLayout, LinearLayout etc.
I have achieved this by using GradientDrawable - Which is just like setting a background for the layout with a shape.
So my requirement will be achieved
Query
When setting corner radius for the border, it is not clipping the children. How to clip the children or any other way alternative to do the both? Kindly share your suggestion on this.?
Note: I have tried with ClipChildren, ClipToPadding for the layout.
PS: Above images are mentioned for illustration purpose, they are not the exact output.
TIA.
You can clip the view using canvas. Use ClipPath(path) method of Canvas to clip the required area.
Syntax:
Path path = new Path();
path.AddCircle(200,200,100,Direction.CW);
canvas.ClipPath(path);
You can make use of ViewOutlineProvider API. As an example usage see ClippingBasic project.
Having defined outline provider class as such:
private class OvalOutlineProvider extends ViewOutlineProvider {
#Override
public void getOutline(View view, Outline outline) {
outline.setOval(0, 0, view.getWidth(), view.getHeight());
}
}
Then apply view outline to parent:
View parent = findViewById(R.id.parent);
parent.setOutlineProvider(new OvalOutlineProvider());
parent.setClipToOutline(true);
ViewOutlineProvider is accessible starting from API 21. Not sure, whether the solution is applicable for Xamarin (hope it does).
Setting ClipToOutline = true; for the parent, resolves the issue.
I am implementing stuff like YouTube App which you can drag the video window to the lower right corner.
I am able to achieve the same effect using setLayoutParam(), but it turns out not as fast as I expected (as smooth as YouTube).
So I use setScaleX and setScaleY instead. It's fast and the behavior is similar to YouTube app. But the scale seems not applied to the child views.
So, what can I do to efficiently scale a ViewGroup and it's child views. Thanks.
layout.xml:
<ScalableRelativeLayout android:id="#+id/parent" >
<VideoView android:id="#+id/video"/>
</ScalableRelativeLayout>
When the parent get scaled, the video seems not scaled and only shows part of it. I guess the parent scale didn't pass to its children, so I override below method.
Override method:
#Override
protected void dispatchDraw(Canvas canvas) {
canvas.save();
canvas.scale(mScale, mScale);
super.dispatchDraw(canvas);
canvas.restore();
}
ScalableRelativeLayout extends RelativeLayout and override above method. Now the video scale inside parent, only occupy upper-left area.
It seems the video content will not affected by the view.
I have a custom view which is a RelativeLayout which contains a ListView (both are set to match parent - width and height).
On top of the list I have a View as seen in the picture below:
I want that the only part that will be drawn is the rect of my View (marked with gray), while all the other portion of the view will be transparent.
is there a way I can do this?
I already tried to extend RelativeLayout and override its onDraw() method, but I didn't manage to draw only the specific region (marked in gray).
Eventually, I did it by extending RelativeLayout and override its draw() method.
#Override
protected void draw(Canvas canvas) {
canvas.clipRect(magnifiedRegion.getLeft(), magnifiedRegion.getTop(), magnifiedRegion.getRight(), magnifiedRegion.getBottom());
super.draw(canvas);
}
where magnifiedRegion is my View as it mentioned in the question above.
Note: you must set this.setWillNotDraw(false); in the constructor or else, draw() method won't be called by the framework (since our root view is a ViewGroup - it's an optimization Android framework does by default).