1,x,y代表的View相對于屏幕左上角的坐標,translationX和translationY是View左上角相對于父控件的偏移量,因此
? ? x = getLeft()+translationX
? ? y = getTop()+translationY
View在平移的過程中,getLeft()和getTop()的值并不會發生變化,變化的是translationX,x,translationY,y的值。
2,getX和getY返回的是View相對于父控件的x和y坐標,getRawX和getRawY返回的是View相對于屏幕左上角的坐標。
3,通過ViewConfiguration.get(getCpntext()).getScaledTouchSlop()來獲取系統能識別出被認為是滑動的最小距離。
4,實現View滑動的三種方式
? ? a,使用scrollTo/scrollBy ?//只能改變View內容位置不能改變View本身位置
? ? scrollBy內部也是通過scrollTo實現的,是一種相對滑動, scrollTo是絕對滑動。
? ? getScrollX的值等于View左邊緣和View內容左邊緣在水平方向上的距離,getScrollY的值等于View上邊緣和View內容上邊緣在豎直方向上的距離。
? ? scrollBy和scrollTo只能改變View內容的位置而不能改變View的位置。
? ? 當View的左邊緣在View內容的左邊緣的右邊時,getScrollX為正值;當View的上邊緣在View內容的上邊緣的下邊時,getScrollY為正值。
? ? b,使用動畫 ? // 適用于實現復雜,但是沒有交互的View
? ? c,改變布局參數 ?// 適用于有交互的View
MarginLayoutParams params = (MarginLayoutParams)mButton.getLayoutParams();
params.width += 100;
params.leftMargin +=? 100;
mButton.requestLayout()
5,彈性滑動的三種方式
? ? a,使用Scroller
? ? 僅僅使用startScroll()是無法讓View滑動的,需要緊接著調用invalidate方法,invalidate方法會調用computeScroll方法,這個方法需要我們自己來重寫,標準寫法如下:
public void computeScroll(){
? ? if(mScroller.computeScrollOffset()){
? ? ? ? ? scrollTo(mScroller.getCurrentX(),mScroller.getCurrentY());
? ? ? ? ? postInvalidate();
? ? }
}
computeScroll內部會去獲取mScroller當前的scrollX和scollY,然后通過scrollTo來實現滑動,緊接著調用postInvalidate()方法,又會再次調用computeScroll方法,如此反復,直到滑動結束。
需要注意:CurrentX和CurrentY的計算過程是在computeScrollOffset方法中進行的
? ? b,通過動畫
ObjectAnimator.ofFloat(targetView,"translationX",0,100).setDuration(100).start();
? ? c,使用延時策略
6,View事件分發機制
? 如果一個View設置了onTouchListener,那么onTouchListener的onTouch方法便會被調用,如果onTouch返回false,則調用onTouchEvent方法,如果onTouch返回true,則onTouchEvent方法不會被調用。可見onTouch的優先級高于onTouchEvent。
? 事件傳遞順序:Activity-Window-View
? 總結:
(1)同一個事件序列是指手機接觸屏幕那一刻起,到手指離開屏幕的那一刻結束,在這個過程中所產生的一系列事件,這個事件序列以down開始,中間含有數不清的move事件,最終以up事件結束。
(2)正常情況下,一個事件序列只能被一個View攔截且消耗,因為一旦有一個元素攔截了某次事件,那么同一個事件序列的所有事件都會直接交給他處理,因此同一個事件序列中的事件不能分別由兩個View同時處理,但是可以通過特殊手段做到,比如一個View將本該自己處理的事件通過onTouchEvent強行傳遞給其他View。
(3)onInterruptTouchEvent只會調用一次。
這個可以通過查看ViewGroup的dispatchTouchEvent方法的源代碼
final boolean intercepted;
if(actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchEvent != null){
? ? final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPET) !=0;
? ? if(!disallowIntercept){
? ? ? ? ?intercepted = onInterceptTouchEvent(ev);
? ? ? ? ?ev.setAction(action);
? ? }else{
? ? ? ? intercepted = false;
? ? }
else
? ? intercepted = true;
}
? ? mFirstTouchEvent只有在ViewGroup將事件傳遞給子元素之后才不為空,也就是說,如果ViewGroup在Down事件選擇攔截,mFirstTouchEvent就一定為空,在之后的MOVE和UP事件,actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchEvent != null這個條件一定為false,不會再次調用onInterceptTouchEvent方法,所以如果我們想在ViewGroup提前處理所有的點擊事件,不能在onInterceptTouchEvent方法中進行,可以選擇dispatchTouchEvent方法。
? ? FLAG_DISALLOW_INTERCEPET這個標記可以用來禁止ViewGroup攔截除ACTION_DOWN以外的事件,為什么無法禁止ACTION_DOWN事件?因為處理ACTION_DOWN事件的時候,ViewGroup會重置FLAG_DISALLOW_INTERCEPET這個標記,導致子元素設置的FLAG_DISALLOW_INTERCEPET標記不生效。
(4)如果一個View不消耗Down事件,那么同一個事件序列的其他事件都不會再交給它處理,而是調用父View的onTouchEvent.
(5)如果一個View不消耗除Down事件以外的事件,那么這個事件會消失,并最終將這些消失的點擊事件傳遞給Activity處理。
(6)ViewGroup默認不攔截任何事件。
(7)View沒有onInterceptTouchEvent方法,一旦有點擊事件傳遞給它,那么他的onTouchEvent方法就會被調用。
(8)View的onTouchEvent方法默認都返回為true,除非他是不可點擊的,根據clickable屬性來判斷
(9)View的enable屬性并不影響onTouchEvent的默認返回值
(10)onClick發生的前提條件是View是可點擊的,并且收到了down和up事件
(11)事件傳遞的過程是由外向內的,通過requestDisallowInterceptTouchEvent方法可以在子元素中干預父元素的事件分發過程,但是ACTION_DOWN事件除外