View事件體系與View的分發(fā)機(jī)制

View基礎(chǔ)知識(shí)

  • View本身可以是單個(gè)控件氯迂,也可以是多個(gè)控件組成的一組控件
  • ViewGroup也繼承了View

View的位置參數(shù)

  • top:左上角縱坐標(biāo)

  • left:左上角橫坐標(biāo)

  • bottom:右下角縱坐標(biāo)

  • right:右下角橫坐標(biāo)

  • x县袱,y:左上角坐標(biāo)

  • translatioX,translationY:左上角相對(duì)于父容器的坐標(biāo)偏移量辉阶,默認(rèn)值為0

x = left + translationX;
y = top + translationY;

MotionEvent和TouchSlop

  1. MotionEvent

在手指接觸到屏幕澈驼,觸發(fā)的典型事件類型:

  • ACTION_DOWN:手指剛接觸屏幕
  • ACTION_MOVE:手指在屏幕上移動(dòng)
  • ACTION_UP:手指離開(kāi)屏幕

通過(guò)MotionEvent對(duì)象可得點(diǎn)擊事件發(fā)生的x讼庇,y坐標(biāo)

  • getX/getY:獲得相對(duì)于View左上角x想邦,y的坐標(biāo)
  • getRowX/getRowY:獲得相對(duì)于手機(jī)屏幕左上角的x峡捡,y坐標(biāo)
  1. TouchSlop

系統(tǒng)所能識(shí)別滑動(dòng)的最小距離,是個(gè)常量么鹤,也就是滑動(dòng)的距離小于這個(gè)常量系統(tǒng)就認(rèn)為沒(méi)有進(jìn)行滑動(dòng)操作

獲得常量方式:ViewConfiguration.get(getContext()).getScaledTouchSlop()

VelocityTracker终娃、GestureDetector和Scroller

  1. VelocityTracker:速度追蹤

用于追蹤手指在滑動(dòng)過(guò)程中的速度,包含水平和垂直上的速度

  • 首先在View的onTouchEvent方法中追蹤當(dāng)前單擊事件的速度
VelocityTracker velocityTracker = VelocityTracker.obtain();
velocityTracker.addMovement(event);
  • 接著蒸甜,計(jì)算速度,獲取速度之前要計(jì)算速度余佛,這里的速度指一段時(shí)間手指所劃過(guò)的像素?cái)?shù)
velocityTracker.computeCurrentVelocity(1000);//計(jì)算速度
int xVelocity = (int)velocityTracker.getXVelocity();
int yVelocity = (int)velocityTracker.getYVelocity();
  • 最后柠新,調(diào)用clear方法重置并回收內(nèi)存
velocityTracker.clear();
velocityTracker.recycle();
  1. GestureDetector:手勢(shì)檢測(cè)

用來(lái)輔助檢測(cè)用戶的單擊、滑動(dòng)辉巡、長(zhǎng)按恨憎、雙擊等行為

  1. Scroller:彈性滑動(dòng),實(shí)現(xiàn)有過(guò)渡效果的滑動(dòng)

View的滑動(dòng)

方式:

  1. 通過(guò)View的ScrollTo/ScrollBy方法
  2. 通過(guò)動(dòng)畫(huà)給View施加平移效果來(lái)實(shí)現(xiàn)滑動(dòng)
  3. 通過(guò)改變View的LayoutParams使得View重新布局從而實(shí)現(xiàn)滑動(dòng)

使用scrollTo/scrollBy

scrollBy最終也是使用scrollTo來(lái)實(shí)現(xiàn)的郊楣,他們都只能該變View內(nèi)容的位置憔恳,不能該變View的布局位置,從左往右滑净蚤,mScrollX為正钥组,反之為負(fù),從下往上滑mScrollY為正今瀑,反之為負(fù)

使用動(dòng)畫(huà)方式(屬性動(dòng)畫(huà))

例:將一個(gè)View在100ms內(nèi)從原始位置向右平移100像素

ObjectAnimator.ofFloat(targetView, "translationX", 0, 100).setDuration(100).start();

View動(dòng)畫(huà)是對(duì)View的影像做操作程梦,并沒(méi)有該變View的位置參數(shù),要使動(dòng)畫(huà)的狀態(tài)得以保存橘荠,要將屬性fillAfter設(shè)置為true屿附,否則動(dòng)畫(huà)完成后結(jié)果就會(huì)消失,屬性動(dòng)畫(huà)不會(huì)出現(xiàn)此問(wèn)題

