View的坐標(biāo)系基公,滑動(dòng)以及事件傳遞基礎(chǔ)

View的坐標(biāo)系和位置參數(shù)

Android中琴庵,view的坐標(biāo)體系是相對(duì)于父View而言的

getTop(); //獲取子View初始位置左上角距父View頂部的距離
getLeft(); //獲取子View初始位置左上角距父View左側(cè)的距離
getBottom(); //獲取子View初始位置右下角距父View頂部的距離
getRight(); //獲取子View初始位置右下角距父View左側(cè)的距離

在MotionEvent中伤溉,獲取觸摸點(diǎn)的位置時(shí)辫秧,Event中除了get的一系列方法之外束倍,還有一個(gè)getRaw系列,它是相對(duì)于屏幕坐標(biāo)系來(lái)說(shuō)的。
從Anroid3.0開(kāi)始绪妹,View又增加了幾個(gè)額外的參數(shù)甥桂,x,y,translationX 和 translationY。

View.getX() //獲取View當(dāng)前位置左側(cè)距離父View左側(cè)的距離
View.getY() //獲取View當(dāng)前位置頂部側(cè)距離父View頂部的距離
View.getTranslationX() //View在X方向的偏移量邮旷,即當(dāng)前位置相對(duì)于初始位置的滑動(dòng)距離黄选,默認(rèn)值為 0,左滑為負(fù)值
View.getTranslationY() //View在Y方向的偏移量婶肩,即當(dāng)前位置相對(duì)于初始位置的滑動(dòng)距離办陷,默認(rèn)值為 0,左滑為負(fù)值

所以狡孔,getX() =getLeft() + getTranslationX() 懂诗。如果View的位置不發(fā)生改變,getX = getLeft苗膝,getTranslationX = 0殃恒;View通過(guò)滑動(dòng)(比如動(dòng)畫(huà))改變位置的時(shí)候,發(fā)生改變的只有x,y,translationX 和 translationY辱揭。
另外离唐,通過(guò)以上方法所獲取到的值,單位都是像素(px)问窃。
參考下圖:

View坐標(biāo)系

View事件的相關(guān)概念

MotionEvent

MotionEvent亥鬓,翻譯過(guò)來(lái)就是動(dòng)作事件,在Android中用它來(lái)接收和處理屏幕上的各種觸摸動(dòng)作域庇。在手指觸摸屏幕所產(chǎn)生的一系列事件中嵌戈,最典型的有以下幾種:

  • ACTION_DOWN 手指接觸屏幕
  • ACTION_MOVE 手指在屏幕上移動(dòng)
  • ACTION_UP 手指抬起

結(jié)合實(shí)際情況,可能會(huì)有以下幾種結(jié)果:

ACTION_DOWN -> ACTION_UP 點(diǎn)擊屏幕后松開(kāi)
ACTION_DOWN -> ACTION_MOVE ->...->ACTION_MOVE ->ACTION_UP 點(diǎn)擊滑動(dòng)听皿,最后松開(kāi)
這兩種情況就是兩個(gè)完整的事件流熟呛。

為了處理點(diǎn)擊事件,MotionEvent提供了一組獲取點(diǎn)擊坐標(biāo)的方法: getX/RawX尉姨,getY/getRawY庵朝。
getX/Y:獲取點(diǎn)擊位置相對(duì)于當(dāng)前View左上角的坐標(biāo)
getRawX/Y:獲取點(diǎn)擊位置相對(duì)于屏幕左上角的坐標(biāo)。這里跟View是不一樣的又厉,它沒(méi)有父View的概念九府。
關(guān)于MotionEvent的詳細(xì)介紹,可以參考這位同學(xué)的文章;

TouchSlop

ToucSlop是指系統(tǒng)所能識(shí)別出的最小滑動(dòng)距離覆致,我們可以用它來(lái)做一些過(guò)濾動(dòng)作侄旬,小于這個(gè)值的就不被認(rèn)為是滑動(dòng)。這是一個(gè)與設(shè)備相關(guān)的常量煌妈,可以通過(guò)以下方法獲壤芨帷:

