A fast and efficient open source media management and image loading framework for Android that wraps media decoding, memory and disk caching, and resource pooling into a simple and easy to use interface.
—
Glide's transition and animation system provides smooth visual effects during image loading through a flexible architecture built around the TransitionOptions base class and specialized implementations like DrawableTransitionOptions. The system supports built-in cross-fade effects, custom transitions, and comprehensive animation timing controls for enhanced user experience.
The foundation for all transition configurations in Glide:
public abstract class TransitionOptions<CHILD extends TransitionOptions<CHILD, TranscodeType>, TranscodeType> {
/**
* Disables all transitions for this request
* @return Updated options instance
*/
public abstract CHILD dontTransition();
/**
* Creates a clone of these options
* @return Cloned options instance
*/
public abstract CHILD clone();
/**
* Creates empty transition options with no effects
*/
public static <T> TransitionOptions<?, T> of() {
// Implementation provided by Glide
return null;
}
}Specialized transition options for Drawable resources with built-in cross-fade support:
public class DrawableTransitionOptions extends TransitionOptions<DrawableTransitionOptions, Drawable> {
/**
* Creates cross-fade transition with default duration (300ms)
* @return DrawableTransitionOptions with cross-fade
*/
public static DrawableTransitionOptions withCrossFade() {
// Implementation provided by Glide
return null;
}
/**
* Creates cross-fade transition with custom duration
* @param duration Fade duration in milliseconds
* @return DrawableTransitionOptions with timed cross-fade
*/
public static DrawableTransitionOptions withCrossFade(int duration) {
// Implementation provided by Glide
return null;
}
/**
* Creates cross-fade with custom duration and animation builder
* @param duration Fade duration in milliseconds
* @param animationId Android animation resource ID
* @return DrawableTransitionOptions with custom animation
*/
public static DrawableTransitionOptions withCrossFade(int duration, int animationId) {
// Implementation provided by Glide
return null;
}
/**
* Creates transition with custom transition factory
* @param transitionFactory Factory for creating transitions
* @return DrawableTransitionOptions with custom factory
*/
public static DrawableTransitionOptions with(TransitionFactory<? super Drawable> transitionFactory) {
// Implementation provided by Glide
return null;
}
/**
* Creates transition with no animation effects
* @return DrawableTransitionOptions with transitions disabled
*/
public static DrawableTransitionOptions withNoTransition() {
// Implementation provided by Glide
return null;
}
@Override
public DrawableTransitionOptions clone() {
// Implementation provided by Glide
return null;
}
}Transition options specifically for Bitmap resources:
public class BitmapTransitionOptions extends TransitionOptions<BitmapTransitionOptions, Bitmap> {
/**
* Creates cross-fade transition for bitmaps (default 300ms)
* @return BitmapTransitionOptions with cross-fade
*/
public static BitmapTransitionOptions withCrossFade() {
// Implementation provided by Glide
return null;
}
/**
* Creates cross-fade transition with custom duration
* @param duration Fade duration in milliseconds
* @return BitmapTransitionOptions with timed cross-fade
*/
public static BitmapTransitionOptions withCrossFade(int duration) {
// Implementation provided by Glide
return null;
}
/**
* Creates custom bitmap transition
* @param transitionFactory Factory for bitmap transitions
* @return BitmapTransitionOptions with custom factory
*/
public static BitmapTransitionOptions with(TransitionFactory<? super Bitmap> transitionFactory) {
// Implementation provided by Glide
return null;
}
/**
* Creates no-transition options for bitmaps
* @return BitmapTransitionOptions with no effects
*/
public static BitmapTransitionOptions withNoTransition() {
// Implementation provided by Glide
return null;
}
@Override
public BitmapTransitionOptions clone() {
// Implementation provided by Glide
return null;
}
}Core interface for implementing custom transitions:
public interface Transition<R> {
/**
* Executes the transition animation
* @param current Current resource (may be placeholder)
* @param adapter Adapter for applying the transition
* @return True if transition was applied, false otherwise
*/
boolean transition(R current, ViewAdapter adapter);
/**
* Adapter interface for applying transitions to views
*/
interface ViewAdapter {
/**
* Gets the current drawable from the view
* @return Current drawable or null
*/
Drawable getCurrentDrawable();
/**
* Sets the drawable on the view
* @param drawable Drawable to set
*/
void setDrawable(Drawable drawable);
/**
* Gets the underlying view
* @return View being transitioned
*/
View getView();
}
}Specialized transition interface for view-based animations:
public interface ViewTransition {
/**
* Executes transition on a specific view
* @param view Target view for the transition
* @param adapter Adapter for view operations
* @return True if transition was handled
*/
boolean transition(View view, Transition.ViewAdapter adapter);
/**
* Creates a view transition from an animation resource
* @param animationId Android animation resource ID
* @return ViewTransition using the animation
*/
static ViewTransition withAnimation(int animationId) {
// Implementation provided by Glide
return null;
}
/**
* Creates a view transition with no animation
* @return ViewTransition that immediately sets the drawable
*/
static ViewTransition withNoTransition() {
// Implementation provided by Glide
return null;
}
}Factory interface for creating transition instances:
public interface TransitionFactory<R> {
/**
* Creates a transition for the given data source
* @param dataSource Source of the image data
* @param isFirstResource Whether this is the first resource loaded
* @return Transition instance or null for no transition
*/
Transition<R> build(DataSource dataSource, boolean isFirstResource);
}Apply basic cross-fade effects to image loading:
public class BasicTransitionsActivity extends AppCompatActivity {
public void basicCrossFade() {
// Default cross-fade (300ms)
Glide.with(this)
.load(imageUrl)
.transition(DrawableTransitionOptions.withCrossFade())
.into(imageView);
// Custom duration cross-fade
Glide.with(this)
.load(imageUrl)
.transition(DrawableTransitionOptions.withCrossFade(500))
.into(imageView);
// Bitmap-specific cross-fade
Glide.with(this)
.asBitmap()
.load(imageUrl)
.transition(BitmapTransitionOptions.withCrossFade(400))
.into(imageView);
}
public void noTransition() {
// Disable transitions completely
Glide.with(this)
.load(imageUrl)
.transition(DrawableTransitionOptions.withNoTransition())
.into(imageView);
// Alternative syntax
Glide.with(this)
.load(imageUrl)
.dontAnimate()
.into(imageView);
}
}Use Android animation XML resources for transitions:
public class AnimationResourceActivity extends AppCompatActivity {
public void useAnimationResources() {
// Create animation XML file: res/anim/fade_in.xml
/*
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:fromAlpha="0.0"
android:toAlpha="1.0"
android:duration="600" />
*/
// Apply animation resource
Glide.with(this)
.load(imageUrl)
.transition(DrawableTransitionOptions.withCrossFade(600, R.anim.fade_in))
.into(imageView);
// Scale animation: res/anim/scale_up.xml
/*
<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:fromXScale="0.8"
android:fromYScale="0.8"
android:toXScale="1.0"
android:toYScale="1.0"
android:pivotX="50%"
android:pivotY="50%"
android:duration="400"
android:interpolator="@android:anim/decelerate_interpolator" />
*/
ViewTransition scaleTransition = ViewTransition.withAnimation(R.anim.scale_up);
Glide.with(this)
.load(imageUrl)
.transition(DrawableTransitionOptions.with(
new TransitionFactory<Drawable>() {
@Override
public Transition<Drawable> build(DataSource dataSource, boolean isFirstResource) {
return scaleTransition;
}
}
))
.into(imageView);
}
}Create custom transition effects by implementing the Transition interface:
public class FadeInTransition implements Transition<Drawable> {
@Override
public boolean transition(Drawable current, Transition.ViewAdapter adapter) {
View view = adapter.getView();
// Set initial alpha and drawable
view.setAlpha(0f);
adapter.setDrawable(current);
// Animate fade in
view.animate()
.alpha(1f)
.setDuration(500)
.setInterpolator(new DecelerateInterpolator())
.start();
return true;
}
}
public class CustomTransitionActivity extends AppCompatActivity {
public void useCustomTransition() {
TransitionFactory<Drawable> fadeInFactory = new TransitionFactory<Drawable>() {
@Override
public Transition<Drawable> build(DataSource dataSource, boolean isFirstResource) {
return new FadeInTransition();
}
};
Glide.with(this)
.load(imageUrl)
.transition(DrawableTransitionOptions.with(fadeInFactory))
.into(imageView);
}
}Create complex animations with multiple effects:
public class SlideUpFadeTransition implements Transition<Drawable> {
@Override
public boolean transition(Drawable current, Transition.ViewAdapter adapter) {
View view = adapter.getView();
float originalY = view.getTranslationY();
// Set initial state
view.setAlpha(0f);
view.setTranslationY(view.getHeight() * 0.3f);
adapter.setDrawable(current);
// Create animator set for combined effects
ObjectAnimator fadeAnimator = ObjectAnimator.ofFloat(view, "alpha", 0f, 1f);
ObjectAnimator slideAnimator = ObjectAnimator.ofFloat(view, "translationY", view.getTranslationY(), originalY);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(fadeAnimator, slideAnimator);
animatorSet.setDuration(600);
animatorSet.setInterpolator(new DecelerateInterpolator());
animatorSet.start();
return true;
}
}
public class CrossFadeTransition implements Transition<Drawable> {
private final int duration;
public CrossFadeTransition() {
this.duration = 300;
}
public CrossFadeTransition(int duration) {
this.duration = duration;
}
@Override
public boolean transition(Drawable current, Transition.ViewAdapter adapter) {
Drawable previous = adapter.getCurrentDrawable();
if (previous == null) {
adapter.setDrawable(current);
return false;
}
// Create cross-fade effect
TransitionDrawable crossFadeDrawable = new TransitionDrawable(new Drawable[]{previous, current});
adapter.setDrawable(crossFadeDrawable);
crossFadeDrawable.startTransition(duration);
return true;
}
}Apply different transitions based on data source or conditions:
public class ConditionalTransitionFactory implements TransitionFactory<Drawable> {
@Override
public Transition<Drawable> build(DataSource dataSource, boolean isFirstResource) {
// No transition for cached images (already fast)
if (dataSource == DataSource.MEMORY_CACHE) {
return null;
}
// Slow fade for remote images (user expects loading time)
if (dataSource == DataSource.REMOTE) {
return new CrossFadeTransition(500);
}
// Quick fade for local images
if (dataSource == DataSource.LOCAL) {
return new CrossFadeTransition(200);
}
// Custom transition for first load
if (isFirstResource) {
return new SlideUpFadeTransition();
}
// Default cross-fade
return new CrossFadeTransition();
}
}
public class ConditionalTransitionsActivity extends AppCompatActivity {
public void useConditionalTransitions() {
Glide.with(this)
.load(imageUrl)
.transition(DrawableTransitionOptions.with(new ConditionalTransitionFactory()))
.into(imageView);
}
}Control animation duration and timing:
public class TimingControlActivity extends AppCompatActivity {
public void variableTimingTransitions() {
// Fast transition for thumbnails
RequestOptions thumbnailOptions = new RequestOptions()
.override(100, 100)
.centerCrop();
Glide.with(this)
.load(thumbnailUrl)
.apply(thumbnailOptions)
.transition(DrawableTransitionOptions.withCrossFade(150))
.into(thumbnailView);
// Slow transition for hero images
Glide.with(this)
.load(heroImageUrl)
.transition(DrawableTransitionOptions.withCrossFade(800))
.into(heroImageView);
// No transition for rapid scrolling
if (isRapidScrolling) {
Glide.with(this)
.load(imageUrl)
.dontAnimate()
.into(imageView);
}
}
}Use different interpolators for varied animation feels:
public class InterpolatorTransition implements Transition<Drawable> {
private final long duration;
private final Interpolator interpolator;
public InterpolatorTransition() {
this.duration = 300;
this.interpolator = new AccelerateDecelerateInterpolator();
}
public InterpolatorTransition(long duration, Interpolator interpolator) {
this.duration = duration;
this.interpolator = interpolator;
}
@Override
public boolean transition(Drawable current, Transition.ViewAdapter adapter) {
View view = adapter.getView();
view.setAlpha(0f);
adapter.setDrawable(current);
view.animate()
.alpha(1f)
.setDuration(duration)
.setInterpolator(interpolator)
.start();
return true;
}
}
public class InterpolatorActivity extends AppCompatActivity {
public void useInterpolators() {
// Bouncy animation
TransitionFactory<Drawable> bounceFactory = new TransitionFactory<Drawable>() {
@Override
public Transition<Drawable> build(DataSource dataSource, boolean isFirstResource) {
return new InterpolatorTransition(600, new BounceInterpolator());
}
};
// Overshoot animation
TransitionFactory<Drawable> overshootFactory = new TransitionFactory<Drawable>() {
@Override
public Transition<Drawable> build(DataSource dataSource, boolean isFirstResource) {
return new InterpolatorTransition(500, new OvershootInterpolator());
}
};
Glide.with(this)
.load(imageUrl)
.transition(DrawableTransitionOptions.with(bounceFactory))
.into(imageView);
}
}Set default transitions through GlideModule:
public class GlobalTransitionModule extends AppGlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
// Set default drawable transitions
builder.setDefaultTransitionOptions(
Drawable.class,
DrawableTransitionOptions.withCrossFade(400)
);
// Set default bitmap transitions
builder.setDefaultTransitionOptions(
Bitmap.class,
BitmapTransitionOptions.withCrossFade(300)
);
}
}Create reusable transition configurations:
public class TransitionPresets {
public static final RequestOptions INSTANT = new RequestOptions()
.dontAnimate();
public static final RequestOptions FAST_FADE = new RequestOptions()
.transition(DrawableTransitionOptions.withCrossFade(200));
public static final RequestOptions SLOW_FADE = new RequestOptions()
.transition(DrawableTransitionOptions.withCrossFade(600));
public static final RequestOptions CUSTOM_SLIDE = new RequestOptions()
.transition(DrawableTransitionOptions.with(
new TransitionFactory<Drawable>() {
@Override
public Transition<Drawable> build(DataSource dataSource, boolean isFirstResource) {
return new SlideUpFadeTransition();
}
}
));
}
public class TransitionPresetsActivity extends AppCompatActivity {
public void usePresets() {
// Gallery thumbnails - fast
Glide.with(this)
.load(thumbnailUrl)
.apply(TransitionPresets.FAST_FADE)
.into(thumbnailView);
// Featured image - slow elegant fade
Glide.with(this)
.load(featuredUrl)
.apply(TransitionPresets.SLOW_FADE)
.into(featuredView);
// Profile image - custom slide effect
Glide.with(this)
.load(profileUrl)
.apply(TransitionPresets.CUSTOM_SLIDE)
.into(profileView);
}
}Balance visual appeal with performance:
public class PerformanceOptimizedTransitions extends AppCompatActivity {
public void optimizeForPerformance() {
// Disable transitions during fast scrolling
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
boolean useTransitions = newState != RecyclerView.SCROLL_STATE_SETTLING;
if (useTransitions) {
// Normal transitions when not scrolling
Glide.with(PerformanceOptimizedTransitions.this)
.setDefaultRequestOptions(TransitionPresets.FAST_FADE);
} else {
// No transitions during scroll
Glide.with(PerformanceOptimizedTransitions.this)
.setDefaultRequestOptions(TransitionPresets.INSTANT);
}
}
});
}
public void adaptiveTransitions() {
// Adjust transitions based on device performance
boolean isLowEndDevice = !ActivityManager.isHighPerformanceDevice();
DrawableTransitionOptions transitionOptions;
if (isLowEndDevice) {
// Minimal transitions on low-end devices
transitionOptions = DrawableTransitionOptions.withCrossFade(150);
} else {
// Rich transitions on capable devices
transitionOptions = DrawableTransitionOptions.withCrossFade(400);
}
Glide.with(this)
.load(imageUrl)
.transition(transitionOptions)
.into(imageView);
}
}public class TransitionBestPractices {
// Context-aware transition selection
public static DrawableTransitionOptions getOptimalTransition(Context context, ImageSize imageSize) {
switch (imageSize) {
case THUMBNAIL:
return DrawableTransitionOptions.withCrossFade(150);
case MEDIUM:
return DrawableTransitionOptions.withCrossFade(300);
case LARGE:
return DrawableTransitionOptions.withCrossFade(500);
case HERO:
return DrawableTransitionOptions.withCrossFade(800);
default:
return DrawableTransitionOptions.withCrossFade(300);
}
}
// Scroll-state aware transitions
public static DrawableTransitionOptions getScrollAwareTransition(boolean isScrolling) {
if (isScrolling) {
return DrawableTransitionOptions.withNoTransition();
} else {
return DrawableTransitionOptions.withCrossFade(250);
}
}
}This comprehensive transition system provides the tools needed to create smooth, visually appealing image loading experiences while maintaining optimal performance across different device capabilities and usage patterns.
Install with Tessl CLI
npx tessl i tessl/maven-com-github-bumptech-glide--glide