改變布局參數(shù)

即改變LayoutParams哥童,例如通過(guò)marginLeft參數(shù)可以達(dá)到向右平移的效果挺份,還可以預(yù)先設(shè)置一個(gè)空View等

3種滑動(dòng)方式的比較

  • ScrollTo/ScrollBy:操作簡(jiǎn)單,適合對(duì)View內(nèi)容的滑動(dòng)贮懈,不影響內(nèi)部元素的單擊事件匀泊,缺點(diǎn):只能滑動(dòng)View的內(nèi)容优训,不能滑動(dòng)View本身
  • 動(dòng)畫(huà):不能該變View本身屬性,適合不與用戶交互的View和實(shí)現(xiàn)復(fù)雜動(dòng)畫(huà)
  • 該變布局:操作復(fù)雜探赫,適用于與用戶交互的View

彈性滑動(dòng)

即漸進(jìn)式滑動(dòng)型宙,主要思想是將一次很大的滑動(dòng)分成若干個(gè)小的滑動(dòng),并且在一段時(shí)間內(nèi)完成

實(shí)現(xiàn)方式:Scroller伦吠、Handler.postDelayed妆兑、Thread.sleep

Scroller

典型使用方法:

Scroller scroller = new Scroller(mContext);
//緩慢移動(dòng)到指定位置
private void smoothScrollerTo(int destX, int destY) {
    int scrollX = getScrollX();
    int destX = destX - scrollX;
    //1s內(nèi)滑動(dòng)destX
    mSroller.startScroll(scrollX, 0, deltaX, 0, 1000);
    invalidate();
}

@Override
public void computeScroll() {
    if (mScroller.computeScrollOffset()) {
        scrollTo(mSroller.getCurrX(), mScroller.getCurrY());
        postInvalidate();
    }
}

注意:這里的滑動(dòng)是指內(nèi)容上的滑動(dòng),View本身是沒(méi)有改變的毛仪,在上述中搁嗓,僅僅調(diào)用startScroll方法是無(wú)法讓View進(jìn)行滑動(dòng)的,它的內(nèi)部沒(méi)有做滑動(dòng)相關(guān)的事情箱靴,只是保存了些數(shù)據(jù)而已腺逛,真正讓View滑動(dòng)的是invalidate方法,這個(gè)方法導(dǎo)致View重繪衡怀,在View重繪的draw方法中又會(huì)去調(diào)用computeScroll方法棍矛,這個(gè)方法又會(huì)去向Scroller獲取當(dāng)前的scrollX和scrollY,然后通過(guò)scrollTo方法實(shí)現(xiàn)滑動(dòng)

computeScrollOffset方法返回的是true表示滑動(dòng)沒(méi)有結(jié)束抛杨,返回false够委,表示滑動(dòng)結(jié)束

Scroller本身不能實(shí)現(xiàn)View滑動(dòng),要配合View的computeScroll方法才能完成彈性滑動(dòng)的效果

View事件分發(fā)機(jī)制

點(diǎn)擊事件傳遞規(guī)則

點(diǎn)擊事件分析的對(duì)象是MotionEvent怖现,點(diǎn)擊事件分發(fā)過(guò)程是由:dispatchTouchEvent茁帽、onInterceptTouchEvent、onTouchEvent方法共同完成的

  • dispatchTouchEvent:用于事件的分發(fā)屈嗤,如果事件傳遞到當(dāng)前View潘拨,那么此方法一定會(huì)被調(diào)用,返回結(jié)果受當(dāng)前View的onTouchEvent和下級(jí)View的dispatchTouchEvent影響
  • onInterceptTouchEvent:在上述方法內(nèi)部調(diào)用饶号,判斷是否攔截此事件铁追,true為攔截
  • onTouchEvent:在dispatchTouchEvent方法中調(diào)用,用來(lái)處理點(diǎn)擊事件

對(duì)于ViewGroup來(lái)說(shuō)讨韭,點(diǎn)擊事件后脂信,調(diào)用dispatchTouchEvent方法,此時(shí)ViewGroup的onInterceptTouchEvent方法被調(diào)用透硝,返回true代表攔截狰闪,接著調(diào)用onTouchEvent處理事件,如果返回false濒生,分發(fā)給子元素

