I'm using a 3rd party drawer that allows bottom- and top-edge drawers (https://gist.github.com/patrickfav/6284130 MultipleOrientationSlidingDrawer).
When I animate opening of the drawer, I get display tearing but only under particular circumstances. When the drawer header is clicked, animation is smooth and beautiful.
I have a RecyclerView in the main window. If users click on an entry in the list view, the drawer slides open showing details of the entry selected (click on a track, slide the media controls up from the bottom). Part of the drawer animation is to fade the main window contents to black as the drawer opens. So there's heavy overdraw going on. Oddly, when the animation is triggered from clicking in the RecyclerView, I get tearing of the display during animation (serious flickering, occurring about 2/3 of the way down from the top of the screen).
I'm looking for advice as to how to proceed: how to debug, or theories as to what might be causing flickering of the display during animation.
Here's what I've tried so far.
Initially, I though this was caused by RippleDrawable animations in the RecyclerView entries (both on the press and an activition change that is used to indicate selection. To prevent this, I've tried to cancel the ripple animations by calling recylerView.jumpDrawablesToCurrentState() at various points (start of animation, as well as during every animation update). As far as I can tell, the RippleDrawable animations have been effectively cancelled. But the display ripping still occurs. Things are a bit complicated. State change animations don't get triggered until after a dispatch of some kind occurs; but the call to startAnimation also doesn't appear to occur until after drawable state changes are complete. So I think I've done this right. Just in case, I've tried calling recyclerView.jumpDrawablesToCurrentState during each animation pass, which certinly should cancel the drawable animations. Still no joy.
I have deferred responses to the click itself until the drawer open animation completes, so there are no major background or foreground operations going on while the animation occurs. Also no audio running, no service activity to speak of. Profiling indicates that there's no code running that isn't related to drawing while the animation runs.
Since everything happens relatively quickly, it's hard to tell exactly what's happening, other than that there's serious flickering going on. Since there's an animation involved, there's no way to break to the debugger in the middle of the animation to see what's going on. Once I break to the debugger, the next redraw will be fully-open state, since the animation positions derive from the current device uptimeMillis().
If I profile the animation, I see an awful lot of text layout operations, which kind of suggest that the recycler view entries might be constantly performing layout during the animation.. Which doesn't really make sense. There's no reason I can think of for layout to occur. It could just be that the text calls are related to onDraw operation. But I mention this because what I did see in profiling seems a bit odd. The majority of CPU time during the animation seems to be spent performing text measurement operations, presumably for content in the RecyclerView entries.
There is heavy overdraw occurring. The drawer itself has a large bitmap containing album artwork, And it's possible that I may have stacked up semi-opaque layers and backgrounds. e.g. the RippleDrawable backgrounds on individual entries in the RecyclerView could very well cause a complete overdraw pass for most of the screen. In addition, there is a complete overdraw pass for the view that dims out the background content as the drawer animates into open position. That being said, the drawer animates beautifully when the animation is triggered by clicking the header of the drawer. Just not when the click that starts the animation comes from the RecyclerView. So I don't think overdraw is really the problem.
For what its worth, the location of display ripping seems to be independent of where the selection and press occurs. Clicking first or last entries in the RecyclerView doesn't affect where the display-ripping occurs.
ADB shell dumpsys SurfaceFlinger indicates that hardware composition is being performed on three layers (status bar, navigaton bar, and main activity). So the drawer itself is not using SurfaceFlinger composition (which would, if it were happening at least point in the general direction of what's causing tearing of the display). However, I can't really get the SurfaceFlinger state while the animation is running. It's possible that hardware compositing is being used during the animation. It sure looks like display ripping, and I can't think of why ripping should occur if SurfaceFLinger isn't doing the compositing.
I'm completely mystified as to what the problem might be, or approaches to try and debug this. Any suggestions as to general debugging approach, or suggestions as to what the cause of the problem might would be much appreciated.
On what layer is the described tearing happening? Is it on the drawer itself, or on the layout behind the drawer?
Either way, I have a couple of suggestions:
Overdrawing can be glitchy at times, especially with complicated layouts - I have run into issues with it specifically on GoogleMaps Fragments. Something that resolved this in my case was creating a simple transparent overlay to cast the overdraw on, like so:
<FrameLayout for drawer....>
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/transparent" />
<LinearLayout w/ fragments etc..../>
</FrameLayout>
Drawer Animations are notoriously bad for their intended purpose... they will lag like crazy during layout transitions. I know you said you made sure the drawer animation finished before doing anything, but that is something worth double-checking. I personally put all of my navigation code in the OnDrawerClosed() method for this reason, and use the itemSelected() code to set a flag and close the drawer.
Related
I want to achieve an effect of doing an activity transition where the user can see the previous activity while the transition is happening. As far as I understand, using overridePendingTransition(enterAnim, exitAnim) can achieve this effect, but these animations must be xml files that affect the entire activity (e.g. sliding the entire activity in), but I have a navigational bar that I want to be fixed, so this solution won't work. I also can't make my activities translucent because these animations would ideally be used in the entire app.
What I realized after playing with transitions is that even if you don't specify an activity to be translucent, the Android OS will let you see the previous activity while a transition is happening (e.g. a slide animation on an activity will let you see the previous activity while the slide happens). So I tried setting an "empty transition" by using overridePendingTransition(R.id.keep, R.id.keep) where R.id.keep references an anim xml which is a fade from 1.0 alpha to 1.0 alpha, and doing a view animation in a ValueAnimator in the onCreate of the next activity. The idea is that while the activity transition happens, the OS will let you see the previous activity, and we can take advantage of this time frame of transparency by doing a ValueAnimator on the view of the next activity (after the activity transition happens, the OS won't let you see the previous activity and will show a black screen even if you set the window background colour to be transparent, but the ValueAnimator finishes before that).
The technique I used above works well on Nougat, but it seems for API 23 and below, the UI doesn't change during the empty activity transition even though a ValueAnimator is running, and the ValueAnimator can only update the UI after the activity transition finishes. I didn't really expect this behaviour, so I'm not sure how to fix it.
Admittedly my way of doing view-level animations while being able to see the previous activity is a bit hacky, does anyone know a better way of achieving the effect I want, or some insight into why my technique works on Nougat but not on API below that?
In my app, there are 2 activities. To make the transition seem smooth I animated elements of the first and second activity and disabled transition between the 2 activities. An example of what happens is in the video below:
https://youtu.be/L85HfIUPQuk
The problem as you might see is that, once the animations in the first activity end, there is a period, less than a second but still noticeable, where the screen hangs on the empty white background. Only after that does the second activity and animations start.
The animations are simple alpha and translate effects, nothing fancy.
Any suggestions how to get rid of the hanging period?
You should use Activity Transition.
I think what you need is Shared Elements Transition, since the list from first screen is also on the second screen. Checkout the documentation:
http://developer.android.com/training/material/animations.html
I am trying to figure out how to handle this UI feature where I want to make a ViewPager just appear over everything, scroll through the page items, and then when you press back it goes away. Normally I would just use a full screen dialog for this, but I want to apply a combination fade animation (fade in on appear and fade out on disappear) as well as a scale animation (scale up from nothing to fullscreen on appear and scale down from fullscreen to nothing on disappear).
Dialogs seem to be a lot less animation friendly based on the Dialog and Animation interfaces compared to Views. Then again, I don't know how to make a View just sit on top of everything since that's normally what Dialogs are for. Seems hard to tell which is the lesser evil, but I could potentially benefit more from having a full screen View over everything else. What would I need to do?
Edit: A point brought up by #sound-conception I should make clear, that sadly due to the application context for this feature, making this a separate activity is not an option right now.
You state in your question that the ViewPager will be a full screen view. In that case you could implement the ViewPager as a seperate Activity and use Activity Transitions.
Check out this Google I/O developer video on Activity Transitions.
There's also lots of info on the Developer Website
What I want to achieve is a flip animation when going from activity to activity.
I've seen a recommendation somewhere on SO, that I should use appropriate layout animation in one activity, switch to next one without any animation whatsoever and then execute the second half of the animation in the second activity.
I guess it could work, not tried it yet. But what bothers me is more general aspect - I believe it should be possible to achieve the same effect with activity transition animation, but...
Somewhere (I guess it was SO, but I can't find it now) I've read that during the activity transition animation the background should always be fully covered by the animated activities. I'm not sure why - I can see the background is always black on all my devices, and making it visible during the animation appears to be harmless.
But perhaps it is not guaranteed? Can someone confirm that requirement? Is it officially stated anywhere?
In the Honeycomb sample gallery app, there's a layout that uses a two-fragment setup: one on the left of the screen showing titles, and one on the right showing the selected content. The titles fragment can be hidden with an animation.
During the hiding animation, the app asks the framework to recalculate the layout on every single frame. This way the content-fragment can take up the empty space that the titles-fragment leaves behind while it moves off-screen. This produces a great, dynamic effect, but is terribly inefficient I think.
I have fairly complex layouts, and I'd rather not ask the system to re-layout on every single frame. But I'd like a smooth transition animation like in the sample. Are there any alternative solutions to this problem?
P.s.: Just to be clear, I'm not asking how to do basic fragment-transaction animations. I know those, and AFAIK, those animations can't produce the behaviour found in that sample gallery app (another example would be the Honeycomb Gmail app, it has similar transitions that I'd like to achieve).
You can provide custom animations to the fragment system that do whatever you want. You can move the fragments around, fade them, etc. If these animations do not explicitly or implicitly cause layout (by changing properties that trigger a layout), then you should not get a layout on each animation frame. There maybe still be a layout call at the beginning/end as the fragments are added/removed, but the layout/invalidation process during the animation is up to your animations and what they do.