In part 4 of the series we explored combining Shared Element Transitions with RecyclerView.

In part 5 we're going to fix the white flashing or blinking that you may have seen throughout the series during the transition from screen to screen.

This part was going to be longer but I was slowed down by what I think is a bug that I found on my Nexus 6P on 7.1.2. My advice, therefore, for this part, is to make sure the device you're using is NOT on 7.1.2. Just in case you run into the same issues I did.

Going back to the start

"What flashing and blinking?" I might hear you say. Take a look at this video and pay attention to the status bar, navigation bar and action bar.

In part 1 I mentioned that Shared Element Transitions take part in the windows ViewOverlay. This ViewOverlay sits on top of everything including the status bar and the navigation bar. It is also where the Shared Element Transitions take place. So when we do our Shared Element Transition, the first and second window are animating (typically a fade) and that's why we get the white flashing; the alpha is animating out on the first screen and animating in on the second screen. So how do we get around this? We need to exclude the views we don't want animating during the Shared Element Transition by excluding them from the window transition.

For the next few examples I've added in a Toolbar as I believe a lot of you will be using one. It's something else that needs to be taken into consideration as well.

Fixing Flashing with Activities

Programmatic

Our ProgrammaticActivityA is setup exactly the same way that the other Activity example have been in regards to Shared Element Transitions so head back and take a look if you haven't already. The key part is this next piece of code:

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_flash_fix_a);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Fade fade = new Fade();
            fade.excludeTarget(R.id.appBar, true);
            fade.excludeTarget(android.R.id.statusBarBackground, true);
            fade.excludeTarget(android.R.id.navigationBarBackground, true);

            getWindow().setEnterTransition(fade);
            getWindow().setExitTransition(fade);
        }

        //Button setup
    }

Line 8 we do our obligatory check to make sure we're on 21+. Line 9 we create a Fade Transition (Note it can be any type of Transition you like). It's this Transition where we're going to add the views that we want to exclude. First up on line 10 is the AppBarLayout that contains our Toolbar. We call excludeTarget on the Fade which can take a String, View, Class or the id of the view as it's first parameter. We're going to be using the id. If you add the id of the Toolbar instead of the AppBarLayout then it won't work. See Nick Butcher's comment for why this is the case. The second parameter it takes is a boolean which sets whether we want to exclude it or not. We're going to be setting this to true on all our calls as we want to exclude them.

Line 11 and 12 we add the id for the statusBarBackground and the navigationBarBackground by accessing the android resources. Again, we set the second parameter to true.

Finally we set the Fade as the enter transition and exit transition for our ProgrammaticActivityA.

ProgrammaticActivityB has the exact same code as above, so I won't repeat it here. But you can go see this for yourself in the source code. And that's all that's needed when doing it programmatically.

XML

We can also solve this problem by creating a transition in XML and applying that through a theme to our activities. So first we need to add aTransition into a transition package in our resources directory.

<fade xmlns:android="http://schemas.android.com/apk/res/android">  
    <targets>
        <target android:excludeId="@android:id/statusBarBackground" />
        <target android:excludeId="@android:id/navigationBarBackground" />
        <target android:excludeId="@id/appBar" />
    </targets>
</fade>  

Here in this default_window_fade XML we have a Fade Transition. Within this we add our targets for exclusion, much like the above programmatic example.

Next we need to create a theme to apply the default_window_fade to our Activity themes. In the styles.xml we add the following:

    <style name="AppTheme.NoActionBar.DefaultActivity">
        <item name="android:windowEnterTransition">@transition/default_window_fade</item>
        <item name="android:windowExitTransition">@transition/default_window_fade</item>
    </style>

Here we're simply setting the enter and exit Transition of the window to be our default_window_fade.

Lastly we need to set this as the theme for our FlashFixActivityA and FlashFixActivityB in the manifest.

        <activity
            android:name=".flash_fix.xml_activity.FlashFixXmlActivityA"
            android:theme="@style/AppTheme.NoActionBar.DefaultActivity" />
        <activity
            android:name=".flash_fix.xml_activity.FlashFixXmlActivityB"
            android:theme="@style/AppTheme.NoActionBar.DefaultActivity" />

And that's all that's needed. Again, the code for the Activity looks identical to those used in a previous post.

Fragment doesn't seem to suffer the same issues as Activity when it come to the blinking and flashing, at least with the status and navigation bar. I believe this is likely due to it's ViewOverlay being confined to the content area of the holding Activity. If this does occur for you please let me know and provide an example. I believe that if it is a problem for you it may be remedied by supplying an XML Transition like above as part of the FragmentTransaction

With all this done the status bar and navigation bar should no longer flash/blink!

Source code can be found here under the flash_fix package.

This is obviously applicable to the RecyclerView examples in part 4, but I'll leave it as an exercise to you. It should be easy to implement with the above code.

I want to move into more complex examples, so my next post may not be out for a few weeks. If there's anything you want to see specifically and you think it can be of use to a lot of people then please let me know.