日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

如何理解MotionEvent对象

發布時間:2023/12/15 综合教程 26 生活家
生活随笔 收集整理的這篇文章主要介紹了 如何理解MotionEvent对象 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

如何理解MotionEvent對象,相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。

一、MotionEvent對象

當用戶觸摸屏幕時,將創建一個MontionEvent對象。MotionEvent包含了關于發生觸摸的位置和時間的信息,以及觸摸事件的其他細節。

獲取MontionEvent對象的方法有:

1.重載Activity中的onTouchEvent(MotionEvent event)方法;

2.View對象調用View.setOnTouchListener接口實現onTouch(View v, MotionEvent event)方法;

獲得MontionEvent對象后,可以通過以下常用方法進一步獲取觸控事件的具體信息:

event.getAction()//獲取觸控動作比如ACTION_DOWN
event.getPointerCount();//獲取觸控點的數量,比如2則可能是兩個手指同時按壓屏幕
event.getPointerId(nID);//對于每個觸控的點的細節,我們可以通過一個循環執行getPointerId方法獲取索引
event.getX(nID);//獲取第nID個觸控點的x位置
event.getY(nID);//獲取第nID個點觸控的y位置
event.getPressure(nID);//LCD可以感應出用戶的手指壓力,當然具體的級別由驅動和物理硬件決定的
event.getDownTime()//按下開始時間
event.getEventTime()//事件結束時間
event.getEventTime()-event.getDownTime());//總共按下時花費時間

觸控對象中的主要相關常量:

/**
*用于多點觸控進行操作
*/
publicstaticfinalintACTION_MASK=0xff;

/**
*手指按下時
*/
publicstaticfinalintACTION_DOWN=0;

/**
*手指放開時
*/
publicstaticfinalintACTION_UP=1;

/**
*移動操作時
*/
publicstaticfinalintACTION_MOVE=2;

/**
*用戶無規則的操作時可能觸發.此操作用于表明,一個觸摸序列在未發生任何實際操作的情況下結束.
*/
publicstaticfinalintACTION_CANCEL=3;

/**
*觸摸操作發生在窗口之外,但仍然能夠找到該操作的特殊情況下設置.
*/
publicstaticfinalintACTION_OUTSIDE=4;

/**
*
*/
publicstaticfinalintACTION_POINTER_DOWN=5;

/**
*
*/
publicstaticfinalintACTION_POINTER_UP=6;

/**
*
*/
publicstaticfinalintACTION_HOVER_MOVE=7;

/**
*Android3.1開始引入的常量,來自于輸入設備(如鼠標),而非觸摸屏.
*/
publicstaticfinalintACTION_SCROLL=8;

二、MotionEvent對象處理過程

范例視圖如下:

activity_main.xml代碼如下:

<RelativeLayoutxmlns: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"
tools:context=".MainActivity">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:orientation="vertical">

<LinearLayout
android:id="@+id/layout1"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="vertical"
android:tag="true_Layout">

<com.example.d_motionevent.TrueButton
android:id="@+id/trueButton1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:tag="true_Btn_上"
android:text="true_Btn_上"/>

<com.example.d_motionevent.FalseButton
android:id="@+id/falseButton1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:tag="false_Btn_上"
android:text="false_Btn_上"/>

</LinearLayout>

<LinearLayout
android:id="@+id/layout2"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="#ff00ff"
android:orientation="vertical"
android:tag="false_Layout">

<com.example.d_motionevent.TrueButton
android:id="@+id/trueButton2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:tag="true_Btn_下"
android:text="true_Btn_下"/>

<com.example.d_motionevent.FalseButton
android:id="@+id/falseButton2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:tag="false_Btn_下"
android:text="false_Btn_下"/>

</LinearLayout>
</LinearLayout>

</RelativeLayout>

自定義Button類代碼如下:

packagecom.example.d_motionevent;

importandroid.content.Context;
importandroid.util.AttributeSet;
importandroid.util.Log;
importandroid.view.MotionEvent;
importandroid.widget.Button;

