android - 對button的width屬性做屬性動畫時出錯
問題描述
給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: 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();}
相關文章:
1. Docker for Mac 創建的dnsmasq容器連不上/不工作的問題2. docker安裝后出現Cannot connect to the Docker daemon.3. css - 定位為absolute的父元素中的子元素 如何設置在父元素的下面?4. java - 請問在main方法中寫成對象名.屬性()并賦值,與直接參參數賦值輸錯誤是什么原因?5. java - Spring boot 讀取 放在 jar 包外的,log4j 配置文件,系統有創建日志文件,不寫入日志信息。6. mysql里的大表用mycat做水平拆分,是不是要先手動分好,再配置mycat7. java - socket類服務端如何防止被ddos攻擊?8. javascript - 圖片鏈接請求一直是pending狀態,導致頁面崩潰,怎么解決?9. python - beautifulsoup獲取網頁內容的問題10. 怎么用css截取字符?
