Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save guilhermesgb/2db3efd5122fe6eaad1b749e24fe6120 to your computer and use it in GitHub Desktop.
Save guilhermesgb/2db3efd5122fe6eaad1b749e24fe6120 to your computer and use it in GitHub Desktop.
TransitionManager transition for animating CollapsingToolbarLayout's contentScrim color changes
package com.mcontigo.unycos.utils;
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.animation.TypeEvaluator;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
import android.support.design.widget.CollapsingToolbarLayout;
import android.support.transition.Transition;
import android.support.transition.TransitionValues;
import android.util.Property;
import android.view.View;
import android.view.ViewGroup;
//Usage:
//
// Consider you have a plain regular CoordinatorLayout -> AppBarLayout -> CollapsingToolbarLayout hiearchy in your XML layout.
//
// Example goes:
//
// <android.support.design.widget.CoordinatorLayout>
//
// <android.support.design.widget.AppBarLayout
// android:id="@+id/appBar">
//
// <android.support.design.widget.CollapsingToolbarLayout
// app:contentScrim="@color/someColor">
//
// <ImageView
// android:id="@+id/backgroundImage"
// android:layout_width="match_parent"
// android:layout_height="match_parent"
// android:src="@drawable/someImage"
// android:scaleType="centerCrop"
// android:contentDescription="@null"/>
//
// </android.support.design.widget.CollapsingToolbarLayout>
//
// </android.support.design.widget.AppBarLayout>
//
// </android.support.design.widget.CoordinatorLayout>
//
//
// Now let's suppose that, often, you have to programmatically change the CollapsingToolbarLayout contentScrim
// as well as and its child ImageView src (common use case: whenever the user moves from one TabLayout tab to another).
//
// You would have some code loading another image into the imageView alongside some code for changing
// the contentScrimColor of the collapsingToolbarLayout.
//
// So, if you do have a parallax background image, make sure you have your commands for loading
// another image come first, before your code for setting a new content scrim into the collapsingToolbarLayout!
//
// Now it's time to schedule our transition: precede your content scrim setter call by a "beginDelayedTransition" operation,
// precisely using this method below:
//
// `TransitionManager.beginDelayedTransition(View, Transition)`,
//
// passing the parent AppBarLayout view as the transition target and an instance of this class below as the actual transition.
//
// Then, in your code, simply set a new content_scrim to the CollapsingToolbarLayout via the some of the available choices below:
//
// `CollapsingToolbarLayout.contentScrimColor(@ColorInt int)`,
//
// `CollapsingToolbarLayout.setContentScrim(Drawable)` or
//
// `CollapsingToolbarLayout.setContentScrimResource(@DrawableRes int)`
//
// and that's it!
// You'll notice the CollapsingToolbarLayout smoothly animating a transition from the old contentScrim into the new one,
// whenever your view is collapsed. When you decide to swap content scrims while the view is fully expanded (which means
// the underlying imageView is visible), this transition object will actually skip the transition, properly setting the
// new content scrim directly and you will only be able to notice the change later when you collapse your view again.
//
//
// For example:
//
// //Some code for loading a new image into the collapsingToolbarLayout's underlying imageView (with parallax effect enabled).
// TransitionManager.beginDelayedTransition(appBarLayout, new CollapsingToolbarContentScrimTransition());
// collapsingToolbarLayout.setContentScrim(new ColorDrawable(resolvedColorInt));
//
//
public class CollapsingToolbarContentScrimTransition extends Transition {
private final String PROPNAME_CONTENT_SCRIM = "app:recolor:contentScrim";
private void captureValues(@NonNull TransitionValues transitionValues) {
if (transitionValues.view instanceof CollapsingToolbarLayout) {
transitionValues.values.put(PROPNAME_CONTENT_SCRIM,
((CollapsingToolbarLayout) transitionValues.view).getContentScrim());
}
}
@Override
public void captureStartValues(@NonNull TransitionValues transitionValues) {
captureValues(transitionValues);
}
@Override
public void captureEndValues(@NonNull TransitionValues transitionValues) {
captureValues(transitionValues);
}
@Override
public Animator createAnimator(@NonNull ViewGroup sceneRoot,
TransitionValues startValues, TransitionValues endValues) {
if (startValues == null || endValues == null) {
return null;
}
Drawable startContentScrim = (Drawable) startValues.values.get(PROPNAME_CONTENT_SCRIM);
Drawable endContentScrim = (Drawable) endValues.values.get(PROPNAME_CONTENT_SCRIM);
View view = endValues.view;
//Skipping any views that aren't CollapsingToolbarLayouts since they're not supported.
//
//Also skipping CollapsingToolbarLayouts if they're provided Drawables that aren't plain colors since the
// transition animation is implemented by means of interpolating RGB colors (you might want to replace this
// by a better color interpolator by writing another `evaluate` method).
//
//Also skipping CollapsingToolbarLayouts that have been passed transparent colors because otherwise, this could mess up
// with the CollapsingToolbarLayout's child image view which is meant to be shown only while the view is expanded,
// making it visible also while the CollapsingToolbarLayout is in its collapsed state.
// Since whenever the underlying image view is meant to be shown, that's actually achieved internally by the
// CollapsingToolbarLayout by setting its contentScrim color alpha to zero, I use that as a way to determine whether the
// CollapsingToolbarLayout is in its collapsed or expanded state so that I can skip the transition in the latter case.
if (view instanceof CollapsingToolbarLayout
&& startContentScrim instanceof ColorDrawable
&& endContentScrim instanceof ColorDrawable
&& isFullyOpaque(((ColorDrawable) endContentScrim).getColor())) {
ColorDrawable startColor = (ColorDrawable) startContentScrim;
ColorDrawable endColor = (ColorDrawable) endContentScrim;
//Also there's no need to animate if the colors won't end up changing anyways.
if (startColor.getColor() != endColor.getColor()) {
final CollapsingToolbarLayout collapsingToolbarLayout = (CollapsingToolbarLayout) view;
return ObjectAnimator.ofObject(collapsingToolbarLayout,
Property.of(CollapsingToolbarLayout.class, Drawable.class, "contentScrim"),
new TypeEvaluator<Drawable>() {
//The RGB color interpolation needed by the transition animation is being done below.
@Override
public Drawable evaluate(float fraction,
Drawable startValue, Drawable endValue) {
int sColor = ((ColorDrawable) startValue).getColor();
float sR = ((sColor >> 16) & 0xff) / 255.0f;
float sG = ((sColor >> 8) & 0xff) / 255.0f;
float sB = ((sColor ) & 0xff) / 255.0f;
float sA = ((sColor >> 24) & 0xff) / 255.0f;
int eColor = ((ColorDrawable) endValue).getColor();
float eR = ((eColor >> 16) & 0xff) / 255.0f;
float eG = ((eColor >> 8) & 0xff) / 255.0f;
float eB = ((eColor ) & 0xff) / 255.0f;
float eA = ((eColor >> 24) & 0xff) / 255.0f;
float tR = sR + (eR - sR) * fraction;
float tG = sG + (eG - sG) * fraction;
float tB = sB + (eB - sB) * fraction;
float tA = sA + (eA - sA) * fraction;
return new ColorDrawable(Color.argb(
(int) (tA * 255),
(int) (tR * 255),
(int) (tG * 255),
(int) (tB * 255)
));
}
}, startColor, endColor);
}
}
return null;
}
//Checking if the alpha channel of the color is totally max.
private boolean isFullyOpaque(int color) {
return ((color >> 24) & 0xff) / 255.0f >= 1;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment