MotionLayout

MotionLayout: Visibility

MotionLayout is a wonderful tool for creating complex layout animations. However it does have some foibles which are easy enough to work around, but can initially be baffling. In this post we’ll look at some oddities concerning the visibility of views within the layout.

For this particular issue, the setup will actually take much longer than the fix, but please bear with me because it is important to understand that the initial MotionLayout XML initially looks correct yet doesn’t give us the behaviour that we might expect.

We’ll begin by setting up a very simple ConstraintLayout consisting of two views – a TextView and a MaterialSwitch:



    

    

The TextView has default visibility of GONE and we wire ip the switch to toggle the visibility:

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.switch1.setOnCheckedChangeListener { _, isChecked ->
            binding.textView.visibility = if (isChecked) View.VISIBLE else View.GONE
        }
    }
}

This gives us a behaviour of the switch toggling the visibility of the TextView:

So far so good. We now convert the ConstraintLayout to MotionLayout:



    

    

Then we create the corresponding MotionScene XML:



    

    
        
            
            
        
    

    

The MotionScene actually does nothing because we never trigger the transition. I have included a Constraint for the TextView which specifies an initial state. We get exactly the same behaviour if we omit it – but I just wanted to be clear that including it does not avoid the issue we’re about to see.

Given that MotionLayout is a subclass of ConstraintLayout it would seem to be a reasonable assumption that we’ll get exactly the same behaviour as before, but this is not the case:

The visibility control of our TextView no longer works. The reason for this is that MotionLayout actually takes control of various aspects of the layout in order to correctly manipulate things when it comes to running a MotionLayout transition. That is what has broken our programmatic control of the TextViews visibility. To overcome this we need to tell MotionLayout that we want to control the visibility of this particular control, and there’s an attribute that we can set to do precisely that:



    

    
        
            
            
        
    

    

visibilityMode controls whether MotionLayout will take control of the visibility of this specific View – the default value of normal means that MotionLayout has control, but if we set this to ignore it means that we have control. Making this one small change gives us the behaviour that we had before:

Although that was a fairly long winded setup for what is effectively a one-line fix, it is important to know that MotionLayout alters some of the fundamental behaviours of static layouts including ConstraintLayout. While it is easy enough to work around, the first time I encountered this issue I was scratching my head for a while before I understood what was happening and how to overcome it.

The source code for this article is available here.

© 2020, Mark Allison. All rights reserved.

Copyright © 2020 Styling Android. All Rights Reserved.
Information about how to reuse or republish this work may be available at http://blog.stylingandroid.com/license-information.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.