ViewConfiguration.get(getContext()).getScaledTouchSlop()

VelocityTracker

VelocityTracker是速度追蹤的意思婆排,顧名思義,它就是用來(lái)追蹤滑動(dòng)過(guò)程中的速度笔链。使用方法如下:
首先在View的onTouchEvent方法中獲取對(duì)象,對(duì)添加對(duì)事件的追蹤:

VelocityTracker tracker = VelocityTracker.obtain();
        tracker.addMovement(event);

然后設(shè)置速度的單位腮猖,這里的單位是指像素與時(shí)間的關(guān)系鉴扫。比如我們將單位設(shè)置為1000(1s),在這1s內(nèi)劃過(guò)的像素?cái)?shù)為1000澈缺,得到的結(jié)果并不是1 px/ms坪创,而是1000,它指的是在整個(gè)時(shí)間段內(nèi)劃過(guò)的像素?cái)?shù)姐赡。代碼如下:

tracker.computeCurrentVelocity(1000);
        int xVelocity = (int) tracker.getXVelocity();
        int yVelocity = (int) tracker.getYVelocity();

GestureDector

手勢(shì)檢測(cè)莱预,用于輔助檢測(cè)用戶(hù)的動(dòng)作行為,比如單擊项滑,滑動(dòng)依沮,長(zhǎng)按等等。它是和onTouchEvent來(lái)配合使用的枪狂。當(dāng)處理比較復(fù)雜的事件時(shí)危喉,只用onTouchEvent可能會(huì)顯得非常麻煩,這時(shí)候就可以將事件托管給GestureDector來(lái)處理州疾。

GestureDector提供了兩個(gè)接口:
GestureDetector.OnGestureListener 和 GestureDetector.OnDoubleTapListener辜限,分別提供了針對(duì)不同事件的方法。

OnGestureListener :

       // 用戶(hù)輕觸觸摸屏严蓖,由1個(gè)MotionEvent ACTION_DOWN觸發(fā)       
        public boolean onDown(MotionEvent e) {    
            Log.i("MyGesture", "onDown");       
            Toast.makeText(MainActivity.this, "onDown", Toast.LENGTH_SHORT).show();       
            return false;    
        }    

        /\*    
          用戶(hù)輕觸觸摸屏薄嫡,尚未松開(kāi)或拖動(dòng),由一個(gè)1個(gè)MotionEvent ACTION_DOWN觸發(fā)    
          注意和onDown()的區(qū)別颗胡,強(qiáng)調(diào)的是沒(méi)有松開(kāi)或者拖動(dòng)的狀態(tài)    
            
          而onDown也是由一個(gè)MotionEventACTION_DOWN觸發(fā)的毫深,但是他沒(méi)有任何限制,  
          也就是說(shuō)當(dāng)用戶(hù)點(diǎn)擊的時(shí)候杭措,首先MotionEventACTION_DOWN费什,onDown就會(huì)執(zhí)行,  
          如果在按下的瞬間沒(méi)有松開(kāi)或者是拖動(dòng)的時(shí)候onShowPress就會(huì)執(zhí)行手素,如果是按下的時(shí)間超過(guò)瞬間  
          拖動(dòng)了鸳址,就不執(zhí)行onShowPress。  
         \*/    
        public void onShowPress(MotionEvent e) {    
            Log.i("MyGesture", "onShowPress");       
            Toast.makeText(MainActivity.this, "onShowPress", Toast.LENGTH_SHORT).show();       
        }    

        // 用戶(hù)(輕觸觸摸屏后)松開(kāi)泉懦,由一個(gè)1個(gè)MotionEvent ACTION_UP觸發(fā)       
        ///輕擊一下屏幕稿黍,立刻抬起來(lái),才會(huì)有這個(gè)觸發(fā)    
        //從名子也可以看出,一次單獨(dú)的輕擊抬起操作,當(dāng)然,如果除了Down以外還有其它操作,那就不再算是Single操作了,所以這個(gè)事件 就不再響應(yīng)    
        public boolean onSingleTapUp(MotionEvent e) {    
            Log.i("MyGesture", "onSingleTapUp");       
            Toast.makeText(MainActivity.this, "onSingleTapUp", Toast.LENGTH_SHORT).show();       
            return true;       
        }    

        // 用戶(hù)按下觸摸屏崩哩,并拖動(dòng)巡球,由1個(gè)ACTION_DOWN, 多個(gè)ACTION_MOVE觸發(fā)       
        public boolean onScroll(MotionEvent e1, MotionEvent e2,    
                float distanceX, float distanceY) {    
            Log.i("MyGesture22", "onScroll:"+(e2.getX()-e1.getX()) +"   "+distanceX);       
            Toast.makeText(MainActivity.this, "onScroll", Toast.LENGTH_LONG).show();       
            return true;       
        }    

        // 用戶(hù)長(zhǎng)按觸摸屏       
        public void onLongPress(MotionEvent e) {    
             Log.i("MyGesture", "onLongPress");       
             Toast.makeText(MainActivity.this, "onLongPress", Toast.LENGTH_LONG).show();       
        }    
  
        // 用戶(hù)按下觸摸屏言沐、快速移動(dòng)后松開(kāi),由1個(gè)MotionEvent ACTION_DOWN, 多個(gè)ACTION_MOVE, 1個(gè)ACTION_UP觸發(fā)酣栈,這是快速滑動(dòng)行為       
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,    
                float velocityY) {    
            Log.i("MyGesture", "onFling");       
            Toast.makeText(MainActivity.this, "onFling", Toast.LENGTH_LONG).show();       
            return true;    
        }   
 

OnDoubleTapListener:

       /\* 
          用來(lái)判定該次點(diǎn)擊是SingleTap而不是DoubleTap险胰,如果連續(xù)點(diǎn)擊兩次就是DoubleTap手勢(shì),如果只點(diǎn)擊一次矿筝, 
         系統(tǒng)等待一段時(shí)間后沒(méi)有收到第二次點(diǎn)擊則判定該次點(diǎn)擊為SingleTap而不是DoubleTap, 
         然后觸發(fā)SingleTapConfirmed事件起便。只要觸發(fā)該事件,那么當(dāng)前事件一定是單擊而不是雙擊中的一次窖维。 
         \*/  
        @Override  
        public boolean onSingleTapConfirmed(MotionEvent e) {  
            // TODO Auto-generated method stub  
            return false;  
        }  
  
        /\*雙擊事件 
         \*/  
        @Override  
        public boolean onDoubleTap(MotionEvent e) {  
            // TODO Auto-generated method stub  
            return false;  
        }  
  
        /\*  
         雙擊間隔中發(fā)生的動(dòng)作榆综。指觸發(fā)onDoubleTap以后,在雙擊之間發(fā)生的其它動(dòng)作铸史,包含down鼻疮、up和move事件 
          @MotionEvent e中包含了雙擊直接發(fā)生的其他動(dòng)作. 
         \*/  
        @Override  
        public boolean onDoubleTapEvent(MotionEvent e) {  
            // TODO Auto-generated method stub  
            return false;  
        }  

除此之外,GestureDetector還提供了一個(gè)簡(jiǎn)單的內(nèi)部類(lèi)琳轿,SimpleOnGestureListener判沟,基本就是將兩個(gè)listener拼到了一起。
GestureDetector使用起來(lái)也非常簡(jiǎn)單利赋,首先要獲取GestureDetector對(duì)象水评,然后在View的onTouchEvent方法中接管Event。代碼如下:

GestureDetector mGestureDetector = new GestureDetector(this);//當(dāng)前class繼承了GestureDetector的接口
@Override
    public boolean onTouchEvent(MotionEvent event) {
        mGestureDetector.setIsLongpressEnabled(false);//解決長(zhǎng)按后屏幕無(wú)法拖動(dòng)
        return mGestureDetector.onTouchEvent(event);
        
    }

View的滑動(dòng)

