【问题标题】:ActionBarSherlock animation moves positionActionBarSherlock 动画移动位置
【发布时间】:2012-04-20 19:33:51
【问题描述】:

我的 ActionBarSherlock 操作栏中有一个启动 AsyncTask 的菜单选项。我正在尝试在后台任务运行时让图标设置动画。我让它工作了,除了当我点击图标时,图标本身会向右“跳”几个像素,而且非常明显。不完全确定是什么导致了这种情况发生。

这是我目前所拥有的:

private MenuItem refreshItem;   

private void DoRefresh() 
{
    final LayoutInflater inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    final ImageView ivRefresh = (ImageView)inflater.inflate(R.layout.refresh_view, null);

    final Animation rotation = AnimationUtils.loadAnimation(activity, R.anim.refresh);
    ImageView ivRefresh.startAnimation(rotation);

    refreshItem.setActionView(ivRefresh);

    //AsyncTask is kicked off here
}

@Override
public boolean onOptionsItemSelected(final MenuItem item) 
{
    if (item.getItemId() == R.id.refresh) {
        refreshItem = item;
        this.DoRefresh();
        return true;
    } else {
        return super.onOptionsItemSelected(item);
    }
}

刷新视图.xml

<?xml version="1.0" encoding="utf-8"?>

<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:src="@drawable/refresh"
/>

刷新.xml

<rotate
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromDegrees="0"
    android:toDegrees="360"
    android:pivotX="50%"
    android:pivotY="50%"
    android:repeatCount="infinite"
    android:interpolator="@android:anim/linear_interpolator"
    android:duration="1100" 
/>

更新:

一直在玩这个,没有运气。它与动画无关,因为如果我禁用动画,图标仍然会跳到右侧。如果我注释掉setActionView,图标不会跳转,所以它与此有关。一点头绪都没有,快把我逼疯了。

【问题讨论】:

标签: android android-animation actionbarsherlock


【解决方案1】:

我不知道这是否会产生任何影响,但如果您使用pivotvalues,您可能会解决这个问题。也许只绕一个轴旋转?

