setClipChildren and hardware layers - android

I have a custom view group that I'm animating around on a screen based on touch events (drag and drop, for instance).
I want it to be able to draw slightly outside its bounds, and still animate smoothly.
I tried setting setLayerType(LAYER_TYPE_HARDWARE, null) on the dragging view, and performance was great, but anything outside the bounds was clipped.
I tried setting LAYER_TYPE_NONE, performance was still fine, but there was animation ghosting/streaking where the view had been dragged (as if it were smearing the screen).
Tried this on a Moto X and Nexus 4, same results.
What's the best way to approach this? Is setClipChildren(false) supposed to work with LAYER_TYPE_HARDWARE? Interestingly, LAYER_TYPE_SOFTWARE exhibits the same unexpected clipping as does LAYER_TYPE_HARDWARE.

Try to use following combination:
setClipChildren(false);
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
android:hardwareAccelerated="false"
As described here: ViewPager and setClipChildren(false) workaround for 2.x devices

I had a similar problem when doing exactly the same thing. I wanted a drop shadow beneath my views. The problem is that the system will only clean up the rectangle left by the view you have moved and since you are rendering outside that view by choosing to not clip children, the rectangle is not covering all of your tracks and remains of the rendered view leave a smudgy sort of mess behind.
The solution I used was to put the view I wanted to move inside another view (A RelativeLayout with the child view aligned to the parent's center) that is slightly larger and does cover the drop shadow or whatever else you are rendering outside the bounds of your child. You can set the background of that parent view to transparent/clear. Now attach the drag action to your parent view and the Rect that is cleaned up behind it will also clean up the child's mess.
The only other way I found to do it which was very costly in terms of performance was to re-render the views that show the mess at each frame. I couldn't find any other solutions.

Related

Overlapping views and drag events

I have two overlapping views which both consume the same drag event. Think a small circle under a much larger one. When the touch location cross over the smaller circle which is under (in zdepth) the larger one it triggers a ACTION_DRAG_EXITED event, even though the view's bounds have not been exited. The event is effectively hopping from the higher view to the lower view.
How can I prevent the lower view from triggering events when occluded?
thanks!
Nigel
In case anyone else encounters this, to fix it I used view.bringToFront() to ensure the larger circle view was on top. Interestingly even though it was drawing on top and its elevation was higher, android didn't consider it on top in the context of the drag event.

How to draw outside view bounds?

How can I draw (bitmap, line, etc) outside the bounds of a view? From the view's onDraw(), I've read this is not possible as everything drawn will get clipped to the view's bounds.
I did come up with one solution but I'm hoping there's a better one. What does work is to create a transparent view that is at the top of the z order and includes the area I want to draw in (the entire app client area). Then, whenever I want to draw outside some child view, I can simply translate to the coordinates to the transparent view and draw there.
I also read about SurfaceView hoping that would do what I want. But I think it's main purpose is to provide drawing in a separate thread and doesn't solve the problem I'm discussing.
To be clear, it isn't sufficient to simply draw in the parent of the target view because other views in the parent will be higher in the z order and hide the drawing.
Intuition tells me there's a "right way" to do this. Anyone know?
I'm drawing the conclusion that the right way is to do what I proposed - create a transparent view that is at the top of the z-order for the space you need to draw in.
I come to this conclusion after learning how the Navigation Drawer drawing works - exactly in this way. So, if Google uses this technique, I conclude that it's the best way available.

Touch event on scaled custom ViewGroup returns view of non scaled state

I have been having the hardest time trying to figure this out, so I really hope someone can help, or at least point me in the correct direction.
My goal is to have a ViewGroup which supports scaling and panning.
I have tried a bunch of different approaches. Lately with this View with horizontal and vertical pan/drag and pinch-zoom.
Pinching and zooming works fine, and so does a single tap when in unzoomed or unpanned mode.
However, if i zoom the view and tap the screen, onTouch of the childview is triggered with the view that was placed in the tapped position of the unzoomed ViewGroup. It's like the content of the childviews is getting scaled, panned, and drawn correct, but the views themselves stay where they are.
Am I missing some native understanding?
Background. All my views are generated programmatically, and all the subviews to the custom ViewGroup have their own OnTouchListener. There are typically between one and 5 subviews.
I have not added any code, as I am now back to using the code from the link as is.

Rotated (not animated) views in XML pre-honeycomb?

Is it possible to rotate views in XML with APIs previous to Honeycomb - maybe with the support package? Or is the only way to create a custom class, like described here Vertical (rotated) label in Android
Edit: What I need is a statically rotated view (specifically a TextView, but I guess it's enough to know how to do it with a View). Starting with honeycomb there's a rotation attribute which can be used in XML. I need something like that.
The only thing I have found until now is use an animation with duration 0 but this still moves a bit at start and I don't want that. I tried setting the views invisible and attaching a listener to the animation which makes them visible on animation finished callback, but that made strange results... that changed the position of the views, for some reason.
The best way is with the custom subclass implementation that you linked to, where you can rotate the canvas and resize the view appropriately. This ensures that the view bounds are also set to match the text that is drawn.
The only method of transforming views externally prior to HC is the animation framework, and applying an Animation to the view with a duration of 0 and fillAfter set to true will work, but you may notice flickering on some devices as often the view will render normally on its first frame and then animated to its final position from that point onward. You can work around this by hiding the view and displaying it a bit late...but you can see how hacks are starting to stack up.
In addition, doing an Animation prior to HC will not transform the view bounds themselves, so you won't be able to neatly pack other views around this one because its position from a layout perspective will still be the rectangle calculated for the horizontal (non-rotated) text.
The simple subclass is definitely the preferred method.
HTH
Is it possible to rotate views in XML with APIs previous to Honeycomb
There is RotateAnimation. However, depending on what you are trying to accomplish, that may not meet your needs.

Android: SurfaceView and Z-Level Question

I have a FrameLayout containing first a SurfaceView, and second a RelativeLayout which in turn contians various Buttons and TextViews.
Upon the canvas of the SurfaceView I am drawing numerous Bitmaps, and, via Touch and Motion Events am allowing the user to drag them around.
These Bitmaps, when dragged around pass underneath the Buttons etc that are inside the RelativeLayout.
Now, it's my (possibly mistaken) understanding that the "Z-level" of the SurfaceView, or whatever it has that passes for it, is entirely unrelated to the actual Z-level of the rest of the Layout. Is this the case? If so, how may I get around it, so that dragged Bitmaps are drawn ontop of other Views? Or what other way can I implement a full-screen canvas and yet not have my buttons etc act like the controls of an overlay.
I guess what I actually need is an underlay, where touch events can still be picked up by the Buttons etc underneath. But I don't know how to achieve this, as, when redrawing my Canvas, I have to also redraw the background.
Can I swap the order of the RelativeLayout and the SurfaceView inside the FrameLayout, and then make the background of the Canvas transparent? If so how? Will touch events still "fall through" to the buttons underneath?
Thanks for bearing with me, I know I'm a bit of a waffler.
SurfaceView doesn't work that way.
SurfaceView has two parts, the "view" part and the "surface" part. The "view" part is a transparent hole that is laid out with the other view elements, and composited onto the view layer. The "surface" part is a completely separate layer that will be positioned and scaled to match the "view" part, and composited by the system compositor rather than the app.
You can control the Z-order of the "surface" layer when the SurfaceView is first created, but it's going to be above or below all View elements. It can't go above some Views and below others because they're completely independent layers. The "surface" layer does not catch input events, so having it on top (via SurfaceView#setZOrderOnTop()) doesn't affect input focus.
For API 14+, you can use a TextureView, which behaves in a more View-friendly way.
Edit: for full details, see the much longer explanation.
Can you clarify a little bit more? In your SurfaceView do you have a background assigned? If not, you could probably also use AbsoluteView if your intention is to simply drag pieces around. If there is no background, you should be able to place the entire view above the RelativeView that you have and only have the various Buttons and such drawn on the view on top, which would be draggable and remain above everything else.

Categories

Resources