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.
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)
Im trying to make a custom view with some drawings in it. I want these drawings to take up the entire view but I also want the view to maintain a certain height/width ratio for these drawings.
In your onDraw(canvas) method, use canvas.getWidth() and canvas.getHeight() get to get the entire width and height of the View. Use this detail (like calculating the center of the View (width/2,height/2)) to draw your drawings. This way they will always fill the entire view and maintain the right aspect ratio.
I have a custom view which is nested inside of a ScrollView. The idea is that the custom view can be longer than the height of the screen, so when you swipe through the ScrollView you can see all of its contents.
In order to do this I adjust the clip of the custom view's canvas like this:
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.clipRect(mCanvasArea, Region.Op.REPLACE);
}
Where mCanvasArea is a pre-generated RectF which represents the entire area the canvas can draw. I logged it's value # 10308.
However when the app runs, the custom view is unscrollable, it acts as if the visible content represents all of the content in the canvas, but I can see more peeking up from the bottom of the screen.
Here is my XML declaration for the view:
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#id/activity_toolbar"
android:fillViewport="true">
<com.myapp.CustomView
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</ScrollView>
(I had to add fillViewport in order to get the content to show up in the first place)
Is there anything else that needs to be done in order yo make the view scroll?
You will need to specify the height of the custom view, not the canvas. The canvas passed to onDraw reflects the width and height of the view.
Setting the view to wrap_content doesn't mean it will grow whenever you draw outside of its bounds. It will only "wrap" the child views, not the canvas.
To set your view's height programmatically:
getLayoutParams().height = 500;
invalidate();
I'm not sure what you are going for with this exactly so I can't get too much more specific about where you should set the height, but I'd recommend not changing your view's dimensions in onDraw because when you invalidate it will cause another draw and you'll need some funky logic to handle this recursion.
Instead you could determine the height you need when the view is constructed, and then override onMeasure with something like this:
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, calculatedHeight);
}
This would force your custom view into the calculated height.
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.