android - 對(duì)button的width屬性做屬性動(dòng)畫時(shí)出錯(cuò)
問題描述
給button寫了一個(gè)包裝類,設(shè)置setWidth()和getWidth()方法,大多時(shí)候動(dòng)畫運(yùn)行是正確的,但是當(dāng)我連續(xù)運(yùn)行幾次之后就出錯(cuò)了,目的是把button的寬度從500px通過動(dòng)畫變成800px

運(yùn)行幾次后, 動(dòng)畫執(zhí)行完成后button的寬度未設(shè)置為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(); //長度寬度改變需要調(diào)用此方法進(jìn)行view的測(cè)量、布局和繪制 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(...) 調(diào)用, 由于 ObjectAnimator 本身實(shí)現(xiàn)的問題, 它會(huì)把 target 存為 WeakReference 類型. 關(guān)鍵代碼如下:
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; }}
由于這個(gè)原因, 如果不保持對(duì)象實(shí)例, 那么就很有可能會(huì)被gc回收掉. 因此, ViewWrapper 應(yīng)該作為類成員變量, 以防被回收.
另外, 如果不停地按, 就會(huì)不停地產(chǎn)生多個(gè)動(dòng)畫請(qǐng)求. 而上次以及上上次(上...上次)未執(zhí)行完成的動(dòng)畫會(huì)影響當(dāng)次的動(dòng)畫動(dòng)作. 如果要達(dá)到預(yù)期的要求, 就應(yīng)該把上次的動(dòng)畫請(qǐng)求取消掉. 代碼如下:
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();}
相關(guān)文章:
1. javascript - sublime快鍵鍵問題2. javascript - immutable配合react提升性能?3. 配置Apache時(shí),添加對(duì)PHP的支持時(shí)語法錯(cuò)誤4. 實(shí)現(xiàn)bing搜索工具urlAPI提交5. javascript - vue-router 地址改變數(shù)據(jù)未改變6. javascript - html5多個(gè)label中其中一個(gè)觸發(fā)change,如何判斷是哪一個(gè)出發(fā)了change7. javascript - 移動(dòng)端上不能實(shí)現(xiàn)拖拽布局嗎?8. css - 寫頁面遇到個(gè)布局問題,求大佬們幫解答,在線等,急!~9. phpstudy8.1支持win11系統(tǒng)嗎?10. javascript - nodejs關(guān)于進(jìn)程間發(fā)送句柄的一點(diǎn)疑問

網(wǎng)公網(wǎng)安備