當觸摸android裝置的螢幕時,android系統將建立一個MotionEvent對象。
MotionEvent對象是描述了一組和使用者觸摸相關的事件序列,它包含了觸摸的位置,時間,動作等資訊。
發生觸摸時,這個MotionEvent對象會被傳遞到合適的方法中作為方法參數(通常是回調方法)。
getAction()方法描述了當前的動作:
ACTION_DOWN:手指按下 值:0ACTION_MOVE:手指移動 值:2ACTION_UP:手指彈起 值:1
像OnTouchListener的onTouch()回調方法就接受一個系統產生的MotionEvent,這類回調方法傳回值為boolean,如果返回true,那麼實現OnTouchListener的View會告訴android:我對此觸摸的事件序列以及以後會發生的觸摸事件序列都不感興趣,請你尋找下一個感興趣的View。android系統下次收到觸摸事件不會將MotionEvent的執行個體發送給這個View,而是尋找下一個接受MotionEvent的View。前後2個MotionEvent是同一個對象,而不是新產生的(下面的例子會驗證這個說法)
下面是Demo:
布局檔案: activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity" > <RelativeLayout android:id="@+id/truelayout" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@android:color/black" android:layout_weight="1" android:tag="truelayout" > <com.example.mytouchdemo.TrueButton android:id="@+id/truebutton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="@android:color/white" android:tag="truebutton" android:text="true" /> <com.example.mytouchdemo.FalseButton android:id="@+id/falsebutton" android:layout_below="@+id/truebutton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="@android:color/white" android:tag="falsebutton" android:text="false" /> </RelativeLayout> <RelativeLayout android:id="@+id/falselayout" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1" android:tag="falselayout" android:background="@android:color/darker_gray" > <com.example.mytouchdemo.TrueButton android:id="@+id/falsebutton2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="@android:color/white" android:tag="falsebutton2" android:text="false" /> </RelativeLayout></LinearLayout>
TrueButton和FalseButton都是自訂控制項,繼承了BooleanButton,BooleanButton繼承了Button
BooleanButton的onTouchEvent回調在接收了MotionEvent對象,傳回值取決於是trueButton.還是falseButton..
public abstract class BooleanButton extends Button { protected boolean myValue() { return false; }public BooleanButton(Context context, AttributeSet attrs) {super(context, attrs);}@Overridepublic boolean onTouchEvent(MotionEvent event) {Log.v(tag, "------"+ getTag() +"--------------"); Log.v(tag, "action:" + event.getAction()); Log.v(tag, "and I'm returning " + myValue()); Log.v(tag, "MotionEvent's hashcode="+event.hashCode()+""); return(myValue());}private String tag = "test";}
public class TrueButton extends BooleanButton { protected boolean myValue() { return true; } public TrueButton(Context context, AttributeSet attrs) {super(context, attrs);}}
public class FalseButton extends BooleanButton {public FalseButton(Context context, AttributeSet attrs) {super(context, attrs);}}
MainActivity.java
public class MainActivity extends Activity implements OnTouchListener{@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//上半部分布局,ontouch方法返回trueRelativeLayout trueLayout = (RelativeLayout)findViewById(R.id.truelayout);//true按鈕,ontouch方法和自身的onTouchEvent方法返回trueButton trueButton = (Button)findViewById(R.id.truebutton);//false按鈕,ontouch方法和自身的onTouchEvent方法返回falseButton falseButton = (Button)findViewById(R.id.falsebutton);Button falseButton2 = (Button)findViewById(R.id.falsebutton2);//下半部分布局,ontouch方法返回falseRelativeLayout falseLayout = (RelativeLayout)findViewById(R.id.falselayout);//註冊ontouch事件trueLayout.setOnTouchListener(this);falseLayout.setOnTouchListener(this);trueButton.setOnTouchListener(this);falseButton.setOnTouchListener(this);falseButton2.setOnTouchListener(this);}@Overridepublic boolean onTouch(View v, MotionEvent event) {String tagStr = v.getTag().toString().substring(0, 4);//tag前4位元為true的控制項,onTouch返回true,否則返回falseif ("true".equals(tagStr)) {Log.v(tag, "------"+ v.getTag() +"--------------"); Log.v(tag, "action:" + event.getAction()); Log.v(tag, "and I'm returning " + tagStr); Log.v(tag, "MotionEvent's hashcode="+event.hashCode()+"");return true;} else {return false;}}String tag = "test";}
現在啟動程式,點擊上半布局的TrueButton,檢查logcat輸出:
06-17 22:26:31.662: V/test(5766): ------truebutton--------------
06-17 22:26:31.662: V/test(5766): action:0
06-17 22:26:31.662: V/test(5766): and I'm returning true
06-17 22:26:31.662: V/test(5766): MotionEvent's hashcode=1097894832
06-17 22:26:31.682: V/test(5766): ------truebutton--------------
06-17 22:26:31.682: V/test(5766): action:2
06-17 22:26:31.682: V/test(5766): and I'm returning true
06-17 22:26:31.682: V/test(5766): MotionEvent's hashcode=1097894832
06-17 22:26:31.702: V/test(5766): ------truebutton--------------
06-17 22:26:31.702: V/test(5766): action:2
06-17 22:26:31.702: V/test(5766): and I'm returning true
06-17 22:26:31.702: V/test(5766): MotionEvent's hashcode=1097894832
06-17 22:26:31.712: V/test(5766): ------truebutton--------------
06-17 22:26:31.712: V/test(5766): action:1
06-17 22:26:31.712: V/test(5766): and I'm returning true
06-17 22:26:31.712: V/test(5766): MotionEvent's hashcode=1097894832
可以看到所有的事件都在truebutton中被接收了,包括按下action:0,移動action:2,彈起action:1。這是因為onTouch方法中返回了true。
現在點擊上半布局的FalseButton,檢查logcat輸出:
06-17 22:29:06.892: V/test(5766): ------falsebutton--------------
06-17 22:29:06.892: V/test(5766): action:0
06-17 22:29:06.892: V/test(5766): and I'm returning false
06-17 22:29:06.892: V/test(5766): MotionEvent's hashcode=1097894832
06-17 22:29:06.892: V/test(5766): ------truelayout--------------
06-17 22:29:06.892: V/test(5766): action:0
06-17 22:29:06.902: V/test(5766): and I'm returning true
06-17 22:29:06.902: V/test(5766): MotionEvent's hashcode=1097894832
06-17 22:29:06.912: V/test(5766): ------truelayout--------------
06-17 22:29:06.912: V/test(5766): action:2
06-17 22:29:06.912: V/test(5766): and I'm returning true
06-17 22:29:06.912: V/test(5766): MotionEvent's hashcode=1097894832
06-17 22:29:06.932: V/test(5766): ------truelayout--------------
06-17 22:29:06.932: V/test(5766): action:2
06-17 22:29:06.932: V/test(5766): and I'm returning true
06-17 22:29:06.932: V/test(5766): MotionEvent's hashcode=1097894832
06-17 22:29:06.952: V/test(5766): ------truelayout--------------
06-17 22:29:06.952: V/test(5766): action:2
06-17 22:29:06.952: V/test(5766): and I'm returning true
06-17 22:29:06.952: V/test(5766): MotionEvent's hashcode=1097894832
06-17 22:29:06.972: V/test(5766): ------truelayout--------------
06-17 22:29:06.972: V/test(5766): action:1
06-17 22:29:06.972: V/test(5766): and I'm returning true
06-17 22:29:06.972: V/test(5766): MotionEvent's hashcode=1097894832
看到falseButton和包含它的布局truelayout都接收了按下事件,但是之後的移動,彈起都只有truelayout接收。因為falseButton在onTouch中返回false,這表明它不再接收之後的觸摸時間,交由其他View處理,所以android系統找到了truelayout,因為truelayout的onTouch返回true,所以它處理所有的觸摸事件序列。並且注意到,2次接受的motionEvent的hashcode都是一樣的,說明他們是一個motionevent
下面點擊下半個布局的falsebutton,logcat輸出:
06-17 22:36:57.942: V/test(7164): ------falsebutton2--------------
06-17 22:36:57.942: V/test(7164): action:0
06-17 22:36:57.942: V/test(7164): and I'm returning false
06-17 22:36:57.942: V/test(7164): MotionEvent's hashcode=1098095696
可以看到非常悲劇,因為包含它的布局falselayout返回的也是false,現在沒有view可以接收motionevent對象,所以不能監聽到移動和彈起的事件
下面看一個特殊的情況:按住上半部分的trueButton並且移動手指一直到移出button外,移到relativelayout上。看log:
06-17 22:39:41.442: V/test(7164): ------truebutton--------------
06-17 22:39:41.442: V/test(7164): action:0
06-17 22:39:41.442: V/test(7164): and I'm returning true
06-17 22:39:41.442: V/test(7164): MotionEvent's hashcode=1098095696
06-17 22:39:41.462: V/test(7164): ------truebutton--------------
06-17 22:39:41.462: V/test(7164): action:2
06-17 22:39:41.462: V/test(7164): and I'm returning true
06-17 22:39:41.462: V/test(7164): MotionEvent's hashcode=1098095696
………………………………………………
你會發現就算出了button的範圍它依然監聽觸摸事件,這是因為它返回true,代表這一系列觸摸動作都由我來處理,這也是“觸摸序列”的含義