【讨论】:

    【解决方案2】:

    Alex Fu,您的文章很有帮助,但还有一些可以改进的地方。

    每次在onAnimationEnd中重启动画效率低下。相反,在开始之前将动画重复计数设置为无限,然后当动作完成时,将重复计数设置为 0。动画将结束当前循环,然后调用 onAnimationEnd,您可以在其中调用 setActionView(null)。

    我还遇到了 ActionBarSherlock 和本机 ICS ActionBar 的最后几帧的问题(至少在模拟器中,还没有在真实设备上测试,因为我没有)调用 setActionView(null) 后动画与静态按钮重叠。我的解决方案是将正在动画的 ImageView 包装在 LinearLayout 中,然后在 LinearLayout 上调用 setVisibility(View.GONE)。如果为视图设置动画,使用 setVisibility 隐藏它是行不通的,但隐藏它的父视图会。

    以下是所有相关代码:

    layout/actionbar_progress.xml(注意我已经添加了 id,虽然只有 ImageView 需要一个):

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/actionbar_progress_container"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" >
    
        <ImageView xmlns:android="http://schemas.android.com/apk/res/android"
            android:id="@+id/actionbar_progress_image"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="5dp"
            android:src="@drawable/ic_action_refresh"
            style="@style/Widget.Sherlock.ActionButton"
            />
    </LinearLayout>
    

    动画/refresh_rotate.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <rotate xmlns:android="http://schemas.android.com/apk/res/android"
        android:fromDegrees="0"
        android:toDegrees="360"
        android:pivotX="50%"
        android:pivotY="50%"    
        android:duration="1000"
        android:interpolator="@android:anim/linear_interpolator"
        android:repeatCount="infinite">
    
    </rotate>
    

    repeatCount 设置为无限,但这不是必需的,因为 java 代码必须将其设置为无限。

    在 onCreateOptionsMenu 我有

    ...
    menuInflater.inflate(R.menu.main, menu);
    
    mRefreshItem = menu.findItem(R.id.menu_refresh);
    ...
    

    这只是为了避免每次单击刷新按钮时都执行 findItem。

    在 onOptionsItemSelected 中:

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
    
        ...
    
            case R.id.menu_refresh:
                refresh();
                break;
    
        ...
    
        }
    }
    

    刷新方法只是一个 5 秒延迟的虚拟帖子;它调用 refreshAnim 来启动动画,然后在完成后调用 refreshAnimEnd 来停止动画:

    private void refresh() {
        refreshAnim();
        getWindow().getDecorView().postDelayed(
            new Runnable() {
                @Override
                public void run() {
                    refreshAnimEnd();
                }
            }, 5000);
    }
    

    这里是最重要的部分,使用 cmets:

    private void refreshAnim() {
        LayoutInflater inflater = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        //The inflated layout and loaded animation are put into members to avoid reloading them every time.
        //For convenience, the ImageView is also extracted into a member
        if (mRefreshIndeterminateProgressView == null || mRefreshRotateClockwise == null) {
            mRefreshIndeterminateProgressView = inflater.inflate(R.layout.actionbar_progress, null);
            mRefreshRotateClockwise = AnimationUtils.loadAnimation(this, R.anim.refresh_rotate);
            mRefreshImage = mRefreshIndeterminateProgressView.findViewById(R.id.actionbar_progress_image);
        }
        //reset some stuff - make the animation infinite again,
        //and make the containing view visible
        mRefreshRotateClockwise.setRepeatCount(Animation.INFINITE);
        mRefreshIndeterminateProgressView.setVisibility(View.VISIBLE);
        mRefreshRotateClockwise.setAnimationListener(new AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {}
    
            @Override
            public void onAnimationRepeat(Animation animation) {}
    
            @Override
            public void onAnimationEnd(Animation animation) {
                //This is important to avoid the overlapping problem.
                //First hide the animated icon
                //setActionView(null) does NOT hide it!
                //as long as the animation is running, it will be visible
                //hiding an animated view also doesn't work
                //as long as the animation is running
                //but hiding the parent does.
                mRefreshIndeterminateProgressView.setVisibility(View.GONE);
                //make the static button appear again
                mRefreshItem.setActionView(null);
            }
        });
        mRefreshItem.setActionView(mRefreshIndeterminateProgressView);
        //everything is set up, start animating.
        mRefreshImage.startAnimation(mRefreshRotateClockwise);
    }
    
    private void refreshAnimEnd() {
        //sanity
        if ( mRefreshImage == null || mRefreshItem == null ) {
            return;
        }
        Animation anim = mRefreshImage.getAnimation();
    
        //more sanity
        if (anim != null) {
            //let the animation finish nicely
            anim.setRepeatCount(0);
        } else {
            //onAnimationEnd won't run in this case, so restore the static button
            mRefreshItem.setActionView(null);
        }
    }
    

    【讨论】:

    • Restarting the animation in onAnimationEnd each time is inefficient. Instead, set the animation repeatcount to infinite before you start it, then when the action finishes, set the repeatcount to 0. The animation will finish the current loop, and then call onAnimationEnd, where you can call setActionView(null). -- 这是一个好主意,我在写那篇文章时没有想到。感谢您的 cmets。
    【解决方案3】:

    如果您使用的是普通的 ActionBar,只需将其添加到您的 img_layout.xml

    style="?android:attr/actionButtonStyle"
    

    或者使用 SherLock 操作栏:

    style="@style/Widget.Sherlock.ActionButton"
    

    【讨论】:

      【解决方案4】:

      如果菜单选项是操作项,则应将样式 Widget.Sherlock.ActionButton 应用于 ImageView 以保持一致性。阅读我写的这篇文章,了解如何正确地为动作项设置动画。 http://alexfu.tumblr.com/post/19414506327/android-dev-animate-action-items

      【讨论】:

        猜你喜欢
        • 2015-05-09
        • 2018-04-22
        • 2016-10-28
        • 2014-06-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-12-16
        相关资源
        最近更新 更多