最近由於項目需要,做一個比較複雜的3D動畫,核心就是根據手指的滑動來翻轉3D效果。因此,又回顧了一下view滑動效果,而且根據不同的方式實現了一下,發現其中很多參數容易搞混,比如改變view的left,marginLeft,translationX中的任意一個值,對另外2個值及其getX是否有影響,因此,做了一下實驗,發現挺有意思,結果如下:
注:第三行,是設置了android:layout_centerInParent=”true”的結果,因此圖片也滑動不了了。如果去掉此設置,getLeft是隨着leftMargin而改變的!
從上面的數據來看,這幾個view的參數之間的值相互之間不影響。同理right, marginRight, translationX和top, marginTop, translationY以及bottom, marginBottom, translationY都能得到相同的答案。
另外一點很重要,就是無論如何變化,始終有如下關係:
view.getX() = view.getLeft() + view.getTranslationX();
這個其實根據View.getX()的代碼也能證明:
/**
* The visual x position of this view, in pixels. This is equivalent to the
* {@link #setTranslationX(float) translationX} property plus the current
* {@link #getLeft() left} property.
*
* @return The visual x position of this view, in pixels.
*/
@ViewDebug.ExportedProperty(category = "drawing")
public float getX() {
return mLeft + getTranslationX();
}
其次,在處理滑動的過程中,很容易弄混亂的一點就是view和MotionEvent event都有getX(), getY()方法, 它們的區別是:
- event的getX(), getY()是觸摸點相對於監聽此觸摸事件的view的左邊,頂部x, y方向距離,而view的getX(),getY()是view的左邊,頂部相對於其父控件的距離。
- 一般來講,利用event的getX(),getY()來作爲主動變化的因素,判斷滑動的方向,距離。然後根據方向,距離來動態改變view的各種參數(如left, marginLeft, translationX)來使得view產生動畫效果。
還有一點需要總結的是:
- view的getTop(), getLeft(), getRight(), getBottom(), getX(), getY()都是相對於其父佈局的值。
- MotionEvent的getX(),getY()是當前觸摸點相對於設置此監聽觸摸事件的view的左邊和頂部的距離。
- 只有MotionEvent的getRawX(),getRawY()是絕對座標。
另外,對於view的動畫視覺效果,大體上分爲以下幾種:
- 改變left, right, top, bottom參數,然後layout(l, t, r, b)
- 改變layoutParams參數,然後requestLayout()或者setLayoutParams(params)
- 利用View的scrollTo(x, y), scrollBy(x, y)
- 利用Scroller類,重寫computeScroll()方法
- View動畫
- 屬性動畫
- ViewDragHelper類
其中1的例子如下:
@Override
public boolean onTouch(View view, MotionEvent event) {
float dx, dy;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startTouchX = event.getX();
startTouchY = event.getY();
circleImageLeft = circleImage.getLeft();
circleImageRight = circleImage.getRight();
break;
case MotionEvent.ACTION_MOVE:
dx = event.getX() - startTouchX;
dy = event.getY() - startTouchY;
/*
注意:
1. 這裏不要用 (circleImage.getLeft() + dx),因爲每次layout之後已經改變了left的值,因此getLeft是已經更新後的值,這樣將會導致dx全部累計,滑動使得view滑動的更快。
2. 一定要記得right也要跟着變,否則right不變的話,view將會被壓縮或者拉伸。
*/
circleImage.layout((int) (circleImageLeft + dx),
circleImage.getTop(),
(int) (circleImageRight + dx),
circleImage.getBottom());
break;
case MotionEvent.ACTION_UP:
AppLog.e("ACTION_UP");
break;
case MotionEvent.ACTION_CANCEL:
AppLog.e("ACTION_CANCEL");
break;
}
return true;
}
第2種方法:
1. 如果設置了view的屬性 android:layout_centerInParent=”true”的話,圖片就滑動不了了
2. 如果滑動view到達邊界處,會出現縮小view而滑不出邊界的現象。
而第1種方法則不會出現這2個問題,所以個人比較推薦第1種方法來滑動。
第3種方法,它只能滑動view的內容,view本身不動,並且是瞬間移動。
第4種方法與第3種類似,只是它能夠設置時間緩慢進行。
第5,6種方法,動畫都是需要start,如果需要根據手指滑動來跟着變化就不適合動畫方法。
第7種,適合自定義viewGroup類型的控件來實現它裏面控件的動畫效果。