0

I have a scene transition and I want to use Recolor animation to change background of a ViewGroup from transparent to another color. Here is my login_to_register.xml:

<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
   <fade android:fadingMode="fade_out"/>
   <fade android:fadingMode="fade_in">
      <targets>
         <target android:targetId="@id/passwordEditor" />
         <target android:targetId="@id/loginNameEditor" />
      </targets>
   </fade>
   <!--<changeTransform />-->
   <changeBounds />
   <recolor>
      <targets>
         <target android:targetId="@id/loginOptions" />
      </targets>
   </recolor>
</transitionSet>

This is the XML for the layout of the fragment, which is inside the ViewPager.

<FrameLayout
      android:layout_width="match_parent" android:layout_height="match_parent">

      <ImageView
         android:layout_width="match_parent" android:layout_height="match_parent"
         android:scaleType="centerCrop"
         android:src="@drawable/onboarding_bg"
         />

      <LinearLayout
         android:id="@+id/loginOptions"
         android:orientation="vertical"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_marginLeft="@dimen/activity_horizontal_margin"
         android:layout_marginRight="@dimen/activity_horizontal_margin"
         android:background="@android:color/transparent"
         android:layout_gravity="bottom"
         android:gravity="center_horizontal"
         >
         <android.support.design.widget.TextInputLayout
            android:id="@+id/loginNameEditor"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="@string/email"
            app:errorEnabled="true"
            app:hintTextAppearance="@style/BlackFloatingTextTextAppearance"
            android:visibility="gone"
            tools:visibility="visible"
            >
            <android.support.design.widget.TextInputEditText
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
               android:text="@={regModel.emailAddress, default=`twinkle`}"
               android:inputType="textEmailAddress"
               android:maxLength="254"
               android:onFocusChange="@{(v, focus) -> context.loginFocusChanged(true, focus)}"
               android:afterTextChanged="@{() -> context.loginTextChanged()}"
               />
         </android.support.design.widget.TextInputLayout>

         <android.support.design.widget.TextInputLayout
            android:id="@+id/passwordEditor"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="@string/password"
            app:passwordToggleEnabled="true"
            app:errorEnabled="true"
            app:hintTextAppearance="@style/BlackFloatingTextTextAppearance"
            app:counterEnabled="true"
            app:counterMaxLength="50"
            android:visibility="gone"
            tools:visibility="visible"
            >
            <android.support.design.widget.TextInputEditText
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
               android:text="@={regModel.password}"
               android:inputType="textPassword"
               android:fontFamily="monospace"
               android:onFocusChange="@{(v, focus) -> context.loginFocusChanged(false, focus)}"
               android:afterTextChanged="@{() -> context.loginTextChanged()}"
               />
         </android.support.design.widget.TextInputLayout>

         <TextView
            android:id="@+id/error"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingStart="4dp"
            android:paddingEnd="4dp"
            android:textAppearance="@style/TextAppearance.Design.Error"
            android:gravity="start"
            android:visibility="gone"
            tools:visibility="visible"
            />

         <!--<Button-->
         <!--android:id="emailAuthButton"-->
         <!--android:layout_width="wrap_content"-->
         <!--android:layout_height="wrap_content"-->
         <!--android:layout_marginTop="@dimen/loginButtonSpacing"-->
         <!--android:text="@string/login"-->
         <!--style="@style/PrimaryColorButtonBorderlessStyle"-->
         <!--android:onClick="loginEmail"-->
         <!--/>-->

         <com.google.android.gms.common.SignInButton
            android:id="@+id/googButton"
            android:layout_width="230dp"
            android:layout_height="wrap_content"
            android:layout_marginBottom="@dimen/loginButtonSpacing"
            android:layout_marginTop="@dimen/loginButtonSpacing"
            android:onClick="loginGoogle"
            app:buttonSize="wide"
            />

         <com.facebook.login.widget.LoginButton
            android:id="@+id/fbButton"
            android:layout_width="220dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="@dimen/loginButtonSpacing"
            app:com_facebook_login_text="@string/com_facebook_loginview_log_in_button"
            android:layout_marginBottom="@dimen/loginButtonSpacing"
            android:paddingTop="12dp"
            android:paddingBottom="12dp"
            android:onClick="loginFB"
            />

         <TextView
            android:id="@+id/emailInfo"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="12sp"
            android:layout_marginTop="@dimen/register_row_spacing"
            android:layout_marginStart="10dp"
            android:text="@string/unauth_email_prompt"
            android:textColor="?android:attr/textColorPrimaryInverse"
            />

         <Button
            android:id="@+id/unfederatedButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="@dimen/loginButtonSpacing"
            android:layout_marginBottom="@dimen/loginButtonSpacing"
            android:text="@string/create_account"
            style="@style/ScalableBorderlessButton"
            android:textColor="@color/colorAccent"
            android:minHeight="53dp"
            android:onClick="showUnfederatedUI"
            />
         <TextView
            android:id="@+id/forgotPassword"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="start"
            android:layout_marginTop="15dp"
            android:paddingTop="@dimen/smallLinkTopPadding"
            android:paddingBottom="@dimen/smallLinkBottomPadding"
            android:paddingLeft="@dimen/smallLinkHPadding"
            android:paddingRight="@dimen/smallLinkHPadding"
            app:hypertext="@{R.string.forgot_pass}"
            android:background="?android:attr/selectableItemBackground"
            tools:text="@string/forgot_pass"
            android:textSize="@dimen/smallLinkTextSize"
            android:onClick="recoverPassword"
            android:visibility="gone"
         />
      </LinearLayout>
   </FrameLayout>

