给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 文件
问题就出在 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(); }