6

I'm using implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta1', and have databinding enabled.

When my view

<ImageView
            android:id="@+id/im_lightning"
            android:layout_width="24dp"
            android:layout_height="24dp"
            android:layout_marginTop="24dp"
            android:layout_marginEnd="4dp"
            android:contentDescription="@string/charging"
            android:src="@drawable/ic_lightning"
            android:visibility="@{batteryViewModel.liveData.charging ? View.VISIBLE : View.GONE}"
            app:layout_constraintBottom_toBottomOf="@id/tv_percent"
            app:layout_constraintRight_toLeftOf="@id/tv_percent"
            app:layout_constraintTop_toTopOf="@id/tv_percent"
            app:layout_constraintVertical_bias="1.0" />

is wrapped around a coordinator layout, its visibility changes occur as expected. As soon as I wrap it in a MotionLayout, the visibility changes don't work as before. To be precise, the view isn't visible when it should be. It becomes visible for a second upon triggering an event and then goes back to invisible. Is this a known bug?

The code in case it's needed:

Coordinator Layout:

<androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/colorBackground">
        <ImageView (same as above) />
</androidx.constraintlayout.widget.ConstraintLayout>

Motion Layout:

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".ui.MainActivity">

    <androidx.constraintlayout.motion.widget.MotionLayout
        android:id="@+id/motion_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/colorBackground"
        app:layoutDescription="@xml/home_scene_0"
        app:showPaths="true">

        <ImageView (same as above) />
    </androidx.constraintlayout.motion.widget.MotionLayout>
    <data>
        <import type="android.view.View" />
        <variable
            name="batteryViewModel"
            type="rish.crearo.minimalphone.viewmodels.BatteryViewModel" />
    </data>
</layout>
Crearo Rotar
  • 559
  • 7
  • 23
  • 1
    @tim-castelijns What really bothers me about SO - to the point where I just dislike posting questions here - is questions are marked duplicate / off-topic yada yada without an explanation or providing a link to the duplicate question. I appreciate you taking the time to mark this question, but without feedback, there is no way I will be a better question-asker the next time I post on StackOverflow. Also, I've looked for this again, I can't seem to find this question anywhere. – Crearo Rotar Sep 16 '19 at 14:13
  • 1
    not sure what kind of feedback you expect other than "please check out the linked question". The question is not bad, no need to improve it. Motion layoout controls the visibility of it's children. It seemed like the issue here is the same as there, hence the close. If you tell me the answer in the linked question doesn't work for you I will happily reopen your question – Tim Sep 16 '19 at 14:21
  • @tim-castelijns I apologize. Between the link at the top of the question, and the marked as duplicate at the bottom, I missed seeing the link. I jumped the gun! The linked question works for me. – Crearo Rotar Sep 16 '19 at 21:17

1 Answers1

4

I encounter the same problem. Its seem that if you are manipulating the same view's attributes (in this case the visibility) in the databinding & motion scene, the motion scene is superior to the binded value, so you need to bind the value on the scene.

TL;DR Bind the view's attribute motion scene's constraintSet (and not the layout.xml)

I used @BindingAdapter in order to achieve this.

@BindingAdapter("goneUnlessOnMotionLayout")
public static void goneUnlessOnMotionLayout(View view, boolean visible) {
  if (view.getParent() instanceof MotionLayout) {
        final MotionLayout layout = (MotionLayout) view.getParent();
        final int setToVisibility = visible ? View.VISIBLE : View.GONE;
        final float setToAlpha = visible ? 1f : 0f;

        for (int constraintId : layout.getConstraintSetIds()) {
            final ConstraintSet constraint = layout.getConstraintSet(constraintId);
            if (constraint != null) {
                constraint.setVisibility(view.getId(), setToVisibility);
                constraint.setAlpha(view.getId(), setToAlpha);
            }
        }
    }
}

pay attention not to notifyPropertyChanged in your view model while motionLayout is in progress i.e motionLayout.progress != 0 or 1, it will cause a break of the natural motion between the constraints

Idan
  • 75
  • 2
  • 6
  • 1
    Looks promising. I'll try it out and mark correct if it works! – Crearo Rotar Sep 16 '19 at 14:14
  • 3
    The answer at [this SO question](https://stackoverflow.com/a/57216827/6844926) works. I suppose it is _more correct_ in that motion layout takes control of all views inside it, and so you must specify it in the constraints. – Crearo Rotar Sep 16 '19 at 21:17
  • Thanks for the solution @Idan. I added a `constraint.applyTo(layout)` to your solution otherwise it didn't get the value applied until I swiped. I also skipped the setAlpha part. – Tobias Lindberg Apr 21 '20 at 22:11
  • Thanks, the solution works perfectly, it wasn't working properly for me first but I figured out that I was setting `app:visibilityMode="ignore"` for the start /end constraints which was preventing the visibility from being set properly. Thanks again. – Zakaria Boukaddouss May 29 '20 at 06:11
  • Hi @Idan, How do u use the goneUnlessOnMotionLayout in your motion scene? – Sharas Mar 17 '21 at 16:50