當(dāng)View要處理事件時(shí)埋泵,如果設(shè)置了onTouchListener,此時(shí)onTouch方法會(huì)被回調(diào),返回false丽声,view的ouTouchEvent會(huì)被調(diào)用礁蔗,返回true,不會(huì)調(diào)用雁社。onTouchListener優(yōu)先級(jí)比onTouchEvent優(yōu)先級(jí)高浴井,onClickListener優(yōu)先級(jí)最低

事件點(diǎn)擊后傳遞順序:Acticity,window,view,如果view不處理事件就會(huì)向上傳遞處理


總結(jié):

  1. 同一個(gè)事件序列指手指觸摸屏幕到離開(kāi)這一過(guò)程事件
  2. 正常情況霉撵,一個(gè)事件序列只能給一個(gè)View處理
  3. 一旦一個(gè)View攔截了一個(gè)事件磺浙,那么它的同一個(gè)事件序列都直接交給它處理,并且它的onInterceptTouchEvent方法不再調(diào)用
  4. 某個(gè)View一旦開(kāi)始處理事件徒坡,如果不消耗ACTION_DOWN事件的話撕氧,那么其它同一系列事件它也不會(huì)消耗,并且重新交給父元素處理
  5. 如果View不消耗ACTION_DOWN以外的其他事件喇完,那么這個(gè)點(diǎn)擊事件就會(huì)消失伦泥,并且父元素的onTouchEvent方法也不會(huì)調(diào)用,View可以持續(xù)收到后續(xù)的事件锦溪,最終這些事件都會(huì)交由Activity來(lái)處理
  6. ViewGroup默認(rèn)不攔截任何事件
  7. View沒(méi)有onInterceptTouchEvent方法不脯,一旦有事件傳遞給它,他就調(diào)用onTouchEvent方法
  8. View的onTouchEvent方法都默認(rèn)會(huì)消耗事件的刻诊,除非它是不可點(diǎn)擊的
  9. View的enable屬性不影響onTouchEvent的返回值
  10. 事件的傳遞方向是從外向內(nèi)的跨新,總是由父元素傳遞給子元素,子元素再傳遞給View
  11. onClick發(fā)生的前提是View可點(diǎn)擊并且收到了down和up事件
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末坏逢,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子赘被,更是在濱河造成了極大的恐慌是整,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,110評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件民假,死亡現(xiàn)場(chǎng)離奇詭異浮入,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)羊异,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)事秀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人野舶,你說(shuō)我怎么就攤上這事易迹。” “怎么了平道?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,474評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵睹欲,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng)窘疮,這世上最難降的妖魔是什么袋哼? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,881評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮闸衫,結(jié)果婚禮上涛贯,老公的妹妹穿的比我還像新娘。我一直安慰自己蔚出,他們只是感情好弟翘,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,902評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著身冬,像睡著了一般衅胀。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上酥筝,一...
    開(kāi)封第一講書(shū)人閱讀 51,698評(píng)論 1 305
  • 那天滚躯,我揣著相機(jī)與錄音,去河邊找鬼嘿歌。 笑死掸掏,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的宙帝。 我是一名探鬼主播丧凤,決...
    沈念sama閱讀 40,418評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼步脓!你這毒婦竟也來(lái)了愿待?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,332評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤靴患,失蹤者是張志新(化名)和其女友劉穎仍侥,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體鸳君,經(jīng)...
    沈念sama閱讀 45,796評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡农渊,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,968評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了或颊。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片砸紊。...
    茶點(diǎn)故事閱讀 40,110評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖囱挑,靈堂內(nèi)的尸體忽然破棺而出醉顽,到底是詐尸還是另有隱情,我是刑警寧澤看铆,帶...
    沈念sama閱讀 35,792評(píng)論 5 346
  • 正文 年R本政府宣布徽鼎,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏否淤。R本人自食惡果不足惜悄但,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,455評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望石抡。 院中可真熱鬧檐嚣,春花似錦、人聲如沸啰扛。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,003評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)隐解。三九已至鞍帝,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間煞茫,已是汗流浹背帕涌。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,130評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留续徽,地道東北人蚓曼。 一個(gè)月前我還...
    沈念sama閱讀 48,348評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像钦扭,于是被迫代替她去往敵國(guó)和親纫版。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,047評(píng)論 2 355

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