如何在Android上实现流畅的片段转换动画

11

我主要使用iOS,并习惯了非常流畅的屏幕切换动画。现在我正在开发Android应用程序,但是我无论如何都不能使片段事务平滑地添加/替换片段。

我的设置如下:MainActivity有一个FrameLayout用于其xml,我立即将FragmentA加载到MainActivityOnCreate中。然后,我的应用程序继续使用FragmentBFragmentC等替换MainActivityFrameLayout。因此,我的整个应用程序只有1个活动。

在按钮点击时,我调用以下内容来添加/替换当前片段:

        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的内容如下(slide_out.xml显然是相反的):

 <?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>

通过继承LinearLayout,我正在滑入滑出并对xFraction进行动画处理,如下所示:

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;
    }
}

我的问题是,大多数情况下,当我最初单击添加/替换片段的按钮时,它不会有任何动画效果,只是突然出现在那里。然而,更多的情况是,当我按下返回按钮并且片段从后堆栈中弹出时,动画会按预期执行。我觉得这是在尝试执行动画时初始化的问题。当片段在内存中并在屏幕上进行动画处理时,效果要好得多,而当创建新的片段然后将其动画化到屏幕上时,则效果要差得多。我想知道其他人是否也遇到了这个问题,如果遇到了,他们是如何解决的。

这是一个一直困扰着我的Android开发体验的麻烦事!任何帮助都将不胜感激。谢谢!


最近我一直遇到同样的问题。第一次转换运行时,它有点卡顿。我发现消除不必要的 alpha 有所帮助,但并不能完全解决问题。 - Eliezer
1个回答

6

根据我的经验,如果片段在加载时需要执行过多的工作,则系统基本上会跳过进入动画并直接显示片段。对于我来说,解决方案是在onCreateAnimation中创建一个动画监听器,等待进入动画完成后再执行导致动画被跳过的耗费大量资源的工作。

@Override
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
    if (nextAnim == 0) {
        return super.onCreateAnimation(transit, enter, nextAnim);
    }

    Animation anim = android.view.animation.AnimationUtils.loadAnimation(getContext(), nextAnim);
    anim.setAnimationListener(new Animation.AnimationListener() {
        @Override
        public void onAnimationStart(Animation animation) {}
        @Override
        public void onAnimationEnd(Animation animation) {
            // Do any process intensive work that can wait until after fragment has loaded
        }

        @Override
        public void onAnimationRepeat(Animation animation) {}
    });
    return anim;
}

它会产生一个错误:java.lang.RuntimeException: 未知的动画名称:objectAnimator(当声明anim变量时)。 我正在使用Support Library Fragment。 - quangkid

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接