I've mostly worked with iOS and have become accustomed to very smooth and fluent screen change animations. I am now working on an Android app and can't for the life of me get a fragment transaction to smoothly add/replace a fragment.
My set up is as follows: MainActivity
has a FrameLayout
for its xml which I immediately load FragmentA
into on the MainActivity
's OnCreate
. My app then proceeds to replace the MainActivity
's FrameLayout
with FragmentB
, FragmentC
, ect. So my entire app has 1 activity.
On button clicks I call the following to add/replace the current fragment:
getFragmentManager()
.beginTransaction()
.setCustomAnimations(R.animator.slide_in, android.R.animator.fade_out, android.R.animator.fade_in, R.animator.slide_out)
.addToBackStack(null)
.add(R.id.fragment_1, fragment)
.commit();
slide_in.xml looks like (slide_out.xml is obviously the opposite):
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:interpolator="@android:interpolator/linear"
android:propertyName="xFraction"
android:valueType="floatType"
android:valueFrom="1.0"
android:valueTo="0"
android:duration="@android:integer/config_shortAnimTime"
/>
</set>
Sliding in and out I'm animating xFraction which I've subclassed LinearLayout
to do like so:
public class SlidingLinearLayout extends LinearLayout {
private float yFraction = 0;
private float xFraction = 0;
public SlidingLinearLayout(Context context) {
super(context);
}
public SlidingLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SlidingLinearLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
private ViewTreeObserver.OnPreDrawListener preDrawListener = null;
public void setYFraction(float fraction) {
this.yFraction = fraction;
if (getHeight() == 0) {
if (preDrawListener == null) {
preDrawListener = new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
getViewTreeObserver().removeOnPreDrawListener(preDrawListener);
setYFraction(yFraction);
return true;
}
};
getViewTreeObserver().addOnPreDrawListener(preDrawListener);
}
return;
}
float translationY = getHeight() * fraction;
setTranslationY(translationY);
}
public float getYFraction() {
return this.yFraction;
}
public void setXFraction(float fraction) {
this.xFraction = fraction;
if (getWidth() == 0) {
if (preDrawListener == null) {
preDrawListener = new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
getViewTreeObserver().removeOnPreDrawListener(preDrawListener);
setXFraction(xFraction);
return true;
}
};
getViewTreeObserver().addOnPreDrawListener(preDrawListener);
}
return;
}
float translationX = getWidth() * fraction;
setTranslationX(translationX);
}
public float getxFraction() {
return xFraction;
}
}
So my issue is most of the time when I initially click a button to add/replace a fragment, I don't get an animation it just sort of pops into place. However more times than not when I press the back button and the fragment pops from the backstack the animation executes as expected. I feel like it is an issue with initialization while trying to animate. When the fragment is in memory and animated off screen it does so much better than when a new fragment is created then animated on to the screen. I'm curious if others have experienced this and if so how they resolved it.
This is an annoyance that has plagued my Android development experience that I'd really like to put to rest! Any help would be greatly appreciated. Thanks!