/**
*
*@authorzeng
*/
publicclassBooleanButtonextendsButton
{
publicBooleanButton(Contextcontext,AttributeSetattrs)
{
super(context,attrs);
}

protectedbooleanmyValue()
{
returnfalse;
}

@Override
publicbooleanonTouchEvent(MotionEventevent)
{
StringmyTag=this.getTag().toString();

Log.e(myTag,"==========="+"Button的onTouchEvent方法"+"===========");
Log.e(myTag,MainActivity.describeEvent(this,event));
Log.e(myTag,"父類onTouchEvent()="+super.onTouchEvent(event));
Log.e(myTag,"該buttontouch="+myValue());
returnmyValue();
}
}

TrueButton類代碼:

packagecom.example.d_motionevent;

importandroid.content.Context;
importandroid.util.AttributeSet;

/**
*onTouchEvent返回true的Button.
*@authorzeng
*/
publicclassTrueButtonextendsBooleanButton
{
publicTrueButton(Contextcontext,AttributeSetattrs)
{
super(context,attrs);
}

@Override
protectedbooleanmyValue()
{
returntrue;
}
}

FalseButton類代碼:

packagecom.example.d_motionevent;

importandroid.content.Context;
importandroid.util.AttributeSet;

/**
*onTouchEvent返回false的Button.
*@authorzeng
*/
publicclassFalseButtonextendsBooleanButton
{
publicFalseButton(Contextcontext,AttributeSetattrs)
{
super(context,attrs);
}
}

MainActivity.class代碼如下:

packagecom.example.d_motionevent;

importandroid.os.Bundle;
importandroid.app.Activity;
importandroid.util.Log;
importandroid.view.MotionEvent;
importandroid.view.View;
importandroid.view.View.OnTouchListener;
importandroid.widget.Button;

/**
*地址:http://glblong.blog.51cto.com/3058613/1557683
*@authorzeng
*/
publicclassMainActivityextendsActivityimplementsOnTouchListener
{
@Override
protectedvoidonCreate(BundlesavedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

Viewlayout1=findViewById(R.id.layout1);
layout1.setOnTouchListener(this);

ButtontrueButton1=(Button)findViewById(R.id.trueButton1);
trueButton1.setOnTouchListener(this);
ButtonfalseButton1=(Button)findViewById(R.id.falseButton1);
falseButton1.setOnTouchListener(this);

Viewlayout2=findViewById(R.id.layout2);
layout2.setOnTouchListener(this);

ButtontrueButton2=(Button)findViewById(R.id.trueButton2);
trueButton2.setOnTouchListener(this);
ButtonfalseButton2=(Button)findViewById(R.id.falseButton2);
falseButton2.setOnTouchListener(this);
}

@Override
publicbooleanonTouch(Viewv,MotionEventevent)
{
StringmyTag=v.getTag().toString();

Log.e(myTag,"==========="+"Activity的onTouch方法"+"===========");
Log.e(myTag,"被點擊的View="+myTag);
Log.e(myTag,describeEvent(v,event));

if("true".equals(myTag.substring(0,4)))
{
Log.e(myTag,"==true");
returntrue;
}
else
{
Log.e(myTag,"==false");
returnfalse;
}
}

@Override
publicbooleanonTouchEvent(MotionEventevent)
{
returnsuper.onTouchEvent(event);
}

protectedstaticStringdescribeEvent(Viewview,MotionEventevent)
{
StringBuildersb=newStringBuilder(300);

sb.append("Action:").append(event.getAction()).append("\n");//獲取觸控動作比如ACTION_DOWN
sb.append("相對坐標:").append(event.getX()).append("*").append(event.getY()).append("");
sb.append("絕對坐標:").append(event.getRawX()).append("*").append(event.getRawY()).append("\n");

if(event.getX()<0||event.getX()>view.getWidth()||event.getY()<0||event.getY()>view.getHeight())
{
sb.append("未點擊在View范圍內");
}

sb.append("Edgeflags:").append(event.getEdgeFlags()).append("");//邊緣標記,但是看設備情況,很可能始終返回0
sb.append("Pressure:").append(event.getPressure()).append("");//壓力值,0-1之間,看情況,很可能始終返回1
sb.append("Size:").append(event.getSize()).append("\n");//指壓范圍
sb.append("Downtime:").append(event.getDownTime()).append("ms");
sb.append("Eventtime:").append(event.getEventTime()).append("ms");
sb.append("Elapsed:").append(event.getEventTime()-event.getDownTime()).append("ms\n");

returnsb.toString();
}
}

