
上述該圖中四個參數left、top、bottom、right參數可以通過以下幾個函數來獲得:getLeft、getTop、getbottom、getRight。需要注意的是上述幾個參數是相對于父容器的,例如:
這張圖中去擷取textView的四個參數,那麼這四個參數是相對于RelativeLayout,而不是相對于LinearLayout的。
從Android3.0增加了幾個參數:x、y、translationX、translationY,其中x和y是view左上角的坐标,而translationX和translationY是view左上角相對于父容器的偏移量,這幾個參數也是相對于父容器的坐标,這幾個參數分别有get\set方法,換算關系如下:
x = translationX + left;
y = translationY + top;
這裡需要注意的是left和top表示的是原始左上角的位置資訊,其值并不會改變,view平移時發生改變的是x、y、translationX、translationY。是以如果你的view位置不動的話,getX和getLeft的值是一樣的。
另外:
width = right - left;
height = bottom - top;
MotionEvent:
手指觸摸螢幕後産生一系列事件中,典型的事件類型有以下幾種:
- ACTION_DOWN:手指接觸螢幕
- ACTION_MOVE:手指滑動螢幕
-
ACTION_UP:手指離開螢幕
觸摸有幾種情況:
- 觸摸螢幕後離開:DOWN->UP
-
觸摸螢幕并滑動後離開:DOWN->MOVE->…->MOVE->UP
通過MotionEvent對象可以得到點選事件發生的x和y坐标,getX/getY傳回的是相對于目前view左上角的x和y坐标,getRawX/getRawY傳回的是相對手機螢幕左上角的x和y坐标。
TouchSlop是系統能識别出的被認為是最小滑動的最小距離,換句話說,當手指在螢幕上滑動時,如果兩次滑動之間的距離小于這個常量,那麼系統就不認為你是在進行滑動操作。這是一個常量,和裝置有關,不同裝置上的該值可能不一樣。通過ViewConfiguration.get(getContext()).getScaledTouchSlop()來擷取。平時我們在處理滑動時,可以用這個常量做一些過濾,當未超過這個值時,不認為是滑動行為。
有的時候,我們需要監聽view上的觸摸事件,包括點選、快速滑動、長按、拖動等,有些人喜歡在onTouchEvent根據點選事件來判斷,撰寫比較麻煩,可以使用GestureDectector來監聽,根據需要還可以實作onDoubleTapListener進而能夠監聽輕按兩下行為:
GestureDetector mGestureDetector = new GestureDetector(this);
mGestureDetector.setIsLongPressEnabled(false);
然後接管目标View的onTouchEvent方法,在onTouchEvent中添加如下實作:
boolean consume = mGestureDetector.onTouchEvent(event);
return consume;
例如觸屏事件觸發onDown函數,長按事件觸發onLongPress函數等,具體可以檢視onGestureListener和onDoubleTapListener接口。
1、onSingleTapUp、onSingleTapConfirmed
使用者(輕觸觸摸屏後)松開,由一個1個MotionEvent ACTION_UP觸發
這個事件執行的順序是onDown-》onShowPress-》onSingleTapUp
差別:
點選一下非常快的(不滑動)Touchup:onDown->onSingleTapUp->onSingleTapConfirmed
點選一下稍微慢點的(不滑 動)Touchup:onDown->onShowPress->onSingleTapUp->onSingleTapConfirmed
2、onShowPress:使用者輕觸觸摸屏,尚未松開或拖動,由一個1個MotionEvent ACTION_DOWN觸發。它與onDown()的差別,強調的是沒有松開或者拖動的狀态
3、onDown:是由一個MotionEventACTION_DOWN觸發的,但是他沒有任何限制,也就是說當使用者點選的時候,首先MotionEventACTION_DOWN,onDown就會執行,如果在按下的瞬間沒有松開或者是拖動的時候onShowPress就會執行,如果是按下的時間超過瞬間(這塊我也不太清楚瞬間的時間差是多少,一般情況下都會執行onShowPress),拖動了,就不執行onShowPress。
4、onLongPress:使用者長按觸摸屏,由多個MotionEvent ACTION_DOWN觸發,這個事件執行的順序是onDown-》onShowPress-》onLongPress
5、onFling:使用者按下觸摸屏、快速移動後松開,由1個MotionEvent ACTION_DOWN, 多個ACTION_MOVE,1個ACTION_UP觸發
6、onScroll:Touch了 滑動時觸發。 等其他觸碰事件。
View的滑動:
有以下幾種方式可以實作view的滑動,分别是:
- 通過view自身提供的scrollTo和scrollBy方法來實作滑動。
- 通過動畫給view添加平移效果來實作滑動。
- 通過改變view的layoutparams使得view重新布局進而實作滑動。
- 通過使用scroller
下面一一說明。
先看下scrollTo和scrollBy的源碼:
public void scrollTo(int x, int y) {
if (mScrollX != x || mScrollY != y) {
int oldX = mScrollX;
int oldY = mScrollY;
mScrollX = x;
mScrollY = y;
invalidateParentCaches();
onScrollChanged(mScrollX, mScrollY, oldX, oldY);
if (!awakenScrollBars()) {
postInvalidateOnAnimation();
}
}
}
public void scrollBy(int x, int y) {
scrollTo(mScrollX + x, mScrollY + y);
}
可以看到scrollBy其實也是調用了scrollTo,差別就是scrollBy是根據相對位置移動,而scrollTo是移動到指定的位置,與原來位置沒什麼關系。
不過這兩個方法移動的是view中的内容,而不是view。舉個例子,如果是textview的話,那麼滑動的是控件中的文字,而textview本身并不會移動。scrollTo很簡單,就不多說了。那來解釋下scrollBy中的變量mScrollX和mScrollY。
mScrollX和mScrollY記錄的是目前view内容所處的位置(移動後所處的位置,一開始為0),這兩個值分别有正值和負值。
mScrollX = view.left - viewcontent.left;
mScrollY = view.top - viewcontent.top;
這裡的view.left指的是該view的左邊框。viewcontent就指的是view内容的左邊框。
看個例子就是移動TextView中的内容:
第二種使用動畫的方法,分補間動畫(Tween Animation)、幀動畫(Frame Animation)和屬性動畫(Property Animation),如果采用補間動畫的是,使用TranslationAnimation,它雖然改變了view的位置,但是對于view來說,點選事件有效區域還是在原來的地方,而不是跟随view一起移動,很不友善,這是因為它隻是改變了view對象繪制的位置,而沒有改變view對象本身。google意識到這個問題,就出了屬性動畫,通過修改view的屬性。
如果要采用屬性動畫的話,為了相容3.0以下的版本(因為3.0以下的版本不支援屬性動畫),需要采用開源動畫庫nineoldandroids(http://nineoldandroids.com)。但是雖然使用了開源動畫庫,屬性動畫的本質依舊是補間動畫,是以一些點選事件還是無效,這還是有解決辦法的。比如一個按鈕向右移動10像素後,停留在移動後的位置,可以在移動位置事先放一個按鈕(隐藏狀态),然後當動畫結束後,将原來的按鈕隐藏,将實作安放好的按鈕顯示出來,狸貓換太子!
或者我們可以通過ValueAnimator,看下面的一段代碼:
final int startX = , deltaX =;
ValueAnimator animator = ValueAnimator.ofInt(,).setDuration();
animator.addUpdateListener(new AnimatorUpdateListener(){
@Override
public void onAnimationUpdate(ValueAnimator animator){
float fraction = animator.getAnimationFraction();
mButton.scrollTo(startX + (int)(deltaX * fraction),);
}
});
animator.start();
我們使用的ValueAnimator并沒有直接作用于button上面,隻是在動畫的每一幀到達時擷取動畫完成的比例,根據這個比例算出移動的距離。
第三種方法就是動态改變view的layoutparams,這個很簡單,像那種下拉重新整理的listview,就是通過動态改變header的高度來實作的。這裡不再贅述。
擴充:剛剛提到scrollTo和scrollBy,這個的确起到了滑動的效果,但是如何達到連續滑動的效果呢?
除了使用剛剛的ValueAnimator,還可以采用延時政策,它的核心思想就是通過發送一系列延時消息進而達到一種漸進式的效果,具體來說可以使用Handler或View的postDelayed方法,也可以使用線程的sleep方法。對于postDelayed方法來說,我們可以通過他來延時發送一個消息,在消息中實作對view的移動,如果接連不斷的話,可以達到連續滑動到效果。對于sleep方法來說,在while循環中不斷的滑動view和sleep,就可以實作彈性滑動的效果。