View的滑動(dòng)是各種控件的基礎(chǔ)媚送,比如ViewPager中燥,SlidingMenu,下拉刷新等等塘偎,他們的基礎(chǔ)都是滑動(dòng)疗涉。通常來(lái)說(shuō),View的滑動(dòng)有以下三種方式:

  • scrollTo/By(int x, int y);
    scrollBy是相對(duì)上一個(gè)位置的滑動(dòng)吟秩,x,y為滑動(dòng)的像素距離咱扣,負(fù)值表示向下或右,scrollTo表示絕對(duì)滑動(dòng)涵防。簡(jiǎn)單來(lái)說(shuō)闹伪,scrollTo表示相對(duì)于View的初始位置滑動(dòng)x,y個(gè)像素,而scrollBy是相對(duì)于上一個(gè)位置滑動(dòng)x,y個(gè)像素壮池。

  • 使用動(dòng)畫(huà)滑動(dòng)
    舉個(gè)簡(jiǎn)單例子:

ObjectAnimator.ofFloat(imageView,"translationX",-30f).setDuration(1000).start(); 

表示在1s內(nèi)偏瓤,將imageView沿X方向向左滑動(dòng)30個(gè)像素。

  • 改變布局參數(shù)
    這個(gè)很好理解椰憋,直接上代碼:
  //MarginLayoutParams params = (MarginLayoutParams) mButton.getLayoutParams();
  LayoutParams params = (LayoutParams) mButton.getLayoutParams();
  params.width += 100;
  mButton.requestLayout();
  //mButton.setLayoutParams(params);    

注意:scrollTo/By(int x, int y)厅克,采用該方法進(jìn)行滑動(dòng),滑動(dòng)的只是View的內(nèi)容橙依,而View的位置不發(fā)生變化证舟。比如將一個(gè)button通過(guò)scrollTo/By(int x, int y)移動(dòng)到新的位置之后硕旗,點(diǎn)擊當(dāng)前button所處的位置是沒(méi)有反應(yīng)的,因?yàn)閂iew實(shí)際上仍然處于原始位置女责。
這里只是簡(jiǎn)單介紹了View的滑動(dòng)方法漆枚,關(guān)于View的彈性滑動(dòng),可以參考這篇博文.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末抵知,一起剝皮案震驚了整個(gè)濱河市浪读,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌辛藻,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件互订,死亡現(xiàn)場(chǎng)離奇詭異吱肌,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)仰禽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)氮墨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人吐葵,你說(shuō)我怎么就攤上這事规揪。” “怎么了温峭?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵猛铅,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我凤藏,道長(zhǎng)奸忽,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任揖庄,我火速辦了婚禮栗菜,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蹄梢。我一直安慰自己疙筹,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布禁炒。 她就那樣靜靜地躺著而咆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪齐苛。 梳的紋絲不亂的頭發(fā)上翘盖,一...
    開(kāi)封第一講書(shū)人閱讀 51,443評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音凹蜂,去河邊找鬼馍驯。 笑死阁危,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的汰瘫。 我是一名探鬼主播狂打,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼混弥!你這毒婦竟也來(lái)了趴乡?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蝗拿,失蹤者是張志新(化名)和其女友劉穎晾捏,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體哀托,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡惦辛,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了仓手。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片胖齐。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖嗽冒,靈堂內(nèi)的尸體忽然破棺而出呀伙,到底是詐尸還是另有隱情,我是刑警寧澤添坊,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布剿另,位于F島的核電站,受9級(jí)特大地震影響贬蛙,放射性物質(zhì)發(fā)生泄漏驰弄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一速客、第九天 我趴在偏房一處隱蔽的房頂上張望戚篙。 院中可真熱鬧,春花似錦溺职、人聲如沸岔擂。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)乱灵。三九已至,卻和暖如春七冲,著一層夾襖步出監(jiān)牢的瞬間痛倚,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工澜躺, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蝉稳,地道東北人抒蚜。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像耘戚,于是被迫代替她去往敵國(guó)和親嗡髓。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容