點擊過程分析:

1.點擊上部的【true_Btn_上】,運行日志如下:

此時執行的是Activity上的onTouch方法并返回了True。

onTouch()方法返回true,因為編碼TrueButton的目的是為返回true。返回true會告訴Android,MotionEvent對象已經被使用,不能將它提供給其他方法。

它還告訴Android,繼續將此觸摸序列的觸摸事件發送到此方法。這就是為什么我們在ACTION_DOWN事件后還會看到ACTION_UP或ACTION_MOVE等其他事件。

當觸摸【true_Btn_上】時,按鈕并沒有高亮顏色變化。這是因為,onTouch()是在調用任何按鈕方法之前調用的,而且onTouch()方法返回了true,所以Android就不會再調用【true_Btn_上】按鈕的onTouchEvent()方法了。

如果在onTouch()方法中,在返回true的行之前添加一行"v.onTouchEvent(event);",那么將會看到觸摸時按鈕會有顏色變化了。

2.點擊上部的【false_Btn_上】,運行日志如下:

點擊后,false_btn按鈕會處于常亮狀態。效果如圖:

Android接收到MotionEvent對象中的ACTION_DOWN事件,將它傳遞給MainActivity類中的onTouch()方法。onTouch()執行后返回false。

這個過程告訴Android,onTouch()方法未使用該事件,所以Android尋找要調用的下一個方法,也就是范例中的FalseButton類中重寫的onTouchEvent()方法。參見BooleanButton.java中的onTouchEvent()方法,先執行父類的onTouchEvent()方法,然后再返回了false。此時打印出來的Logcat日志與之前的完全一樣,因為我們仍然在同一個View對象FalseButton中。

根據日志可以看到,父類希望從onTouchEvent()返回true,但是FalseButton的該方法返回了false。通過返回false,再次告訴Android我們未使用此事件,所以Android不會將ACTION_UP事件發送到我們的按鈕,以至于該按鈕不知道手指是否已離開了觸摸屏。這也是為什么FalseButton在被按下時一直停留在被按下的顏色狀態。

簡而言之,每次從收到MotionEvent對象的UI對象返回false時,Android就會停止將MotionEvent對象繼續發送到該UI對象,同時還會不斷的查找另一個UI對象來使用MotionEvent對象。

接著看日志,Android兩次嘗試找到ACTION_DOWN事件的使用者但都失敗了,現在它前進道應用程序中下一個可能接收該事件的View,也就是按鈕底層的布局true_Layout。

此時,Android再次調用了MainActivity類中的onTouch()方法,但是現在接收事件的對象變成了true_Layout。接收到的MotionEvent對象的所有信息也與之前相同,只有Y坐標除外,因為點擊位置的Y坐標相對于布局左上角要比相對于按鈕的左上角的距離來得大。因為true_Layout的onTouch()方法返回true,所以Android將觸摸事件的剩余信息發送到了布局。

3.點擊上部的【true_Btn_上】按鈕不放,同時觸摸其他區域。

觸摸【true_Btn_上】按鈕,在離開按鈕之前,其他手指在屏幕上其他區域移動。此時,Logcat將顯示接收觸摸事件的對象都是【true_Btn_上】按鈕。甚至手指離開【true_Btn_上】按鈕,而另一手指仍然在屏幕上移動時,接收到觸摸事件的仍然還是【true_Btn_上】按鈕。

