android - 对button的width属性做属性动画时出错
阿神
阿神 2017-04-18 09:05:12
0
1
475

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

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.example.circleview.MainActivity">

    <Button
        android:id="@+id/click"
        android:layout_width="wrap_content"
        android:layout_height="50dp"
        android:text="Property Animation" />

    <TextView
        android:id="@+id/tv_showWidth"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="please click the button" />

</LinearLayout>
阿神
阿神

闭关修行中......

reply all(1)
Ty80

The problem lies in the performAnimate()ObjectAnimator.ofInt(...) 调用, 由于 ObjectAnimator 本身实现的问题, 它会把 target 存为 WeakReference type. The key code is as follows:

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;
    }
}

For this reason, if the object instance is not maintained, it is likely to be recycled by gc. Therefore, ViewWrapper should be used as a class member variable to prevent it from being recycled.

In addition, if you keep pressing, multiple animation requests will continue to be generated. The unfinished animations from the last time and the last time (last...last time) will affect the current animation action. If you want When the expected requirements are met, the last animation request should be canceled. The code is as follows:

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();
}
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template