您的位置:首页技术文章
文章详情页

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

【字号: 日期:2024-08-23 09:24:27浏览:39作者:猪猪

问题描述

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

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

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

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

这是代码

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 文件

<LinearLayout xmlns:android='http://schemas.android.com/apk/res/android' xmlns:tools='http://schemas.android.com/tools' android: android:layout_width='match_parent' android:layout_height='match_parent' android:orientation='vertical' tools:context='com.example.circleview.MainActivity'> <Buttonandroid: android:layout_width='wrap_content'android:layout_height='50dp'android:text='Property Animation' /> <TextViewandroid: android:layout_width='wrap_content'android:layout_height='wrap_content'android:text='please click the button' /></LinearLayout>

问题解答

回答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);}@Overridepublic 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 startingmInitialized = 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();}

相关文章: