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 TextView
s 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.