Результаты пользовательского кругового обнаружения в "java.lang.UnsupportedOperationException" при приостановке?

Я создал настраиваемый круговой показ, чтобы использовать его как часть перехода в действие активности (в частности, я устанавливаю переход, когда окно вводит переход, вызывая Window#setEnterTransition()):

public class CircularRevealTransition extends Visibility {
    private final Rect mStartBounds = new Rect();

    /**
     * Use the view location as the circular reveal starting position.
     */
    public CircularRevealTransition(View v) {
        int[] loc = new int[2];
        v.getLocationInWindow(loc);
        mStartBounds.set(loc[0], loc[1], loc[0] + v.getWidth(), loc[1] + v.getHeight());
    }

    @Override
    public Animator onAppear(ViewGroup sceneRoot, final View v, TransitionValues startValues, TransitionValues endValues) {
        if (endValues == null) {
            return null;
        }
        int halfWidth = v.getWidth() / 2;
        int halfHeight = v.getHeight() / 2;
        float startX = mStartBounds.left + mStartBounds.width() / 2 - halfWidth;
        float startY = mStartBounds.top + mStartBounds.height() / 2 - halfHeight;
        float endX = v.getTranslationX();
        float endY = v.getTranslationY();
        v.setTranslationX(startX);
        v.setTranslationY(startY);

        // Create a circular reveal animator to play behind a shared
        // element during the Activity Transition.
        Animator revealAnimator = ViewAnimationUtils.createCircularReveal(v, halfWidth, halfHeight, 0f,
                FloatMath.sqrt(halfWidth * halfHeight + halfHeight * halfHeight));
        revealAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                // Set the view visibility to VISIBLE to prevent the
                // reveal from "blinking" at the end of the animation.
                v.setVisibility(View.VISIBLE);
            }
        });

        // Translate the circular reveal into place as it animates.
        PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("translationX", startX, endX);
        PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("translationY", startY, endY);
        Animator translationAnimator = ObjectAnimator.ofPropertyValuesHolder(v, pvhX, pvhY);

        AnimatorSet anim = new AnimatorSet();
        anim.setInterpolator(getInterpolator());
        anim.playTogether(revealAnimator, translationAnimator);
        return anim;
    }
}

Это нормально работает нормально. Однако, когда я нажимаю кнопку "Назад" в середине перехода, я получаю следующее исключение:

Process: com.adp.activity.transitions, PID: 13800
java.lang.UnsupportedOperationException
        at android.view.RenderNodeAnimator.pause(RenderNodeAnimator.java:251)
        at android.animation.AnimatorSet.pause(AnimatorSet.java:472)
        at android.transition.Transition.pause(Transition.java:1671)
        at android.transition.TransitionSet.pause(TransitionSet.java:483)
        at android.app.ActivityTransitionState.startExitBackTransition(ActivityTransitionState.java:269)
        at android.app.Activity.finishAfterTransition(Activity.java:4672)
        at com.adp.activity.transitions.DetailsActivity.finishAfterTransition(DetailsActivity.java:167)
        at android.app.Activity.onBackPressed(Activity.java:2480)

Есть ли какая-то конкретная причина, по которой я получаю эту ошибку? Как этого следует избегать?

Ответ 1

Вам нужно будет создать подкласс Animator, который игнорирует вызовы pause() и resume(), чтобы избежать этого исключения.

Для получения дополнительной информации я только что закончил сообщение об этом ниже:

Ответ 2

Есть ли какая-то конкретная причина, по которой я получаю эту ошибку?

ViewAnimationUtils.createCircularReveal является ярлыком для создания нового RevealAnimator, который является подклассом RenderNodeAnimator. По умолчанию RenderNodeAnimator.pause выдает UnsupportedOperationException. Вы видите, что это происходит здесь в вашей трассировке стека:

java.lang.UnsupportedOperationException
        at android.view.RenderNodeAnimator.pause(RenderNodeAnimator.java:251)

Когда Activity.onBackPressed вызывается в Lollipop, он вызывает новый вызов Activity.finishAfterTransition, который в итоге делает обратный вызов Animator.pause в Transition.pause(android.view.View), когда вы окончательно выбрали UnsupportedOperationException.

Причина, по которой она не выбрана при использовании кнопки "Назад" после завершения перехода, объясняется тем, как EnterTransitionCoordinator обрабатывает ввод Transition после его завершения.

Как этого избежать?

Я предполагаю, что у вас есть несколько вариантов, но ни один из них не идеален:

Вариант 1

При вызове Window.setEnterTransition присоедините TransitionListener, чтобы вы могли отслеживать, когда вызывать кнопку "Назад". Итак, что-то вроде:

public class YourActivity extends Activity {

    /** True if the current window transition is animating, false otherwise */
    private boolean mIsAnimating = true;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // Get the Window and enable Activity transitions
        final Window window = getWindow();
        window.requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
        // Call through to super
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_child);

        // Set the window transition and attach our listener
        final Transition circularReveal = new CircularRevealTransition(yourView);
        window.setEnterTransition(circularReveal.addListener(new TransitionListenerAdapter() {

            @Override
            public void onTransitionEnd(Transition transition) {
                super.onTransitionEnd(transition);
                mIsAnimating = false;
            }

        }));

        // Restore the transition state if available
        if (savedInstanceState != null) {
            mIsAnimating = savedInstanceState.getBoolean("key");
        }
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        // Save the current transition state
        outState.putBoolean("key", mIsAnimating);
    }

    @Override
    public void onBackPressed() {
        if (!mIsAnimating) {
            super.onBackPressed();
        }
    }

}

Вариант 2

Используйте отражение для вызова ActivityTransitionState.clear, который остановит Transition.pause(android.view.View) на вызов ActivityTransitionState.startExitBackTransition.

@Override
public void onBackPressed() {
    if (!mIsAnimating) {
        super.onBackPressed();
    } else {
        clearTransitionState();
        super.onBackPressed();
    }
}

private void clearTransitionState() {
    try {
        // Get the ActivityTransitionState Field
        final Field atsf = Activity.class.getDeclaredField("mActivityTransitionState");
        atsf.setAccessible(true);
        // Get the ActivityTransitionState
        final Object ats = atsf.get(this);
        // Invoke the ActivityTransitionState.clear Method
        final Method clear = ats.getClass().getDeclaredMethod("clear", (Class[]) null);
        clear.invoke(ats);
    } catch (final Exception ignored) {
        // Nothing to do
    }
}

Очевидно, каждый из них имеет недостатки. Вариант 1 в основном отключает кнопку "назад" до завершения перехода. Вариант 2 позволяет вам прерывать использование кнопки "назад" , но очищает состояние перехода и использует отражение.

Здесь приведены результаты. Вы можете увидеть, как он полностью переходит от "А" в "М" и обратно, затем Кнопка "назад" прерывает переход и возвращается к "А". Это будет иметь больше смысла, если вы посмотрите его.

Во всяком случае, я надеюсь, что это поможет вам.

Ответ 3

Вы можете добавить слушателя, чтобы ввести переход, который устанавливает флаг transitionInProgress в методах onTransitionStart()/onTransitionEnd(). Затем вы можете переопределить метод finishAfterTransition(), а затем проверить флаг transitionInProgress и вызвать super только при завершении перехода. В противном случае вы можете просто finish() ваш Activity или ничего не делать.

override fun finishAfterTransition() {
    if (!transitionInProgress){
        super.finishAfterTransition()
    } else {
        finish()
    }
}