編輯:關於Android編程
談到android事件處理,最復雜的就是對Touch事件的處理,因為Touch事件包括:down, move, up, cancle和多點觸摸等多種情況,多點觸摸的情況先不討論,因為Touch有這麼多的狀態,所以Touch相對來說是最難處理的,下面就來討論一下android系統是如何處理Touch事件的.
1.說到事件處理,首先我們要明白,為什麼要處理事件,要了解android系統本身對事件的一個處理過程.在實際的開發中,我們如果都用系統的基本控件,那是不需要去處理事件的,但是如果我們用復雜的布局嵌套去做一些特殊的需求,例如:ScrollView中嵌套ListView,ScrollView嵌套ViewPager等,則會產生事件沖突,所以,由於事件沖突的存在,我們要去處理這些沖突,只有了解android的事件處理機制,才能有效的去處理事件沖突.還有就是如果我們要新開發一個組件,則組件的所有事件都要我們自己去做處理,這種情況也需要我們去處理事件.所以:由於存在以上說到的兩種情況,我們要自己處理事件.
2.有了處理事件的動機後,接下來就要了解android系統本身是如何處理復雜的事件的.android系統為所有的事件提供了三個相關的方法,以下只以Touch事件為例說明.
這三個方法分別是:
dispatchTouchEvent(MotionEvent ev); (Activity, ViewGroup, View都有此方法)
onInterceptTouchEvent(MotionEvent ev); (只有ViewGroup有)
onTouchEvent(MotionEvent); (Activity, ViewGroup, View都有此方法)
要想了解android系統是如何對事件進行一步一步的處理,這三個方法是必須要掌握的.其中:dispatchTouchEvent(MotionEvent ev);方法是用來對事件進行分發的,即將事件分發到目標控件,onInterceptTouchEvent(MotionEvent ev)是用來過濾事件的,即進行事件的攔截,也就是是否要向下傳遞事件,onTouchEvent(MotionEvent ev)才是最終用來處理事件的,也就是說我們平常重寫onTouchEvent時,其實,系統已經默認幫我們調用了前兩個方法.下面就來詳細分析一下三個方法.
首先要提的是,android系統對本件的處理是一層一層向下傳遞處理(樹形處理).那這棵樹是從那來的呢..就是我們的布局樹,一個布局,無論是代碼編寫的布局還是xml生成的布局,android系統對它進行解析時都是將其組裝成一棵UI樹,最外層布局是整個UI樹的根.知道這個以後,再來分析事件的處理.
處理流程:當我們的手指觸摸到手機屏幕時,當前處於onStart()狀態的Activity最先接收到此Touch事件下的ACTON_DOWN,然後開始調用它自己dispatchTouchEvent()開始進行DOWN事件分發,如果此方法返回true,則Activity不向下分發事件,則整個布局都不會收到DOWN事件,TouchEvent直接到Activity的onTouchEvent()方法進行事件處理.如果返回false,則表示DOWN是要被分發到下層的,此時DOWN事件被直接分發(因為沒有過濾方法)到UI樹的根布局(即最外層的布局),根布局拿到DOWN事件時,執行自己的dispatchTouchEvent方法,返回true,則事件直接交到根布局的onTouchEvent()中進行處理,false則表示還得向下分發,此時事件被傳遞到根布局的onInterceptTouchEvent()方法中,如果此方法返回true,表示要對此事件進行過濾,則此DOWN事件又直接進行到根布局的onTouchEvent()方法直接處理,false則,要根布局不對事件進行過濾,DOWN事件繼續向下傳遞,直到達到目標組件後,目標組件調用自己的dispatchTouchEvent()方法,由於是目標組件,直接分發事件到自己的onTouchEvent方法中,目標組件如果處理完這個DOWN事件後返回true,表示該事件被消費完畢,不再向上層傳遞,如果返回false,則表示沒有消費完這個DOWN事件,DOWN向上傳遞到自己的父組件中,父組件再進行DOWN事件的處理.一直向上傳遞直到事件被扔到虛擬機.DOWN事件才算處理完成,接著調用MOVE,MOVE完了UP,整個流程與DOWN是一樣的.
這裡要強調一點的是:如果一個組件沒有接收到DOWN事件,那麼一定接收不到MOVE,UP事件。
通過以上的流程,我們可以明白:android系統對任何一個事件的處理都是這樣的,分發事件,過濾事件,處理事件,下一個事件, 分發事件,過濾事件,處理事件……一直這樣循環去處理所有的事件的。即:事件的分發,過濾是從根到葉的,處理則是從葉再到根的。
下面是我將上面的文字流程畫的一張處理流程圖:
從圖上看,我們可以更直觀的感受整個Touch事件的處理流。
<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4KPHA+ICAgICAgMy69ssHLYW5kcm9pZM+1zbPKx8jnus60psDtysK8/rXE1fu49sH3s8yjrMTHztLDx8q1vMq5pNf31tDI57rOyKW0psDtysK8/sTYoa2hrc/Cw+a9q87S1q7HsLSmwO25/bXE0ru49sD919PAtLfWzvahozwvcD4KPHA+ICAgICAgytfPyM7Sw8e007j5tb3StsiltKbA7crCvP6jrLT6wuvI58/Co7o8L3A+CjxwPiAgICAgIDxwcmUgY2xhc3M9"brush:java;">package com.micen.buyers.view.category;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.ScrollView;
import com.micen.buyers.util.Util;
public class MyScrollView extends ScrollView {
private float mLastMotionY;
private float mLastMotionX;
public MyScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
/**
* 通過重寫此方法,達到對事件的處理
*/
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
// TODO Auto-generated method stub
final float x = ev.getX();
final float y = ev.getY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastMotionX = x;
mLastMotionY = y;
break;
case MotionEvent.ACTION_MOVE:
if (Math.abs(y - mLastMotionY) > Util.dip2px(20)
&& Math.abs(x - mLastMotionX) < Util.dip2px(5)) {
return true; // 如果MOVE事件的縱坐標超過20px, 橫向小於5dp
// 則認為是滑動scrollview,返回true,則事件不向下分發,直接傳入到
// onTouchEvent方法中,否則,認為滑動事件不屬於scrollview處理,允許分發
}
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_CANCEL:
break;
}
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// TODO Auto-generated method stub
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
Log.e("--------->", "user want to scroll");
return super.onTouchEvent(ev);
}
}
上述代碼,我們是重寫了ScrollView的dispatchTouchEvent()達到對事件的一個特殊處理,如果滿足了我們的規則,則直接到onTouchEvent()中處理,否則,事件被發送到 onInterceptTouchEvent()中執行過濾,由於此方法中沒有過濾,則下發傳遞事件.
同樣的效果,我們從葉子開始處理事件沖突,代碼如下:
package com.micen.buyers.view.category; import android.content.Context; import android.os.Handler; import android.os.Message; import android.util.AttributeSet; import android.view.MotionEvent; import com.micen.buyers.util.Util; public class MyViewPager extends ViewPager { private boolean flag = true; private float mLastMotionY; private float mLastMotionX; public MyViewPager(Context context) { super(context); } public MyViewPager(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { final float x = ev.getX(); final float y = ev.getY(); switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: setPullToScrollViewStatus(true); //先默認父控件不接收滑動事件,事件直接傳遞到子滑動組件 flag = true; mLastMotionX = x; mLastMotionY = y; mHandler.sendEmptyMessage(1); break; case MotionEvent.ACTION_MOVE: if (flag) { if (Math.abs(y - mLastMotionY) > Util.dip2px(20) && Math.abs(x - mLastMotionX) < Util.dip2px(5)) { flag = false; setPullToScrollViewStatus(false); //滿足條件,父滑動控件將事件過濾掉了,不再傳到ViewPager中了. } } break; case MotionEvent.ACTION_UP: setPullToScrollViewStatus(false); case MotionEvent.ACTION_CANCEL: setPullToScrollViewStatus(false); break; } return super.dispatchTouchEvent(ev); } @Override public boolean onInterceptTouchEvent(MotionEvent event) { return super.onInterceptTouchEvent(event); } @Override public boolean onTouchEvent(MotionEvent event) { return super.onTouchEvent(event); } private void setPullToScrollViewStatus(boolean disallowIntercept) { //調用父控件的requestDisallowInterceptTouchEvent()方法,傳入true,則等將於父控件的onTInterceptTouchEvent返回false, //不過濾,否則,父控件過濾掉事件,不再向下傳遞. getParent().getParent().requestDisallowInterceptTouchEvent(disallowIntercept); } }
本文實例講述了Android簡單實現自定義流式布局的方法。分享給大家供大家參考,具體如下:首先來看一下 手淘HD - 商品詳情 - 選擇商品屬性 頁面的UI商品有很多尺碼
前段時間,我看到了一篇關於Android動畫的文章,十分喜歡文章作者的筆風,可惜每個人的筆風都不同,不過我倒是實現了一個類似的Switch組件,項目地址為https://
一、 View 的基礎知識View 是 Android 中所有空間的基類。1、 View 的位置參數View 的位置主要有四個頂點決定的, top、left、right、
Android的彈窗效果有很多種,就最簡單而言,就可以調用一個AlertDialog彈窗顯示,可是要自定義彈窗效果有以下這種方法,就我個人而言感覺挺方便的,適用性也挺廣的