android - 对button的width属性做属性动画时出错

 秦风2502869477 发布于 2022-10-28 06:02

给button写了一个包装类,设置setWidth()和getWidth()方法,大多时候动画运行是正确的,但是当我连续运行几次之后就出错了,目的是把button的宽度从500px通过动画变成800px

运行几次后, 动画执行完成后button的宽度未设置为800, 如下图:

这是代码

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    private TextView textView;
    private Button button;
    private int clickTimes = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button = (Button) findViewById(R.id.click);
        textView = (TextView) findViewById(R.id.tv_showWidth);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                performAnimate();
                clickTimes ++;
                ViewTreeObserver observer = button.getViewTreeObserver();
                observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                    @Override
                    public void onGlobalLayout() {
                        textView.setText("The " + (clickTimes) + "th click" + "button's width:" + button.getWidth());
                    }
                });
            }
        });
    }

    private void performAnimate() {
        ViewWrapper viewWrapper = new ViewWrapper(button);
        ObjectAnimator.ofInt(viewWrapper, "width", 500, 800).setDuration(1000).start();
    }

    private static class ViewWrapper {
        private View mTarget;

        public ViewWrapper(View mTarget) {
            this.mTarget = mTarget;
        }

        public int getWidth() {
            return mTarget.getLayoutParams().width;
        }

        public void setWidth(int width) {
            mTarget.getLayoutParams().width = width;
            mTarget.requestLayout(); //长度宽度改变需要调用此方法进行view的测量、布局和绘制
            Log.d(TAG, "setWidth: " + mTarget.getWidth());
        }
    }

}

layout 文件



    
1 个回答
  • 问题就出在 performAnimate()ObjectAnimator.ofInt(...) 调用, 由于 ObjectAnimator 本身实现的问题, 它会把 target 存为 WeakReference 类型. 关键代码如下:

    public static ObjectAnimator ofInt(Object target, String propertyName, int... values) {
        ObjectAnimator anim = new ObjectAnimator(target, propertyName);
        anim.setIntValues(values);
        return anim;
    }
    
    private ObjectAnimator(Object target, String propertyName) {
        setTarget(target);
        setPropertyName(propertyName);
    }
    
    @Override
    public void setTarget(@Nullable Object target) {
        final Object oldTarget = getTarget();
        if (oldTarget != target) {
            if (isStarted()) {
                cancel();
            }
            mTarget = target == null ? null : new WeakReference<Object>(target);
            // New target should cause re-initialization prior to starting
            mInitialized = false;
        }
    }
    

    由于这个原因, 如果不保持对象实例, 那么就很有可能会被gc回收掉. 因此, ViewWrapper 应该作为类成员变量, 以防被回收.

    另外, 如果不停地按, 就会不停地产生多个动画请求. 而上次以及上上次(上...上次)未执行完成的动画会影响当次的动画动作. 如果要达到预期的要求, 就应该把上次的动画请求取消掉. 代码如下:

    private ObjectAnimator mObjectAnimator;
    private ViewWrapper viewWrapper;
    
    private void performAnimate() {
        if (mObjectAnimator != null) {
            mObjectAnimator.cancel();
            mObjectAnimator = null;
        }
    
        viewWrapper = new ViewWrapper(button);
        mObjectAnimator = ObjectAnimator.ofInt(viewWrapper, "width", 500, 800).setDuration(1000);
        mObjectAnimator.start();
    }
    
    2022-10-29 12:21 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有