Ответ 1
Вам нужно будет создать подкласс Animator
, который игнорирует вызовы pause()
и resume()
, чтобы избежать этого исключения.
Для получения дополнительной информации я только что закончил сообщение об этом ниже:
Я создал настраиваемый круговой показ, чтобы использовать его как часть перехода в действие активности (в частности, я устанавливаю переход, когда окно вводит переход, вызывая 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)
Есть ли какая-то конкретная причина, по которой я получаю эту ошибку? Как этого следует избегать?
Вам нужно будет создать подкласс Animator
, который игнорирует вызовы pause()
и resume()
, чтобы избежать этого исключения.
Для получения дополнительной информации я только что закончил сообщение об этом ниже:
Есть ли какая-то конкретная причина, по которой я получаю эту ошибку?
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 позволяет вам прерывать использование кнопки "назад" , но очищает состояние перехода и использует отражение.
Здесь приведены результаты. Вы можете увидеть, как он полностью переходит от "А" в "М" и обратно, затем Кнопка "назад" прерывает переход и возвращается к "А". Это будет иметь больше смысла, если вы посмотрите его.
Во всяком случае, я надеюсь, что это поможет вам.
Вы можете добавить слушателя, чтобы ввести переход, который устанавливает флаг transitionInProgress
в методах onTransitionStart()
/onTransitionEnd()
. Затем вы можете переопределить метод finishAfterTransition()
, а затем проверить флаг transitionInProgress
и вызвать super
только при завершении перехода. В противном случае вы можете просто finish()
ваш Activity
или ничего не делать.
override fun finishAfterTransition() {
if (!transitionInProgress){
super.finishAfterTransition()
} else {
finish()
}
}