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

给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>
阿神
阿神

闭关修行中......

Antworte allen(1)
Ty80

问题就出在 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();
}
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage