精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Android進階之View事件分發(fā)機制和源碼詳解

移動開發(fā) Android
View的位置主要由它的四個頂點來決定,即它的四個屬性:top、left、right、bottom,分別表示View左上角的坐標點( top,left) 以及右下角的坐標點( right,bottom)。

[[418059]]

本文轉(zhuǎn)載自微信公眾號「Android開發(fā)編程」,作者Android開發(fā)編程。轉(zhuǎn)載本文請聯(lián)系A(chǔ)ndroid開發(fā)編程公眾號。

前言

在Android 開發(fā)中事件分發(fā)是比較重要的,也是比較難理解的;

開發(fā)中會經(jīng)常遇到滑動沖突(比如ScrollView或是SliddingMenu與ListView的嵌套)的問題,

需要我們深入的了解android事件響應(yīng)機制才能解決,

事件響應(yīng)機制已經(jīng)是android開發(fā)者必不可少的知識。

那么今天我們就來詳細講解下事件分發(fā),各位老鐵們一起學(xué)習(xí)

一、view事件相關(guān)知識了解

1、View的位置參數(shù)

View的位置主要由它的四個頂點來決定,即它的四個屬性:top、left、right、bottom,分別表示View左上角的坐標點( top,left) 以及右下角的坐標點( right,bottom)。

同時,我們可以得到View的大小:

  1. width = right - left 
  2. height = bottom - top 

而這四個參數(shù)可以由以下方式獲取:

  1. Left = getLeft(); 
  2. Right = getRight(); 
  3. Top = getTop(); 
  4. Bottom = getBottom(); 

Android3.0后,View增加了x、y、translationX和translationY這幾個參數(shù)。其中x和y是View左上角的坐標,而translationX和translationY是View左上角相對于容器的偏移量。他們之間的換算關(guān)系如下:

  1. x = left + translationX; 
  2. y = top + translationY; 

2、MotionEvent

我們對屏幕的點擊,滑動,抬起等一系的動作都是由一個一個MotionEvent對象組成的。根據(jù)不同動作,主要有以下三種事件類型:

  • ACTION_DOWN:手指剛接觸屏幕,按下去的那一瞬間產(chǎn)生該事件
  • ACTION_MOVE:手指在屏幕上移動時候產(chǎn)生該事件
  • ACTION_UP:手指從屏幕上松開的瞬間產(chǎn)生該事件

從ACTION_DOWN開始到ACTION_UP結(jié)束我們稱為一個事件序列

正常情況下,無論你手指在屏幕上有多么騷的操作,最終呈現(xiàn)在MotionEvent上來講無外乎下面兩種;

  • 點擊后抬起,也就是單擊操作:ACTION_DOWN -> ACTION_UP
  • 點擊后再風(fēng)騷的滑動一段距離,再抬起:ACTION_DOWN -> ACTION_MOVE -> ... -> ACTION_MOVE -> ACTION_UP;
  1. public class MotionEventActivity extends BaseActivity { 
  2.     private Button mButton; 
  3.     @Override 
  4.     protected void onCreate(Bundle savedInstanceState) { 
  5.         super.onCreate(savedInstanceState); 
  6.         setContentView(R.layout.activity_motion_event); 
  7.         mButton = (Button) findViewById(R.id.button); 
  8.         mButton.setOnTouchListener(new View.OnTouchListener() { 
  9.             @Override 
  10.             public boolean onTouch(View v, MotionEvent event) { 
  11.                 switch (event.getAction()) { 
  12.                     case MotionEvent.ACTION_DOWN: 
  13.                         e("MotionEvent: ACTION_DOWN"); 
  14.                         break; 
  15.                     case MotionEvent.ACTION_MOVE: 
  16.                         e("MotionEvent: ACTION_MOVE"); 
  17.                         break; 
  18.                     case MotionEvent.ACTION_UP: 
  19.                         e("MotionEvent: ACTION_UP"); 
  20.                         break; 
  21.                 } 
  22.                 return false
  23.             } 
  24.         }); 
  25.     } 
  26.     public void click(View v) { 
  27.         e("點擊了按鈕"); 
  28.     } 

3、Scroller

彈性滑動對象,用于實現(xiàn)View的彈性滑動。其本身無法讓View彈性滑動,需要和View的computeScroll方法配合使用才能完成這個功能。使用方法:

  1. Scroller scroller = new Scroller(mContext); 
  2. //緩慢移動到指定位置 
  3. private void smoothScrollTo(int destX,int destY){ 
  4.     int scrollX = getScrollX(); 
  5.     int delta = destX - scrollX; 
  6.     //1000ms內(nèi)滑向destX,效果就是慢慢滑動 
  7.     mScroller.startScroll(scrollX,0,delta,0,1000); 
  8.     invalidata(); 
  9. }  
  10. @Override 
  11. public void computeScroll(){ 
  12.     if(mScroller.computeScrollOffset()){ 
  13.     scrollTo(mScroller.getCurrX,mScroller.getCurrY()); 
  14.     postInvalidate(); 
  15.     } 

4、事件分發(fā)涉及方法了解

  • dispatchTouchEvent(MotionEvent ev):用來進行事件分發(fā)。如果事件能傳遞給當前的View,那么此方法一定會被調(diào)用。
  • onInterceptTouchEvent(MotionEvent ev):用來判斷是否攔截某個事件,如果當前View攔截了某個事件,那么在同一個事件序列中,此方法不會再被調(diào)用。
  • onTouchEvent(MotionEvent ev):用來處理觸摸事件,返回結(jié)果表示是否消耗當前事件,如果不消耗,則在同一個事件序列中,當前View無法再次接受事件。
  • onTouch(View view, MotionEvent motionEvent):用于處理觸摸事件,通過setOnTouchListener設(shè)置,很常見的方法。
  • onClick(View view):用于處理點擊事件,通過setOnClickListener設(shè)置,很常見的方法。
  • requestDisallowInterceptTouchEvent(boolean b):請求不攔截觸摸事件。一般用于處理滑動沖突中,子控件請求父控件不攔截;
  • ACTION_DOWN以外的其他事件,ACTION_DOWN事件不受影響。

二、事件分發(fā)

當一個MotionEvent產(chǎn)生了以后,就是你的手指在屏幕上做一系列動作的時候,系統(tǒng)需要把這一系列的MotionEvent分發(fā)給一個具體的View。我們重點需要了解這個分發(fā)的過程,那么系統(tǒng)是如何去判斷這個事件要給哪個View,也就是說是如何進行分發(fā)的呢?

事件分發(fā)需要View的三個重要方法來共同完成:

1、public boolean dispatchTouchEvent(MotionEvent event)

通過方法名我們不難猜測,它就是事件分發(fā)的重要方法。那么很明顯,如果一個MotionEvent傳遞給了View,那么dispatchTouchEvent方法一定會被調(diào)用!

返回值:表示是否消費了當前事件。可能是View本身的onTouchEvent方法消費,也可能是子View的dispatchTouchEvent方法中消費。返回true表示事件被消費,本次的事件終止。返回false表示View以及子View均沒有消費事件,將調(diào)用父View的onTouchEvent方法;

2、public boolean onInterceptTouchEvent(MotionEvent ev)

事件攔截,當一個ViewGroup在接到MotionEvent事件序列時候,首先會調(diào)用此方法判斷是否需要攔截。特別注意,這是ViewGroup特有的方法,View并沒有攔截方法;

返回值:是否攔截事件傳遞,返回true表示攔截了事件,那么事件將不再向下分發(fā)而是調(diào)用View本身的onTouchEvent方法。返回false表示不做攔截,事件將向下分發(fā)到子View的dispatchTouchEvent方法。

3、public boolean onTouchEvent(MotionEvent ev)

真正對MotionEvent進行處理或者說消費的方法。在dispatchTouchEvent進行調(diào)用;

返回值:返回true表示事件被消費,本次的事件終止。返回false表示事件沒有被消費,將調(diào)用父View的onTouchEvent方法

上面的三個方法可以用以下的偽代碼來表示其之間的關(guān)系。

  1. public boolean dispatchTouchEvent(MotionEvent ev) { 
  2.         boolean consume = false;//事件是否被消費 
  3.         if (onInterceptTouchEvent(ev)){//調(diào)用onInterceptTouchEvent判斷是否攔截事件 
  4.             consume = onTouchEvent(ev);//如果攔截則調(diào)用自身的onTouchEvent方法 
  5.         }else
  6.             consume = child.dispatchTouchEvent(ev);//不攔截調(diào)用子View的dispatchTouchEvent方法 
  7.         } 
  8.         return consume;//返回值表示事件是否被消費,true事件終止,false調(diào)用父View的onTouchEvent方法 
  9.     } 

接下來我們來看一下View 和ViewGroup 在事件分發(fā)的時候有什么不一樣的地方

ViewGroup是View的子類,也就是說ViewGroup本身就是一個View,但是它可以包含子View(當然子View也可能是一個ViewGroup),所以不難理解,上面所展示的偽代碼表示的是ViewGroup 處理事件分發(fā)的流程。而View本身是不存在分發(fā),所以也沒有攔截方法(onInterceptTouchEvent),它只能在onTouchEvent方法中進行處理消費或者不消費。

通過下面的流程圖,會更加清晰的幫助我們梳理事件分發(fā)機制

可以看出事件的傳遞過程都是從父View到子View。

子View可以通過requestDisallowInterceptTouchEvent方法干預(yù)父View的事件分發(fā)過程(ACTION_DOWN事件除外),而這就是我們處理滑動沖突常用的關(guān)鍵方法;

對于View(注意!ViewGroup也是View)而言,如果設(shè)置了onTouchListener,那么OnTouchListener方法中的onTouch方法會被回調(diào)。onTouch方法返回true,則onTouchEvent方法不會被調(diào)用(onClick事件是在onTouchEvent中調(diào)用)所以三者優(yōu)先級是onTouch->onTouchEvent->onClick;

View 的onTouchEvent 方法默認都會消費掉事件(返回true),除非它是不可點擊的(clickable和longClickable同時為false),View的longClickable默認為false,clickable需要區(qū)分情況,如Button的clickable默認為true,而TextView的clickable默認為false;

事件傳遞的機制,這里給出一些總結(jié):

  • 一個事件系列以down事件開始,中間包含數(shù)量不定的move事件,最終以up事件結(jié)束;
  • 正常情況下,一個事件序列只能由一個View攔截并消耗;
  • 某個View攔截了事件后,該事件序列只能由它去處理,并且它的onInterceptTouchEvent不會再被調(diào)用;
  • 某個View一旦開始處理事件,如果它不消耗ACTION_DOWN事件( onTouchEvnet返回false) ,那么同一事件序列中的其他事件都不會交給他處理,并且事件將重新交由他的父元素去處理,即父元素的onTouchEvent被調(diào)用;。
  • 如果View不消耗ACTION_DOWN以外的其他事件,那么這個事件將會消失,此時父元素的onTouchEvent并不會被調(diào)用,并且當前View可以持續(xù)收到后續(xù)的事件,最終消失的點擊事件會傳遞給Activity去處理。
  • ViewGroup默認不攔截任何事件;
  • View沒有onInterceptTouchEvent方法,一旦事件傳遞給它,它的onTouchEvent方法會被調(diào)用;
  • View的onTouchEvent默認消耗事件,除非他是不可點擊的( clickable和longClickable同時為false) 。View的longClickable屬性默認false,clickable默認屬性分情況(如TextView為false,button為true);
  • View的enable屬性不影響onTouchEvent的默認返回值;
  • onClick會發(fā)生的前提是當前View是可點擊的,并且收到了down和up事件;
  • 事件傳遞過程總是由外向內(nèi)的,即事件總是先傳遞給父元素,然后由父元素分發(fā)給子View,通過requestDisallowInterceptTouchEvent方法可以在子元素中干預(yù)父元素的分發(fā)過程,但是ACTION_DOWN事件除外;

三、滑動沖突的解決方式

1、外部攔截法

所謂外部攔截法是指點擊事件都先經(jīng)過父容器的攔截處理,如果父容器需要此事件就攔截,否則就不攔截。下面是偽代碼:

  1. public boolean onInterceptTouchEvent (MotionEvent event){ 
  2. boolean intercepted = false
  3. int x = (int) event.getX(); 
  4. int y = (int) event.getY(); 
  5. switch (event.getAction()) { 
  6. case MotionEvent.ACTION_DOWN: 
  7.     intercepted = false
  8.     break; 
  9. case MotionEvent.ACTION_MOVE: 
  10.     if (父容器需要當前事件) { 
  11.     intercepted = true
  12.     } else { 
  13.     intercepted = false
  14.     }  
  15.     break; 
  16. case MotionEvent.ACTION_UP: 
  17.     intercepted = false
  18.     break; 
  19. default :  
  20.     break; 
  21. }  
  22. mLastXIntercept = x; 
  23. mLastYIntercept = y; 
  24. return intercepted; 

針對不同沖突,只需修改父容器需要當前事件的條件即可。其他不需修改也不能修改。

ACTION_DOWN:必須返回false。因為如果返回true,后續(xù)事件都會被攔截,無法傳遞給子View;

ACTION_MOVE:根據(jù)需要決定是否攔截;

ACTION_UP:必須返回false。如果攔截,那么子View無法接受up事件,無法完成click操作。而如果是父容器需要該事件,那么在ACTION_MOVE時已經(jīng)進行了攔截,根據(jù)上一節(jié)的結(jié)論3,ACTION_UP不會經(jīng)過onInterceptTouchEvent方法,直接交給父容器處理;

2、內(nèi)部攔截法

內(nèi)部攔截法是指父容器不攔截任何事件,所有的事件都傳遞給子元素,如果子元素需要此事件就直接消耗,否則就交由父容器進行處理。這種方法與Android事件分發(fā)機制不一致,需要配合requestDisallowInterceptTouchEvent方法才能正常工作。下面是偽代碼:

  1. public boolean dispatchTouchEvent ( MotionEvent event ) { 
  2. int x = (int) event.getX(); 
  3. int y = (int) event.getY(); 
  4. switch (event.getAction) { 
  5. case MotionEvent.ACTION_DOWN: 
  6.     parent.requestDisallowInterceptTouchEvent(true); 
  7.     break; 
  8. case MotionEvent.ACTION_MOVE: 
  9.     int deltaX = x - mLastX; 
  10.     int deltaY = y - mLastY; 
  11.     if (父容器需要此類點擊事件) { 
  12.         parent.requestDisallowInterceptTouchEvent(false); 
  13.     }  
  14.     break; 
  15. case MotionEvent.ACTION_UP: 
  16.     break; 
  17. default :  
  18.     break; 
  19. }  
  20. mLastX = x; 
  21. mLastY = y; 
  22. return super.dispatchTouchEvent(event); 

除了子元素需要做處理外,父元素也要默認攔截除了ACTION_DOWN以外的其他事件,這樣當子元素調(diào)用parent.requestDisallowInterceptTouchEvent(false)方法時,父元素才能繼續(xù)攔截所需的事件。因此,父元素要做以下修改:

  1. public boolean onInterceptTouchEvent (MotionEvent event) { 
  2.     int action = event.getAction(); 
  3.     if(action == MotionEvent.ACTION_DOWN) { 
  4.         return false
  5.     } else { 
  6.         return true
  7.     } 

四、事件分發(fā)機制-源碼分析

從源碼的角度來了解一下Android下的事件分發(fā)機制

1、首先看到Activity中的dispatchTouchEvent()方法源碼:

  1. public boolean dispatchTouchEvent(MotionEvent ev) { 
  2.     // 如果是ACTION_DOWN事件會走這個語句,onUserInteraction()這個方法在系統(tǒng)中是空實現(xiàn) 
  3.     if (ev.getAction() == MotionEvent.ACTION_DOWN) { 
  4.         onUserInteraction(); 
  5.     } 
  6.     /** 
  7.      * 主要看一下這行代碼 
  8.      * getWindow()表示獲取Window的子類PhoneWindow對象 
  9.      * 也就是說調(diào)用PhoneWindow中的superDispatchTouchEvent(ev)方法,判斷是否有控件處理事件 
  10.      */ 
  11.     if (getWindow().superDispatchTouchEvent(ev)) { 
  12.         return true
  13.     } 
  14.     // 如果沒有控件能處理事件,就走這一行代碼,調(diào)用Activity的onTouchEvent()方法處理事件 
  15.     return onTouchEvent(ev); 

2、接著進入到PhoneWindow中的,查看superDispatchTouchEvent(ev)這個方法:

  1. @Override 
  2. public boolean superDispatchTouchEvent(MotionEvent event) { 
  3.     return mDecor.superDispatchTouchEvent(event); 

在PhoneWindow類的superDispatchTouchEvent(ev)方法中,直接調(diào)用了mDecor對象的superDispatchTouchEvent(ev)方法,mDecore其實就是繼承至FrameLayout的DecorView的對象。在《Activity的組成》這篇博客中貼出了DecorView類的定義源碼。

接著查看類中的superDispatchTouchEvent(ev)這個方法:

  1. public boolean superDispatchTouchEvent(MotionEvent event) { 
  2.     return super.dispatchTouchEvent(event); 

只有一句代碼,super.dispatchTouchEvent(event),調(diào)用父類的dispatchTouchEvent(event)方法,也就是FrameLayout的dispatchTouchEvent(event)方法,查看FrameLayout類會發(fā)現(xiàn)FrameLayout并沒有重寫dispatchTouchEvent(event)方法,那么就是使用的ViewGroup中的dispatchTouchEvent(event)方法。

到這里也就完全說明了Activity在做事件分發(fā)時調(diào)用的是ViewGroup中的dispatchTouchEvent()方法。

3、查看ViewGroup中的dispatchTouchEvent()方法:

  1. @Override 
  2. public boolean dispatchTouchEvent(MotionEvent ev) { 
  3.     ... 
  4.     boolean handled = false
  5.     // 過濾觸摸安全策略,如果是false(窗口或控件被遮住了時),直接跳出觸摸事件 
  6.     // 如果應(yīng)該分發(fā)事件(調(diào)用onTouch()或onTouchEvdent()方法),則返回True;如果應(yīng)該刪除事件,則返回false 
  7.     if (onFilterTouchEventForSecurity(ev)) { 
  8.         ... 
  9.         /** 
  10.          * 如果是DOWN事件就先將mFirstTouchTarget設(shè)置為null, 
  11.          * 然后在resetTouchState()方法中重置狀態(tài) 
  12.          */ 
  13.         if (actionMasked == MotionEvent.ACTION_DOWN) { 
  14.             cancelAndClearTouchTargets(ev); 
  15.             resetTouchState(); 
  16.         } 
  17.         // 定義變量intercepted標記ViewGroup是否攔截Touch事件的傳遞. 
  18.         final boolean intercepted; 
  19.         // 事件為ACTION_DOWN或者mFirstTouchTarget不為null(有控件消費touch事件) 
  20.         if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) { 
  21.             //判斷disallowIntercept(禁止攔截)標志位 
  22.             final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; 
  23.             //當沒有禁止攔截時 
  24.             if (!disallowIntercept) { 
  25.                 // 調(diào)用onInterceptTouchEvent(ev)方法,并將返回值賦給intercepted 
  26.                 intercepted = onInterceptTouchEvent(ev); 
  27.                 ev.setAction(action); 
  28.             } else { 
  29.                  //當禁止攔截時,指定intercepted = false,表示不攔截事件 
  30.                 intercepted = false
  31.             } 
  32.         } else { 
  33.             //當事件不是ACTION_DOWN并且mFirstTouchTarget為null(沒有控件消費touch事件)時 
  34.             //設(shè)置 intercepted = true,表示ViewGroup執(zhí)行Touch事件攔截的操作。 
  35.             intercepted = true
  36.         } 
  37.         ... 
  38.         // 事件分發(fā) 
  39.         final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0; 
  40.         TouchTarget newTouchTarget = null
  41.         boolean alreadyDispatchedToNewTouchTarget = false
  42.         //不是ACTION_CANCEL事件并且intercepted為false(ViewGroup不攔截事件onInterceptTouchEvent()方法返回false
  43.         if (!canceled && !intercepted) { 
  44.             //處理ACTION_DOWN事件 
  45.             if (actionMasked == MotionEvent.ACTION_DOWN 
  46.                 || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN) 
  47.                 || actionMasked == MotionEvent.ACTION_HOVER_MOVE) { 
  48.                 final int actionIndex = ev.getActionIndex();  
  49.                 final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex):TouchTarget.ALL_POINTER_IDS; 
  50.                 removePointersFromTouchTargets(idBitsToAssign); 
  51.                 final int childrenCount = mChildrenCount; 
  52.                 if (childrenCount != 0) { 
  53.                     // 依據(jù)Touch坐標尋找孩子控件來消費Touch事件 
  54.                     final View[] children = mChildren; 
  55.                     final float x = ev.getX(actionIndex); 
  56.                     final float y = ev.getY(actionIndex); 
  57.                     final boolean customOrder = isChildrenDrawingOrderEnabled(); 
  58.                     // 遍歷所有孩子控件,判斷哪個消費Touch事件 
  59.                     for (int i = childrenCount - 1; i >= 0; i--) { 
  60.                         final int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i; 
  61.                         final View child = children[childIndex]; 
  62.                         if (!canViewReceivePointerEvents(child) || !isTransformedTouchPointInView(x, y, child, null)) { 
  63.                             continue
  64.                         } 
  65.                         newTouchTarget = getTouchTarget(child); 
  66.                         if (newTouchTarget != null) { 
  67.                             // 找到消費Touch事件的孩子控件,跳出循環(huán),并用newTouchTarget表示孩子控件 
  68.                             newTouchTarget.pointerIdBits |= idBitsToAssign; 
  69.                             break; 
  70.                         } 
  71.                         resetCancelNextUpFlag(child); 
  72.                         // 沒有跳出循環(huán),走到這一步,就會調(diào)用dispatchTransformedTouchEvent()方法,將事件傳給孩子控件做遞歸處理,第三個參數(shù)不為null 
  73.                         if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) { 
  74.                            ... 
  75.                         } 
  76.                     } 
  77.                 } 
  78.                 /** 
  79.                  * 如果在循環(huán)中沒有孩子控件消費事件并且之前的mFirstTouchTarget不為空 
  80.                  */ 
  81.                 if (newTouchTarget == null && mFirstTouchTarget != null) { 
  82.                     // 將mFirstTouchTarget的賦給newTouchTarget 
  83.                     newTouchTarget = mFirstTouchTarget; 
  84.                     while (newTouchTarget.next != null) { 
  85.                         newTouchTarget = newTouchTarget.next
  86.                     } 
  87.                     // newTouchTarget指向了最初的TouchTarget 
  88.                     newTouchTarget.pointerIdBits |= idBitsToAssign; 
  89.                 } 
  90.             } 
  91.         } 
  92.         /** 
  93.          * 分發(fā)Touch事件至目標控件(target),以上過程主要針對ACTION_DOWN, 
  94.          * 如果不是(上一步中判斷intercepted變量),比如ACTION_MOVE和ACTION_UP,就是從此處開始執(zhí)行 
  95.          */ 
  96.         if (mFirstTouchTarget == null) { 
  97.             /** 
  98.              * mFirstTouchTarget為null表示Touch事件未被消費或Touch事件被攔截了, 
  99.              * 則調(diào)用ViewGroup的dispatchTransformedTouchEvent()方法,遞歸處理,第三個參數(shù)為null 
  100.              */ 
  101.             handled = dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS); 
  102.         } else { 
  103.             /** 
  104.              * mFirstTouchTarget不為null表示找到了可以消費Touch事件的子View 
  105.              * 并且MOVE或UP事件可以傳遞到該子View 
  106.              */ 
  107.             TouchTarget predecessor = null
  108.             // 將找到的可以消費事件的mFirstTouchTarget賦給目標控件(target) 
  109.             TouchTarget target = mFirstTouchTarget; 
  110.             while (target != null) { 
  111.                 final TouchTarget next = target.next
  112.                 // 如果已經(jīng)分發(fā)到新的控件并且消費事件的目標控件就是新的控件 
  113.                 if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) { 
  114.                     handled = true
  115.                 } else { 
  116.                     final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted; 
  117.                     // 否則調(diào)用dispatchTransformedTouchEvent()方法進行遞歸處理,第三個參數(shù)不為null 
  118.                     if (dispatchTransformedTouchEvent(ev, cancelChild, target.child, target.pointerIdBits)) { 
  119.                         handled = true
  120.                     } 
  121.                    ... 
  122.                 } 
  123.                 predecessor = target; 
  124.                 target = next
  125.             } 
  126.         } 
  127.         /** 
  128.          * 如果是ACTION_UP和ACTION_CANCEL事件,還原狀態(tài) 
  129.          */ 
  130.         if (canceled|| actionMasked == MotionEvent.ACTION_UP 
  131.                     || actionMasked == MotionEvent.ACTION_HOVER_MOVE) { 
  132.             resetTouchState(); 
  133.         } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) { 
  134.             ... 
  135.         } 
  136.     } 
  137.     ... 
  138.     return handled; 

我們可以看到在上面的方法中,調(diào)用的onInterceptTouchEvent()判斷是否需要攔截事件。

查看ViewGroup中的dispatchTransformedTouchEvent()方法:

  1. private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel, 
  2.             View child, int desiredPointerIdBits) { 
  3.     final boolean handled; 
  4.     ... 
  5.         if (child == null) { 
  6.             // 如果孩子控件為空,就調(diào)用View的dispatchTouchEvent()方法,在View的dispatchTouchEvent()方法中會調(diào)用onTouchEvent()方法 
  7.             handled = super.dispatchTouchEvent(event); 
  8.         } else { 
  9.             // 如果孩子控件不為空,就調(diào)用孩子控件的dispatchTouchEvent()方法 
  10.             // 在此處孩子控件也還有可能是ViewGroup,所以就是繼續(xù)調(diào)用ViewGroup的dispatchTouchEvent()方法 
  11.             handled = child.dispatchTouchEvent(event); 
  12.         } 
  13.         event.setAction(oldAction); 
  14.         return handled; 
  15.     } 
  16.    ... 
  17.     transformedEvent.recycle(); 
  18.     return handled; 
  19. 查看ViewGroup中onInterceptTouchEvent()方法: 
  20. public boolean onInterceptTouchEvent(MotionEvent ev) { 
  21.     return false

在ViewGroup中,沒有重寫onTouchEvent()方法,所以調(diào)用的是View中的onTouchEvent()方法。

4、在View類中,首先看一下View中的dispatchTouchEvent()方法:

  1. public boolean dispatchTouchEvent(MotionEvent event) { 
  2.     // If the event should be handled by accessibility focus first
  3.     if (event.isTargetAccessibilityFocus()) { 
  4.         // We don't have focus or no virtual descendant has it, do not handle the event. 
  5.         if (!isAccessibilityFocusedViewOrHost()) { 
  6.             return false
  7.         } 
  8.         // We have focus and got the event, then use normal event dispatch. 
  9.         event.setTargetAccessibilityFocus(false); 
  10.     } 
  11.     boolean result = false
  12.     if (mInputEventConsistencyVerifier != null) { 
  13.         mInputEventConsistencyVerifier.onTouchEvent(event, 0); 
  14.     } 
  15.     // 如果是DOWN事件,重置狀態(tài) 
  16.     final int actionMasked = event.getActionMasked(); 
  17.     if (actionMasked == MotionEvent.ACTION_DOWN) { 
  18.         stopNestedScroll(); 
  19.     } 
  20.     // 過濾觸摸安全策略,如果是false(窗口或控件被遮住了時),直接跳出觸摸事件 
  21.     // 如果應(yīng)該分發(fā)事件(調(diào)用onTouch()或onTouchEvdent()方法),則返回True;如果應(yīng)該刪除事件,則返回false 
  22.     if (onFilterTouchEventForSecurity(event)) { 
  23.         ListenerInfo li = mListenerInfo; 
  24.         if (li != null && li.mOnTouchListener != null 
  25.                 && (mViewFlags & ENABLED_MASK) == ENABLED 
  26.                 && li.mOnTouchListener.onTouch(this, event)) { 
  27.             // 當前控件是可用(enabled)的并且View調(diào)用了setOnTouchListener()方法且返回了true,那么就設(shè)置result為true 
  28.             result = true
  29.         } 
  30.         // result為false,表示沒有調(diào)用setOnTouchListener()方法或該方法返回false,那么就調(diào)用 
  31.         // View的onTouchEvent()方法 
  32.         if (!result && onTouchEvent(event)) { 
  33.             result = true
  34.         } 
  35.     } 
  36.     if (!result && mInputEventConsistencyVerifier != null) { 
  37.         mInputEventConsistencyVerifier.onUnhandledEvent(event, 0); 
  38.     } 
  39.     // 如果是UP事件或CANCEL事件或者是DOWN事件但是該控件不能消費事件時,重置狀態(tài) 
  40.     if (actionMasked == MotionEvent.ACTION_UP || 
  41.             actionMasked == MotionEvent.ACTION_CANCEL || 
  42.             (actionMasked == MotionEvent.ACTION_DOWN && !result)) { 
  43.         stopNestedScroll(); 
  44.     } 
  45.     return result; 

最后查看View的onTouchEvent()方法:

  1. public boolean onTouchEvent(MotionEvent event) { 
  2.     final float x = event.getX(); 
  3.     final float y = event.getY(); 
  4.     final int viewFlags = mViewFlags; 
  5.     final int action = event.getAction(); 
  6.     // 判斷是否有單擊或長按事件 
  7.     final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE 
  8.             || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) 
  9.             || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE; 
  10.     if ((viewFlags & ENABLED_MASK) == DISABLED) { 
  11.         if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) { 
  12.             setPressed(false); 
  13.         } 
  14.         mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN; 
  15.         // 控件disabled 了,他還能消耗觸摸事件,只是不相應(yīng)她了 
  16.         return clickable; 
  17.     } 
  18.     // 如果有代理,調(diào)用代理的方法 
  19.     if (mTouchDelegate != null) { 
  20.         if (mTouchDelegate.onTouchEvent(event)) { 
  21.             return true
  22.         } 
  23.     } 
  24.     // 對點擊事件的具體處理,只要有點擊事件,那么onTouchEvent()方法就返回了 true 
  25.     if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) { 
  26.         switch (action) { 
  27.             case MotionEvent.ACTION_UP: 
  28.                 // ... 
  29.                     // mHasPerformedLongPress 表示長按事件的返回值,如果長按事件的的回調(diào)方法返回了true,那么在同一事件序列中,點擊事件就不會調(diào)用了(否則會同時相應(yīng)長按事件和點擊事件) 
  30.                     if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) { 
  31.                         // This is a tap, so remove the longpress check 
  32.                         removeLongPressCallback(); 
  33.                         // Only perform take click actions if we were in the pressed state 
  34.                         if (!focusTaken) { 
  35.                             // Use a Runnable and post this rather than calling 
  36.                             // performClick directly. This lets other visual state 
  37.                             // of the view update before click actions start. 
  38.                             if (mPerformClick == null) { 
  39.                                 mPerformClick = new PerformClick();  
  40.                             } 
  41.                             if (!post(mPerformClick)) { 
  42.                                 performClickInternal(); // 會調(diào)用 performClick()方法處理單擊事件 
  43.                             } 
  44.                         } 
  45.                     } 
  46.                     if (mUnsetPressedState == null) { 
  47.                         mUnsetPressedState = new UnsetPressedState(); 
  48.                     } 
  49.                     if (prepressed) { 
  50.                         postDelayed(mUnsetPressedState, 
  51.                                 ViewConfiguration.getPressedStateDuration()); 
  52.                     } else if (!post(mUnsetPressedState)) { 
  53.                         // If the post failed, unpress right now 
  54.                         mUnsetPressedState.run(); 
  55.                     } 
  56.                     removeTapCallback(); 
  57.                 } 
  58.                 mIgnoreNextUpEvent = false
  59.                 break; 
  60.             case MotionEvent.ACTION_DOWN: 
  61.                 if (event.getSource() == InputDevice.SOURCE_TOUCHSCREEN) { 
  62.                     mPrivateFlags3 |= PFLAG3_FINGER_DOWN; 
  63.                 } 
  64.                 mHasPerformedLongPress = false
  65.                 if (!clickable) { 
  66.                     checkForLongClick(0, x, y); 
  67.                     break; 
  68.                 } 
  69.                 if (performButtonActionOnTouchDown(event)) { 
  70.                     break; 
  71.                 } 
  72.                 // Walk up the hierarchy to determine if we're inside a scrolling container. 
  73.                 boolean isInScrollingContainer = isInScrollingContainer(); 
  74.                 // 根據(jù)是否在滾動容器中,使用不同方式調(diào)用長按事件的回調(diào) 
  75.                 if (isInScrollingContainer) { 
  76.                     mPrivateFlags |= PFLAG_PREPRESSED; 
  77.                     if (mPendingCheckForTap == null) { 
  78.                         mPendingCheckForTap = new CheckForTap(); // 最終調(diào)用長按事件回調(diào) 
  79.                     } 
  80.                     mPendingCheckForTap.x = event.getX(); 
  81.                     mPendingCheckForTap.y = event.getY(); 
  82.                     postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout()); 
  83.                 } else { 
  84.                     // Not inside a scrolling container, so show the feedback right away 
  85.                     setPressed(true, x, y); 
  86.                     checkForLongClick(0, x, y); // 最終調(diào)用長按事件回調(diào) 
  87.                 } 
  88.                 break; 
  89.             case MotionEvent.ACTION_CANCEL: 
  90.                 // ... 
  91.                 break; 
  92.         } 
  93.         // 只要有點擊事件,那么onTouchEvent()方法就返回了 true 
  94.         return true
  95.     } 
  96.     return false

從上面的代碼來看,只要View的 CLICLABLE和LONG_CLICKABLE有一個為true,那么onTouchEvent()方法就會返回true,而且不管該View是否為DISABLE狀態(tài),其他情況返回false。true表示該控件可以消費事件,false表示該控件不能消費事件。現(xiàn)在,在回過頭來看一下第一張圖,是不是更加的清晰了呢。

5、最后,對于View 的 CLICKABLE 和 LONG_CLICKABLE默認值,LONG_CLICKABLE默認值為false,但是對于CLICKABLE就要根據(jù)具體的View來看了,確切的說是可點擊的View的CLICKABLE值為true,如Button,不可點擊的View的CLICKABLE值為false,比如TextView,但是當我們調(diào)用了View的 setOnClickListener(@Nullable OnClickListener l) 方法或者 setOnLongClickListener(@Nullable OnLongClickListener l) 方法就會將對應(yīng)的值改為true。

  1. public void setOnClickListener(@Nullable OnClickListener l) { 
  2.     if (!isClickable()) { 
  3.         setClickable(true); 
  4.     } 
  5.     getListenerInfo().mOnClickListener = l; 
  6. public void setOnLongClickListener(@Nullable OnLongClickListener l) { 
  7.     if (!isLongClickable()) { 
  8.         setLongClickable(true); 
  9.     } 
  10.     getListenerInfo().mOnLongClickListener = l; 

總結(jié)

  • 對于ViewGroup和View的disatchTouchEvent()和onTouchEvent()方法,return true表示處理事件,事件終結(jié);return false表示不處理事件,讓事件回傳到上一層的onTouchEvent()方法中,也就是父控件中的onTouchEvent()方法中;
  • 對于Activity的disatchTouchEvent()方法,如果沒有重寫,就會通過調(diào)用ViewGroup的disatchTouchEvent()方法開始分發(fā)事件,如果重寫了,那么不管返回true還是false都會消費事件,不在將事件往下分發(fā);
  • 對于dispatchTouchEvent()方法,如果開發(fā)者不重寫,就會走系統(tǒng)中的默認實現(xiàn),在ViewGroup中會調(diào)用ViewGroup的onInterceptTouchEvent()方法,而在View中會直接把事件分發(fā)給View的onTouchEvent()方法處理;
  • 對于ViewGroup而言,如果ViewGroup要自己處理事件,需要重寫onInterceptTouchEvent()方法并且返回true,這樣才會終止事件的傳遞,并調(diào)用ViewGroup的onTouchEvent()方法處理事件,否則調(diào)用系統(tǒng)onInterceptTouchEvent()方法,系統(tǒng)默認的返回值是false,不處理事件將事件傳遞下去;
  • 對于View而言,在View中是沒有onInterceptTouchEvent()方法的,因為他沒有孩子控件,不需要攔截,系統(tǒng)在dispatchTouchEvent()方法中默認會把事件分發(fā)給View的onTouchEvent()方法處理;
  • 在Android中,最開始獲取到事件的是Activity,然后由Activity的dispatchTouchEvent()方法開始分發(fā)事件;
  • 如果一個事件由Activity開始下發(fā),但是所有的控件都不處理事件,最終就會回到Activity的onTouchEvent()方法,如果Activity也不消費事件,那么這個事件就丟失了。

 

責(zé)任編輯:武曉燕 來源: Android開發(fā)編程
相關(guān)推薦

2017-02-21 12:20:20

Android事件分發(fā)機制實例解析

2021-09-01 06:48:16

AndroidGlide緩存

2023-10-08 08:23:44

Android事件邏輯

2016-12-08 10:19:18

Android事件分發(fā)機制

2021-09-02 07:00:01

Glide流程Android

2011-06-23 14:05:32

Qt 事件機制

2021-08-10 20:41:33

AndroidApp流程

2021-09-30 07:36:51

AndroidViewDraw

2021-09-09 06:55:43

AndroidViewDragHel原理

2021-09-03 07:27:38

AndroidGlide管理

2021-10-03 15:08:32

Android

2021-09-07 06:40:25

AndroidLiveData原理

2021-09-06 13:12:05

前端JavaScript編程

2010-08-06 10:03:42

Flex事件

2021-10-15 09:19:17

AndroidSharedPrefe分析源碼

2021-09-16 06:44:04

Android進階流程

2021-08-12 16:28:10

AndroidHandleLooper

2011-06-23 13:10:39

Python 對象機制

2021-08-05 20:39:34

AndroidKotlinStandard.kt

2016-12-12 14:55:01

AndroidAndroid Vie
點贊
收藏

51CTO技術(shù)棧公眾號

国产aaaaaaaaa| 精品国产999| 欧美一区永久视频免费观看| www日韩av| 在线观看日本黄色| 久久视频免费| 天堂成人免费av电影一区| 欧美日韩亚洲不卡| 欧美精品中文字幕一区二区| 久久免费视频播放| 9999精品免费视频| 欧美韩国日本不卡| 欧洲日韩成人av| 国产精品无码一区二区三| aa在线视频| 亚洲最大在线| 欧美性猛交xxxx偷拍洗澡| 国产99视频精品免费视频36| 夫妻性生活毛片| 成人亚洲视频| 国产欧美日韩三区| 都市激情久久久久久久久久久| 久久久久久久久久一区二区三区| 久久a爱视频| 一区二区欧美视频| 国产精品久久久久久久天堂第1集| 美国精品一区二区| 精品伊人久久久| 91精品国产综合久久久久久久 | 性欧美videos| 日韩黄色碟片| 国产欧美精品一区二区色综合朱莉| 国产精品老女人精品视频| 精品无码人妻一区二区免费蜜桃| 国产资源在线观看入口av| 成人一区二区三区在线观看| 色在人av网站天堂精品| 先锋资源在线视频| 欧美草逼视频| 成人免费视频视频在线观看免费| 国产成人高清激情视频在线观看 | 国产精品999久久久| 91久久精品无嫩草影院| 久久久久国产精品厨房| 国产成人拍精品视频午夜网站| 神马午夜精品91| 欧美久久综合网| 欧美日韩高清一区二区不卡| 五月天色婷婷综合| 国产片在线播放| 欧美特黄a级高清免费大片a级| 日韩亚洲电影在线| 久久综合色视频| 国产污视频在线| 寂寞少妇一区二区三区| 久久综合免费视频影院| 激情小说欧美色图| 六月婷婷综合| 自拍偷自拍亚洲精品播放| 99国产视频| 国产www免费观看| 99热免费精品在线观看| 一区二区福利视频| 久久久久久久高清| 9765激情中文在线| 日本一区二区三区免费乱视频| 精品乱子伦一区二区三区| 自拍偷拍福利视频| 欧美成人中文| 亚洲午夜未满十八勿入免费观看全集| 两根大肉大捧一进一出好爽视频| 男男激情在线| 国产九九视频一区二区三区| 91精品国产91久久久| 日本黄色特级片| 成人免费91| 狠狠色狠狠色综合日日小说| 99热一区二区三区| 91网在线看| 亚洲午夜视频在线| 亚洲一区免费看| 欧美熟妇乱码在线一区| 日韩中文字幕不卡| 欧美极品少妇与黑人| 国产精品免费无码| 久久久久久久久99精品大| 国产视频久久久| 亚洲黄色片免费| av不卡一区二区| 国产视频精品自拍| 国产中文av在线| 一本色道久久综合亚洲精品酒店 | 亚洲一区色图| 亚洲免费电影一区| 潘金莲一级淫片aaaaa| 国产精品任我爽爆在线播放| 91精品国产综合久久久久久| 国产黑丝一区二区| 久久wwww| 亚洲美女在线视频| 国产又色又爽又高潮免费| 亚洲亚洲免费| 久久精品在线播放| 纪美影视在线观看电视版使用方法| 中文字幕日韩在线| 亚洲人精选亚洲人成在线| 中国黄色片视频| 看亚洲a级一级毛片| 亚洲精品自在久久| 国产一二三四区| 久久精品人人做人人爽电影蜜月| 久久91超碰青草是什么| 人妻 日韩精品 中文字幕| 影音先锋日韩资源| 久久99热精品这里久久精品| 久久久久久久久久影院| 91久久综合| 91国内精品久久| 国产伦精品一区二区三区免.费| 日本成人超碰在线观看| 国产成人精品在线观看| 国产黄色片免费观看| 国产欧美精品一区二区色综合朱莉| 国产女主播av| 99在线视频观看| 欧美在线小视频| 日韩精品一区二区三区不卡| 自拍网站在线观看| 色菇凉天天综合网| 久久久久久久久久久久91| 91成人在线| 日韩黄在线观看| 国产熟妇久久777777| 国产精品片aa在线观看| 在线丨暗呦小u女国产精品| 2019男人天堂| 久久九九99| 精品一区二区国产| 国产美女情趣调教h一区二区| 伊人色综合久久天天| 成人在线播放网址| 亚洲女同av| 亚洲成av人乱码色午夜| 少妇伦子伦精品无吗| 97久久视频| 欧美激情在线视频二区| aaa一区二区| 亚洲欧美日韩国产一区二区三区| 黑人巨大国产9丨视频| 国精一区二区三区| 日韩一区二区三| 麻豆视频在线免费看| 老司机精品视频一区二区三区| 亚洲a∨日韩av高清在线观看| 国产美女明星三级做爰| 中文字幕一区在线观看视频| 99在线免费视频观看| 麻豆国产一区| 欧美福利视频在线观看| 中文字幕av第一页| 国产99久久久国产精品潘金 | 国产精品无码一区二区在线| 精品自拍偷拍| 欧美专区第一页| 精品电影在线| 亚洲精品视频在线看| 999热精品视频| 奇米亚洲欧美| 欧美另类高清videos| av免费在线观看不卡| 一区二区三区中文字幕| xxxwww国产| 视频一区视频二区中文字幕| 亚洲精品一品区二品区三品区| 色呦呦在线播放| 在线观看视频一区二区欧美日韩| 波多野结衣福利| 热久久一区二区| 波多野结衣激情| 超碰成人97| 国产成人精品免高潮在线观看| 性xxxx视频播放免费| 伊人性伊人情综合网| 天堂www中文在线资源| 久久蜜桃精品| 国产精品波多野结衣| 岛国av一区| 国产999精品久久久| 天天操天天插天天射| 亚洲激情网站免费观看| 一级特级黄色片| 另类专区欧美蜜桃臀第一页| 国产成人亚洲综合无码| 国产精品日韩精品在线播放| 久久久人成影片一区二区三区| 一本色道久久综合亚洲| 国产丝袜美腿一区二区三区| 91九色丨porny丨国产jk| 99re8精品视频在线观看| 久久久久久午夜| av资源种子在线观看| 色狠狠综合天天综合综合| 日本一级特级毛片视频| 久久在线免费观看| 777久久久精品一区二区三区| 欧美女优在线视频| 91精品国产一区二区三区动漫| 日本免费中文字幕在线| 欧美亚洲动漫制服丝袜| 精品人妻无码一区| 国产 日韩 欧美大片| 在线观看国产中文字幕| 日韩欧美午夜| 国产欧美日韩亚洲精品| a√资源在线| 欧美丰满少妇xxxbbb| 污污的视频在线免费观看| 91美女蜜桃在线| 人妻丰满熟妇av无码区app| 精品国产中文字幕第一页| 国产91视觉| 成人午夜888| 国产精品扒开腿做爽爽爽男男| 日本大片在线观看| 欧洲av一区二区嗯嗯嗯啊| 黄色小视频在线免费看| 亚洲欧美色图小说| 一级少妇精品久久久久久久| 美女性感视频久久| av磁力番号网| 日韩精品一区二区久久| 欧美亚洲免费高清在线观看| 黄色视屏在线免费观看| 欧美大片免费观看| 哥也色在线视频| 精品久久久久久久久久久久久久久 | 日产精品一区二区| 久久av二区| 亚洲一区资源| 韩剧1988在线观看免费完整版 | 日韩欧美在线网站| 国产精品毛片一区二区在线看舒淇 | 亚洲综合网在线观看| 成人亚洲一区二区一| 久久久久亚洲av片无码v| 久久婷婷蜜乳一本欲蜜臀| 欧美高清性xxxxhd| 亚欧日韩另类中文欧美| 国产精品成人免费电影| 欧美大电影免费观看| 亚洲性日韩精品一区二区| 视频午夜在线| 欧美一区二视频| 国产老女人乱淫免费| 337p亚洲精品色噜噜| 国产裸体永久免费无遮挡| 在线观看91av| av网站在线免费看| 欧美一级片在线观看| 精品人妻无码一区二区三区蜜桃一 | 欧美一区影院| 天堂av在线中文| 欧美日韩国产亚洲一区| 成人国产在线看| 国产字幕视频一区二区| 欧美一区视久久| 国产欧美日韩| 亚洲欧美国产一区二区| 五月天久久久| 麻豆av一区| 欧美成人精品一级| 国产精品久久久久久久久久久久午夜片 | 日韩一级视频在线观看| 久久香蕉国产线看观看99| 国产传媒国产传媒| 国产精品乱人伦中文| www.超碰97| 国产伦精品一区二区三区在线观看| 色婷婷.com| 天堂成人国产精品一区| 一区二区三区韩国| 一本不卡影院| 丁香婷婷激情网| 日韩精彩视频在线观看| 五月天中文字幕在线| 国产成人av在线影院| 中文精品在线观看| 亚洲天堂av老司机| 中国一级免费毛片| 欧美日韩国产电影| 成人午夜免费在线观看| 91精品免费在线观看| 欧美 日韩 国产 在线| 中文字幕久久亚洲| 91av久久| 国产一区二区在线免费视频| 超碰成人免费| 在线观看一区二区三区三州| 欧洲美女日日| 大胆欧美熟妇xx| 一个色综合网| 日韩中文字幕在线视频观看| 蜜桃在线一区二区三区| 国产婷婷在线观看| 中文字幕一区二区三区四区| 男人的天堂一区二区| 亚洲va欧美va人人爽午夜| 久久综合加勒比| 91久久久免费一区二区| 亚洲国产精品国自产拍久久| 日韩视频不卡中文| 国产资源在线播放| 久久久久中文字幕2018| 亚洲精品一区二区在线播放∴| 国产日韩三区| 欧美黄色网视频| 美女被啪啪一区二区| 亚洲影视一区| 一区二区三区 日韩| aaa亚洲精品一二三区| 无码一区二区精品| 亚洲欧美偷拍三级| 中文字幕丰满人伦在线| 欧美精品欧美精品系列| 久久久资源网| 国产91精品视频在线观看| 台湾佬中文娱乐久久久| 国产麻豆一区二区三区在线观看| 四季av在线一区二区三区 | 免费不卡av| 91亚洲国产精品| 91精品尤物| 黄色一级片网址| 久久精品99国产精品| 国产精品久久免费观看| 色天使色偷偷av一区二区| 天堂v视频永久在线播放| 国产+成+人+亚洲欧洲| 中文字幕日韩在线| 97免费视频观看| 国产成人精品免费视频网站| 国产探花在线免费观看| 91精品国产免费久久综合| 欧美一级二级三级区| 久久理论片午夜琪琪电影网| 精品视频一二| 国产日韩欧美大片| 国产麻豆欧美日韩一区| 欧美精品99久久久| 欧美丝袜第一区| 欧美白人做受xxxx视频| 热久久99这里有精品| 亚洲人亚洲人色久| 不要播放器的av网站| 国产日韩欧美制服另类| 中文在线观看av| 久久精品国产精品亚洲| 婷婷视频一区二区三区| 视频在线99| 国内精品福利| 亚洲日本久久久| 欧美日韩一区二区三区在线免费观看| 国产熟女一区二区三区四区| 久久亚洲欧美日韩精品专区| 无人区乱码一区二区三区| www插插插无码视频网站| 97aⅴ精品视频一二三区| 玖玖爱这里只有精品| 日韩精品综合一本久道在线视频| 日本中文字幕一区二区有码在线| 国外视频精品毛片| 伊人成综合网yiren22| 婷婷六月天在线| 亚洲另类一区二区| 日本xxxx人| 国产精品第100页| 亚洲国产一区二区三区在线播放 | 国产午夜精品视频一区二区三区| 国内精品在线播放| 日韩福利片在线观看| 欧美一区二区视频在线观看2020 | 超碰97在线免费观看| 成人激情免费在线| 曰本一区二区三区视频| 国产精品一区二区小说| 亚洲码国产岛国毛片在线| 污污网站在线免费观看| 国产精品极品在线| 欧美阿v一级看视频| 日本黄色特级片| 91精品国产一区二区三区| 多野结衣av一区| 在线免费观看一区二区三区| zzijzzij亚洲日本少妇熟睡| 国产真人无遮挡作爱免费视频| 久久精品国产亚洲精品| 日本妇女一区| 能看毛片的网站| 欧美日韩精品一二三区| 99riav视频在线观看|