Changing the ActionBar hide animation?

2020-05-19 05:39发布

问题:

By default in Android 3.0+, when ActionBar.hide()/show() are called the bar is animated with a brief fade in/out animation.

There does not seem to be an XML style attribute in this list associated with an animation resource.

Is there some way to change this animation? In my case, I simply want to change the animation time, but is it also possible to have a sliding animation?

回答1:

No.

At least not in 3.0, 3.1, or 3.2. If you look at the decompiled sources of com.android.internal.app.ActionBarImpl you'll find that the animations are hard-coded.

E.g., from 3.2:

.method public hide()V
    .locals 8

    .prologue
    const/4 v5, 0x0

    const/4 v7, 0x0

    const/4 v6, 0x1

    .line 529
    iget-object v2, p0, Lcom/android/internal/app/ActionBarImpl;->mCurrentAnim:Landroid/animation/Animator;

    if-eqz v2, :cond_0

    .line 530
    iget-object v2, p0, Lcom/android/internal/app/ActionBarImpl;->mCurrentAnim:Landroid/animation/Animator;

    invoke-virtual {v2}, Landroid/animation/Animator;->end()V

    .line 532
    :cond_0
    iget-object v2, p0, Lcom/android/internal/app/ActionBarImpl;->mContainerView:Lcom/android/internal/widget/ActionBarContainer;

    invoke-virtual {v2}, Lcom/android/internal/widget/ActionBarContainer;->getVisibility()I

    move-result v2

    const/16 v3, 0x8

    if-ne v2, v3, :cond_1

    .line 553
    :goto_0
    return-void

    .line 536
    :cond_1
    iget-boolean v2, p0, Lcom/android/internal/app/ActionBarImpl;->mShowHideAnimationEnabled:Z

    if-eqz v2, :cond_3

    .line 537
    iget-object v2, p0, Lcom/android/internal/app/ActionBarImpl;->mContainerView:Lcom/android/internal/widget/ActionBarContainer;

    const/high16 v3, 0x3f80

    invoke-virtual {v2, v3}, Lcom/android/internal/widget/ActionBarContainer;->setAlpha(F)V

    .line 538
    iget-object v2, p0, Lcom/android/internal/app/ActionBarImpl;->mContainerView:Lcom/android/internal/widget/ActionBarContainer;

    invoke-virtual {v2, v6}, Lcom/android/internal/widget/ActionBarContainer;->setTransitioning(Z)V

    .line 539
    new-instance v0, Landroid/animation/AnimatorSet;

    invoke-direct {v0}, Landroid/animation/AnimatorSet;-><init>()V

    .line 540
    .local v0, anim:Landroid/animation/AnimatorSet;
    iget-object v2, p0, Lcom/android/internal/app/ActionBarImpl;->mContainerView:Lcom/android/internal/widget/ActionBarContainer;

    const-string v3, "alpha"

    new-array v4, v6, [F

    aput v5, v4, v7

    invoke-static {v2, v3, v4}, Landroid/animation/ObjectAnimator;->ofFloat(Ljava/lang/Object;Ljava/lang/String;[F)Landroid/animation/ObjectAnimator;

    move-result-object v2

    invoke-virtual {v0, v2}, Landroid/animation/AnimatorSet;->play(Landroid/animation/Animator;)Landroid/animation/AnimatorSet$Builder;

    move-result-object v1

    .line 541
    .local v1, b:Landroid/animation/AnimatorSet$Builder;
    iget-object v2, p0, Lcom/android/internal/app/ActionBarImpl;->mContentView:Landroid/view/View;

    if-eqz v2, :cond_2

    .line 542
    iget-object v2, p0, Lcom/android/internal/app/ActionBarImpl;->mContentView:Landroid/view/View;

    const-string/jumbo v3, "translationY"

    const/4 v4, 0x2

    new-array v4, v4, [F

    aput v5, v4, v7

    iget-object v5, p0, Lcom/android/internal/app/ActionBarImpl;->mContainerView:Lcom/android/internal/widget/ActionBarContainer;

    invoke-virtual {v5}, Lcom/android/internal/widget/ActionBarContainer;->getHeight()I

    move-result v5

    neg-int v5, v5

    int-to-float v5, v5

    aput v5, v4, v6

    invoke-static {v2, v3, v4}, Landroid/animation/ObjectAnimator;->ofFloat(Ljava/lang/Object;Ljava/lang/String;[F)Landroid/animation/ObjectAnimator;

    move-result-object v2

    invoke-virtual {v1, v2}, Landroid/animation/AnimatorSet$Builder;->with(Landroid/animation/Animator;)Landroid/animation/AnimatorSet$Builder;

    .line 544
    iget-object v2, p0, Lcom/android/internal/app/ActionBarImpl;->mContainerView:Lcom/android/internal/widget/ActionBarContainer;

    const-string/jumbo v3, "translationY"

    new-array v4, v6, [F

    iget-object v5, p0, Lcom/android/internal/app/ActionBarImpl;->mContainerView:Lcom/android/internal/widget/ActionBarContainer;

    invoke-virtual {v5}, Lcom/android/internal/widget/ActionBarContainer;->getHeight()I

    move-result v5

    neg-int v5, v5

    int-to-float v5, v5

    aput v5, v4, v7

    invoke-static {v2, v3, v4}, Landroid/animation/ObjectAnimator;->ofFloat(Ljava/lang/Object;Ljava/lang/String;[F)Landroid/animation/ObjectAnimator;

    move-result-object v2

    invoke-virtual {v1, v2}, Landroid/animation/AnimatorSet$Builder;->with(Landroid/animation/Animator;)Landroid/animation/AnimatorSet$Builder;

    .line 547
    :cond_2
    iget-object v2, p0, Lcom/android/internal/app/ActionBarImpl;->mHideListener:Landroid/animation/Animator$AnimatorListener;

    invoke-virtual {v0, v2}, Landroid/animation/AnimatorSet;->addListener(Landroid/animation/Animator$AnimatorListener;)V

    .line 548
    iput-object v0, p0, Lcom/android/internal/app/ActionBarImpl;->mCurrentAnim:Landroid/animation/Animator;

    .line 549
    invoke-virtual {v0}, Landroid/animation/AnimatorSet;->start()V

    goto :goto_0

    .line 551
    .end local v0           #anim:Landroid/animation/AnimatorSet;
    .end local v1           #b:Landroid/animation/AnimatorSet$Builder;
    :cond_3
    iget-object v2, p0, Lcom/android/internal/app/ActionBarImpl;->mHideListener:Landroid/animation/Animator$AnimatorListener;

    const/4 v3, 0x0

    invoke-interface {v2, v3}, Landroid/animation/Animator$AnimatorListener;->onAnimationEnd(Landroid/animation/Animator;)V

    goto :goto_0
.end method

UPDATE

Same goes for ICS and JellyBean



回答2:

A hack for disabling the ActionBar animation (found by inspecting the source code) can be done through introspection via:

try
{
  getActionBar().getClass().getDeclaredMethod("setShowHideAnimationEnabled", boolean.class).invoke(getActionBar(), false);
}
catch (Exception exception)
{
  // Too bad, the animation will be run ;(
}

Ugly, of course, but this actually work, at least from Android v4.2.



回答3:

if you use support-v7-compat, you can disable hide animation like this:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    //消除动画效果
    disableABCShowHideAnimation(getSupportActionBar());
    return super.onCreateOptionsMenu(menu);
}



public static void disableABCShowHideAnimation(ActionBar actionBar) {
    try
    {
        actionBar.getClass().getDeclaredMethod("setShowHideAnimationEnabled", boolean.class).invoke(actionBar, false);
    }
    catch (Exception exception)
    {
        try {
            Field mActionBarField = actionBar.getClass().getSuperclass().getDeclaredField("mActionBar");
            mActionBarField.setAccessible(true);
            Object icsActionBar = mActionBarField.get(actionBar);
            Field mShowHideAnimationEnabledField = icsActionBar.getClass().getDeclaredField("mShowHideAnimationEnabled");
            mShowHideAnimationEnabledField.setAccessible(true);
            mShowHideAnimationEnabledField.set(icsActionBar,false);
            Field mCurrentShowAnimField = icsActionBar.getClass().getDeclaredField("mCurrentShowAnim");
            mCurrentShowAnimField.setAccessible(true);
            mCurrentShowAnimField.set(icsActionBar,null);
            //icsActionBar.getClass().getDeclaredMethod("setShowHideAnimationEnabled", boolean.class).invoke(icsActionBar, false);
        }catch (Exception e){
            //....
        }
    }
}


回答4:

Neither of solutions doesn't work for me so I try this to disable animation at all:

public void setActionBarVisible(boolean isVisible) {
    View decorView = getWindow().getDecorView();
    int resId;
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB
        || Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        resId = getResources().getIdentifier(
                "action_bar_container", "id", getPackageName());
    } else {
        resId = Resources.getSystem().getIdentifier(
                "action_bar_container", "id", "android");
    }
    if (resId != 0) {
        decorView.findViewById(resId).setVisibility(isVisible ? View.VISIBLE : View.GONE);
    }
}

And it works just fine for me. You can try to animate whole action bar viewGroup to achieve similar effect.



回答5:

Yes, you most certainly can.

You first get the ActionBar view like this:

public View getActionBarView() {
    Window window = getActivity().getWindow();
    View v = window.getDecorView();
    int resId = getResources().getIdentifier("action_bar_container", "id", "android");
    return v.findViewById(resId);
}

Then you can apply animations directly on the view like this:

View actionbar = getActionBarView();
actionbar.setTranslation(-48); // move it out of the screen