10

我创建了一个自定义的圆形揭示过渡作为活动的进入过渡的一部分使用(特别是,我通过调用Window#setEnterTransition()设置过渡为窗口的输入转换):自定义循环显示暂停时的结果是“java.lang.UnsupportedOperationException”吗?

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

    /** 
    * Use the view's location as the circular reveal's 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's 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; 
    } 
} 

这正常工作OK。然而,当我点击“返回”按钮在过渡的中间,我得到以下异常:

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) 

有为什么我收到此错误的任何具体的原因是什么?应该如何避免?

+0

我给你我的观点在G +上的这种小车行为的解释! – Pavlos 2014-11-05 11:46:31

回答

8

您需要创建Animator的子类,以忽略对pause()resume()的调用,以避免此例外。

有关详细信息,我只是完成了下面关于这个话题帖子:

+0

非常感谢您的帮助!这似乎解决了我的问题。我编辑了你的答案,为链接提供了更多的上下文。 – 2014-11-07 15:20:54

3

是否有任何具体的原因,为什么我得到这个错误?

ViewAnimationUtils.createCircularReveal是创建一个新的RevealAnimator,这是RenderNodeAnimator子类中的快捷方式。默认情况下,RenderNodeAnimator.pause将抛出一个UnsupportedOperationException。你看这发生在你这里堆栈跟踪:当你的UnsupportedOperationException终于

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

Activity.onBackPressed被称为棒棒糖,它对Activity.finishAfterTransition新的呼叫,从而最终使呼叫回Animator.pauseTransition.pause(android.view.View),这是抛出。

之所以采用“返回”按钮时,转换完成后,将不会抛出,是因为如何EnterTransitionCoordinator handles the entering Transition once it's completed.

究竟应该如何避免?

我假设你有两个选择,但也不是真正的理想:

选项1

附上TransitionListener当你调用Window.setEnterTransition这样就可以监视何时调用“回“按钮。所以,像这样:

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允许您使用“返回”按钮中断,但清除过渡状态并使用反射。

Here's a gfy of the results.您可以看到它是如何从“A”完全转换到“M”并再次返回,然后“返回”按钮中断转换并返回到“A”。如果你看它,那会更有意义。

无论如何,我希望能帮到你一些。

+0

这两种方法肯定会让人感觉不舒服。如果我不得不在两者之间进行选择,那么我可能会更喜欢第一个选项,因为反射看起来有点吓人。谁知道清除过渡状态的副作用可能是...... – 2014-11-05 15:15:40

+2

也许还有另外一种方法......如果你只想阻止'Animator#pause()'被调用,也许你可以把循环显示动画师在一个简单的'Animator'包装器子类中,它将所有的方法调用_except_'pause()'转发给循环显示动画器。看起来比使用反射更安全一点,至少......但似乎并不是理想的解决方案。 – 2014-11-05 15:22:27

+0

@AlexLockwood是的,这似乎更好,但仍然不理想。无论哪种方式,肯定你会抛出'Exception',因为'Animator'最终会回到'RenderNodeAnimator.pause'。 – adneal 2014-11-05 21:18:34

0

您可以添加监听器输入在方法onTransitionStart()/onTransitionEnd()中设置标志transitionInProgress的转换。然后,您可以覆盖方法finishAfterTransition(),然后检查transitionInProgress标志,并且只有在转换完成时才调用super。否则,你只能finish()你的Activity或什么也不做。

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