Heim > Fragen und Antworten > Hauptteil
给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>
PHP中文网2017-04-18 09:07:12
问题就出在 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();
}