为什么我的 Android 转换会忽略 TransitionListener?

Why does my Android transition ignore the TransitionListener?

我正在尝试通过在更新显示之前调用 TransitionManager.beginDelayedTransition(viewGroup, new AutoTransition()) 来平滑更新显示。但我发现有时我快速更新显示并且 Android 感到困惑。

所以我在 AutoTransition 中添加了一个 TransitionListener,其中 transitionEnd() 回调,表示转换已完成,此时我将动画设置为屏幕的新状态。但是我发现通常连这个都不叫。

有什么方法可以在 a) 转换结束或 b) 转换一开始就没有被调用时回调回调?


更多信息:

我正在使用 Xamarin.Android。我有一段代码看起来像这样启动转换:

if (animated && viewGroup != null && Utils.Api19PlusKitkat) {
    App.Log("** Beginning animation.");
    var transition = new AutoTransition();
    transition.AddListener(new TransitionListener(this));
    TransitionManager.BeginDelayedTransition(viewGroup, transition);
}

TransitionListener 非常简单:

public class TransitionListener : Java.Lang.Object, Transition.ITransitionListener {
    readonly TreeNode owner;

    public TransitionListener(TreeNode owner)
    {
        this.owner = owner;
    }

    public void OnTransitionEnd(Transition transition)
    {
        App.Log("**** TransitionListener: Transition End.");
        owner.FinishTransition();
    }

    public void OnTransitionCancel(Transition transition) {}
    public void OnTransitionPause(Transition transition) {}
    public void OnTransitionResume(Transition transition) {}
    public void OnTransitionStart(Transition transition) {
        App.Log("**** TransitionListener: Transition Start.");
    }
}

当我查看我的应用程序日志时,我看到了一堆 "Beginning animation" 的情况,但很少有 "Transition Start" 或 "Transition End" 的情况(目前我没有看到任何情况但有时我在日志中看到它发生了)。

Upd2:

我想,我现在明白了。经过2天无果的尝试,我成功地重现了这个问题。

TransitionManager.beginDelayedTransition() 仅当屏幕上有要更新的内容时才会启动。

例如,如果您 setOnClickListenerOnClick 中的 TransitionManager.beginDelayedTransition() 到没有选择器的自定义背景按钮(android:background="#000",而不是默认选择器)- 没有将会发生,Transition 根本不会开始(正如您在您的案例中看到的那样)。同时,如果您将相同的 OnClickListener 分配给 default-styled 按钮(即以选择器作为背景)- Transition 立即开始。

同样,如果您在调用 TransitionManager.beginDelayedTransition() 之后执行一些 UI-changes:

public void changeScene(View v){
    AutoTransition autoTransition = new AutoTransition();
    autoTransition.setDuration(3000);
    autoTransition.addListener(new Transition.TransitionListener() {
        @Override
        public void onTransitionStart(Transition transition) {
            Toast.makeText(MainActivity.this, "start", Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onTransitionEnd(Transition transition) {
            Toast.makeText(MainActivity.this, "end", Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onTransitionCancel(Transition transition) {}

        @Override
        public void onTransitionPause(Transition transition) {}

        @Override
        public void onTransitionResume(Transition transition) {}
    });

    TransitionManager.beginDelayedTransition(container, autoTransition);
    findViewById(R.id.btn1).setVisibility(
            (findViewById(R.id.btn1).getVisibility()) == View.VISIBLE?
                     View.INVISIBLE : View.VISIBLE);
}

这将代码片段将工作,因为它应该:

但是一旦您删除了可见性的 setter,Transition 将添加到 TransitionManagerbacklog 而不会执行(方法的名称说 - 它可能 延迟 )。并且只会在下一个 UI-change:

期间执行

在此示例中,我删除了 setVisibility() - 您可以看到结果:"animation"(在我的例子中只是 Toasts)仅在我单击带有选择器的按钮后才开始背景(即 UI-change 发生)。

因此修复将是 - "make sure there's some UI change just after you call TransitionManager.beginDelayedTransition()".

希望对你有所帮助

P.S。有点奇怪的是,在 TransitionManager.beginDelayedTransition() 之后写的 TransitionManager.go() 不起作用。我在这里找到的唯一解决方法是将它放在 onTransitionEnd 中用于延迟转换。

Upd1:

第一时间想到可能和Xamarin有关。然后我发现了两个在 Xamarin.Android 中使用 BeginDelayedTransition 的例子:xamarin/monodroid-samples and garuma/Moyeu。 要检查您的问题是否与 Xamarin.Android 相关 - 我建议调试这两个项目以查看 TransitionListener 是否触发 Transition-events.

两个示例都使用 TransitionManager.beginDelayedTransition() 和一个参数 (viewGroup)。检查 TransitionManagersource code 显示,1 参数方法正在调用 beginDelayedTransition(sceneRoot, null);

public static void beginDelayedTransition(final ViewGroup sceneRoot) {
    beginDelayedTransition(sceneRoot, null);
}

和 null 替换为 sDefaultTransition:

private static Transition sDefaultTransition = new AutoTransition();
....
public static void More ...beginDelayedTransition(final ViewGroup sceneRoot, Transition transition) {
    if (!sPendingTransitions.contains(sceneRoot) && sceneRoot.isLaidOut()) {
        if (Transition.DBG) {
            Log.d(LOG_TAG, "beginDelayedTransition: root, transition = " +
                    sceneRoot + ", " + transition);
        }
        sPendingTransitions.add(sceneRoot);
        if (transition == null) {
            transition = sDefaultTransition;
        }
        final Transition transitionClone = transition.clone();
        sceneChangeSetup(sceneRoot, transitionClone);
        Scene.setCurrentScene(sceneRoot, null);
        sceneChangeRunTransition(sceneRoot, transitionClone);
    }
}

所以要调试它,您必须获取默认 TransactionManager.getDefaultTransition() 并将您的 TransitionListener 添加到其中。

如果有效 - 那么我们将不得不在您的代码中查找问题。如果它不起作用 - 好吧..那么,大概是我们发现了一个系统错误,我们可以将其归档。

我终于找到了问题的答案。

我在 FrameLayout 上调用 TransitionManager.beginDelayedTransition,当时我调用时没有 children,然后我添加了一个 child.

这不会导致发生转换,也不会调用任何回调。

我现在在打电话之前检查我正在呼叫 beginDelayedTransitionViewGroup 是否有 children;如果没有,我不会费心尝试制作动画。