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() 
    } 
}