當從onTouch()返回true時,觸摸事件序列將與【true_Btn_上】按鈕相關聯。這告訴了Android,它可以停止查找用來接收MotionEvent對象的UI對象了,只需將此觸摸序列的所有未來MotionEvent對象發送給我們。即使在拖動手指時遇到了另一個視圖,我們仍然會綁定到此序列的原始視圖。

4.點擊下部的【false_Btn_下】按鈕,運行日志如下:

原理與前幾點相同。此時若按住按鈕的手指離開前,其他手指繼續觸摸屏幕,則將不再繼續輸出日志,因為Android找不到接收MotionEvent對象并返回true結果的UI對象,直到開始下一個新觸摸序列。

范例源碼見附件。地址:http://glblong.blog.51cto.com/3058613/1557683

三、MotionEvent回收

MotionEvent類中有個recycle()方法,但是不要通過此方法對MotionEvent對象進行回收。如果回調方法沒有使用MotionEvent對象,并且返回了false,MotionEvent對象可能會被傳遞到其他某個方法、視圖或我們的活動。

MotionEvent類中還有個obtain()方法,通過此方法可以創建一個MotionEvent的副本或者全新的MotionEvent,這個副本或全新的事件對象是在完成之后應該回收的對象。

四、小結

1.onTouch()方法與View類中的onTouchEvent()方法的區別

onTouch()是View.setOnTouchListener后調用的方法,如果一個View對象setOnTouchListener后,同時又重寫了View類自身的onTouchEvent()方法,那么當屏幕觸摸時會先調用哪個方法呢?

每當事件產生時,都要先經由dispatchTouchEvent方法來分發。看下View類中的dispatchTouchEvent()源碼,如下:

/**
*Passthetouchscreenmotioneventdowntothetargetview,orthisview
*ifitisthetarget.
*
*@paramevent
*Themotioneventtobedispatched.
*@returnTrueiftheeventwashandledbytheview,falseotherwise.
*/
publicbooleandispatchTouchEvent(MotionEventevent)
{
//用于測試目,直接忽略
if(mInputEventConsistencyVerifier!=null)
{
mInputEventConsistencyVerifier.onTouchEvent(event,0);
}

//上面代碼的第2個標注。未被其他窗口遮蓋
if(onFilterTouchEventForSecurity(event))
{
//noinspectionSimplifiableIfStatement
if(mOnTouchListener!=null&&(mViewFlags&ENABLED_MASK)==ENABLED&&mOnTouchListener.onTouch(this,event))
{
//如果有監聽器,執行監聽器的,不在執行當前視圖的onTouchEvent方法
returntrue;
}
//執行當前視圖的onTouchEvent方法
if(onTouchEvent(event))
{
returntrue;
}
}

//用于測試目,直接忽略
if(mInputEventConsistencyVerifier!=null)
{
mInputEventConsistencyVerifier.onUnhandledEvent(event,0);
}
returnfalse;
}

dispatchTouchEvent()方法里先是判斷了是否有監聽器,然后才會去判斷onTouchEvent(event)。因此會先執行onTouch()方法,如果監聽器的onTouch()返回false,則繼續執行View的onTouchEvent()。

2.觸摸事件的傳遞過程

1).觸摸事件最先被最頂層的UI對象(比如范例中的Btn)接收,如果該View對象接收后返回false,則繼續傳向其上一層UI對象(比如范例中的layout),依此類推,直到最底層的Activity。

2).同一個View對象,接收到觸摸事件時,如果有設置監聽器,先執行View.setOnTouchListener里的onTouch(),然后執行View類中的onTouchEvent(event)方法。

3).當一個UI對象接收到觸摸事件并返回true時,同時多指觸摸的其他事件序列都會被該UI對象接收,直至下一個新的觸摸事件序列產生。

4).當各層的UI對象接收觸摸事件都返回false時,此刻同時多指觸摸,由此產生的其他事件序列將找不到接收的UI對象。因為當前原始的接收UI對象返回false,系統會一直查找下一個可能接收事件并返回true的UI對象但卻又無法找到。

總結

以上是生活随笔為你收集整理的如何理解MotionEvent对象的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。