Now the view that I want to use Recolor is the id loginOptions. You can see that I am giving its specific targetId in the transition. And it is defaulting to transparent in case there are any issues with starting from null. So here is the code to start the scene transition

    val trans = TransitionInflater.from(this).inflateTransition(R.transition.login_to_register)
    TransitionManager.beginDelayedTransition(this.dataBinding.root, trans)
    this.loginBinding!!.loginNameEditor.visibility = View.VISIBLE
    this.loginBinding!!.passwordEditor.visibility = View.VISIBLE
    this.loginBinding!!.error.visibility = View.VISIBLE
    this.loginBinding!!.googButton.visibility = View.GONE
    this.loginBinding!!.fbButton.visibility = View.GONE
    this.loginBinding!!.emailInfo.visibility = View.GONE
    this.loginBinding!!.forgotPassword.visibility = View.VISIBLE

    val color = this.resources.getColor(R.color.almostWhite)
    this.loginBinding!!.loginOptions.background = ColorDrawable(color)

I am not including the activity layout of the ViewPager, it is straightforward, and probably doesn't have any issues, because all the other transitions work! Yes, all the VISIBILITY changes are working properly, and the bounds changes. What's wrong with the Recolor? Edit: Let me add that, if I remove <recolor>, the background of @id/loginOptions does change to almostWhite.

androidguy
  • 3,005
  • 2
  • 27
  • 38

2 Answers2

0

This reported to Google issue tracker,there has a solution that works for me, which is why I'm accepting this.

To use a custom transition in a transition xml do :

<transition 
        class="com.example.BackgroundRecolor" 
        android:duration="200"/> 

Add constructors from Transition in your custom transition to support xml attributes :

public BackgroundRecolor() {} 

public BackgroundRecolor(Context context, AttributeSet attrs) { 
    super(context, attrs); 
}

[Update] The code file on the issuetracker is "deleted", here is the full contents of this code as it is in my project:

public class BackgroundRecolor extends Transition {
    public BackgroundRecolor() {}

    public BackgroundRecolor(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    private static final String PROPNAME_BACKGROUND = "com.example:recolor:background";

    private void captureValues(TransitionValues transitionValues) {
        Drawable bg = transitionValues.view.getBackground();
        if (bg != null && bg instanceof ColorDrawable) {
            transitionValues.values.put(PROPNAME_BACKGROUND, ((ColorDrawable) bg).getColor());
        }
    }

    @Override
    public void captureStartValues(TransitionValues transitionValues) {
        captureValues(transitionValues);
    }

    @Override
    public void captureEndValues(TransitionValues transitionValues) {
        captureValues(transitionValues);
    }

    @Override
    public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) {
        if (startValues == null || endValues == null) {
            return null;
        }

        if (startValues.values.get(PROPNAME_BACKGROUND) == null || !(startValues.values.get(PROPNAME_BACKGROUND) instanceof Integer))
            return null;
        if (endValues.values.get(PROPNAME_BACKGROUND) == null || !(endValues.values.get(PROPNAME_BACKGROUND) instanceof Integer))
            return null;

        int colorStart = (int) startValues.values.get(PROPNAME_BACKGROUND);
        int colorEnd = (int) endValues.values.get(PROPNAME_BACKGROUND);

        Drawable bg = endValues.view.getBackground();
        if (!(bg instanceof ColorDrawable))
            return null;

        ColorDrawable bgc = (ColorDrawable) bg;
        bgc.setColor(colorStart);
        return ObjectAnimator.ofObject(bgc, "color", new ArgbEvaluator(), colorStart, colorEnd);
    }

}
androidguy
  • 3,005
  • 2
  • 27
  • 38
0

I’m looking at the Recolor source I found here and it appears that it only captures values for TextViews. I don’t know if this is the latest version, I can’t test it now.

Alternatively, try the (excellent) TransitionsEverywhere library found here, I’ve tested both side by side (Android Transitions vs. Library) and they both perform the same. Worth a try, since all you have to do is change the namespace :) (Or you can even create your own “recolor” version that works for what you want, by looking at the existing source code.

UPDATE: By looking at more recent source code, (and even TransitionsEverywhere version of Recolor) it does seem that it now captures the view’s Background. (Source)

It does the same as the Android native version:

transitionValues.values.put(PROPNAME_BACKGROUND,
                            transitionValues.view.getBackground());

Perhaps you can try with a TransitionDrawable? See this StackOverflow question/answer combo.

Martin Marconcini
  • 26,875
  • 19
  • 106
  • 144
  • I think that reading of the framework Recolor is inaccurate: there is a check for 2 ColorDrawables. There is an explanation for why it doesn't work on the issue link in my answer. – androidguy Jul 14 '17 at 19:29