- Android經(jīng)典應(yīng)用程序開發(fā)
- 韓超編著
- 2326字
- 2019-01-09 15:18:46
2.3 設(shè)備事件的響應(yīng)
在應(yīng)用程序的控制方面,在大多數(shù)場合下使用的是屏幕上的控件,控件本身也是包含了事件處理的載體。
在某些情況下也需要直接響應(yīng)由輸入設(shè)備發(fā)送過來的事件。在Android系統(tǒng)中,輸入設(shè)備主要為鍵盤、觸摸屏、軌跡球和鼠標(biāo),在Android中,鍵盤響應(yīng)的是KeyEvent,后三者通常響應(yīng)的是MotionEvent。
2.3.1 鍵盤事件的響應(yīng)
鍵盤是Android中的主要輸入設(shè)備,對按鍵事件的處理是在程序中使用鍵盤的核心內(nèi)容。
鍵盤的事件通常用KeyEvent來表示,KeyEvent是android.view包中的一個類,主要包含以下一些方法:
final int getKeyCode() // 獲得按鍵碼 final int getAction() // 獲得按鍵的動作 final int getFlags() // 獲得標(biāo)志 final int getRepeatCount() // 獲得重復(fù)的信息 final int getScanCode() // 獲得掃描碼
通過KeyEvent接口,可以獲得按鍵相關(guān)的詳細(xì)信息,這些信息都是用整數(shù)值來表示的。按鍵相關(guān)的信息中,其中最主要的是KeyCode,用于標(biāo)識哪一個按鍵發(fā)生了事件,Action表示按鍵的動作(抬起、按下),RepeatCount表示同一個按鍵被按下的次數(shù),ScanCode(掃描碼)表示底層按鍵的原始標(biāo)識。
android.view.KeyEvent.Callback是一個接口,用于表示發(fā)生按鍵事件后的回調(diào),其中包含了如下幾個方法:
public abstract boolean onKeyDown(int keyCode, KeyEvent event) public abstract boolean onKeyUp(int keyCode, KeyEvent event) public abstract boolean onKeyMultiple(int keyCode, int count, KeyEvent event) public abstract boolean onKeyLongPress(int keyCode, KeyEvent event)
KeyEvent.Callback接口中的幾個方法,均具有整型和KeyEvent類型兩個參數(shù),KeyEvent類型中實(shí)際上包含整型的信息。
以上幾個方法的返回類型均為boolean類型,如果返回true,表示自己完成這個事件的處理,如果返回false,表示由下一個接收器處理這個事件。
View和Activity都已經(jīng)實(shí)現(xiàn)KeyEvent.Callback接口。在這兩個類的繼承者中,可以通過重新實(shí)現(xiàn)以上的幾個方法來響應(yīng)按鍵的事件。
通常情況下,按鍵事件屬于整個屏幕,因此在Activity中響應(yīng)即可。按照一般的邏輯,當(dāng)鍵盤上一個按鍵被按下的時候,不會去區(qū)分這個按鍵屬于哪一個控件。
Activity中的dispatchKeyEvent()方法用于截獲一個按鍵的事件:
public boolean dispatchKeyEvent (KeyEvent event)
這個方法將在按鍵到達(dá)窗口之前被調(diào)用,因此通過這個方法可以阻止系統(tǒng)的其他部分不獲得按鍵事件。
以下的示例需要實(shí)現(xiàn)的內(nèi)容是通過鍵盤來控制一個圖片的Alpha值,使用上鍵和右鍵增加圖片的Alpha值,使用下鍵和左鍵減少圖片的Alpha值。
這個按鍵事件響應(yīng)程序的運(yùn)行效果如圖2-4所示。

圖2-4 按鍵事件響應(yīng)程序運(yùn)行效果(左:初始化;中:中間Alpha值;右:Alpha值為0)
本例的布局文件testkeyevent.xml如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/screen" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:id="@+id/alphavalue" android:layout_gravity="center" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <ImageView android:id="@+id/image" android:src="@drawable/robot" android:layout_gravity="center" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>
從以上的布局文件可以看到,本例包含了一個文本框和一個顯示圖片的控件,這樣文本框可用來顯示當(dāng)前Alpha的比例值,顯示圖片的控件ImageView用于顯示一個圖片。
本例的源代碼的核心部分實(shí)現(xiàn)如下所示:
public class TestKeyEvent extends Activity { private static final String TAG = "TestKeyEvent"; private ImageView mImage; // 界面中的圖片 private TextView mAlphavalueText; private int mAlphavalue; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.testkeyevent);
mImage = (ImageView) findViewById(R.id.image); mAlphavalueText = (TextView) findViewById(R.id.alphavalue); mAlphavalue = 100; mImage.setAlpha(mAlphavalue); // 設(shè)置初始化的透明數(shù)值 mAlphavalueText.setText("Alpha = " + mAlphavalue*100/0xff + "%"); } @Override public boolean onKeyDown(int keyCode, KeyEvent msg){ // 按下事件的處理 Log.v(TAG, "onKeyDown: keyCode = "+ keyCode); // 打印事件信息 Log.v(TAG, "onKeyDown: String = " + msg.toString()); switch (keyCode) { case KeyEvent.KEYCODE_DPAD_UP: // 上鍵和右鍵的處理 case KeyEvent.KEYCODE_DPAD_RIGHT: mAlphavalue += 20; break; case KeyEvent.KEYCODE_DPAD_DOWN: // 下鍵和左鍵的處理 case KeyEvent.KEYCODE_DPAD_LEFT: mAlphavalue -= 20; break; default: break; } if(mAlphavalue>=0xFF)mAlphavalue = 0xFF; // 控制Alpha數(shù)值的范圍 if(mAlphavalue<=0x0)mAlphavalue = 0x0; mImage.setAlpha(mAlphavalue); mAlphavalueText.setText("Alpha = " + mAlphavalue*100/0xff + "%"); return super.onKeyDown(keyCode, msg); } @Override public boolean onKeyUp(int keyCode, KeyEvent msg){ // 抬起事件的處理 Log.v(TAG, "onKeyUp: keyCode = "+ keyCode); // 打印事件信息 Log.v(TAG, "onKeyUp: String = " + msg.toString()); return super.onKeyUp(keyCode, msg); } }
本例使用onKeyDown()方法來獲得按鍵的事件,同類的方法還包括onKeyUp()方法,其參數(shù)int keyCode為按鍵碼,KeyEvent msg表示按鍵事件的消息(其中包含了更詳細(xì)的內(nèi)容)。
通過keyCode(int)可以獲得是哪一個按鍵響應(yīng),而通過msg(KeyEvent)除了按鍵碼之外,可以獲得更多的信息,例如按鍵的動作、重復(fù)信息、掃描碼等內(nèi)容。
本程序打出的一段Log信息如下所示:
VERBOSE/TestKeyEvent(771): onKeyDown: keyCode = 20 VERBOSE/TestKeyEvent(771): onKeyDown: String = KeyEvent{action=0 code=20 repeat=7 meta=0 scancode=108 mFlags=8} VERBOSE/TestKeyEvent(771): onKeyDown: keyCode = 20 VERBOSE/TestKeyEvent(771): onKeyDown: String = KeyEvent{action=0 code=20 repeat=8 meta=0 scancode=108 mFlags=8} VERBOSE/TestKeyEvent(771): onKeyDown: keyCode = 20 VERBOSE/TestKeyEvent(771): onKeyDown: String = KeyEvent{action=1 code=20 repeat=0 meta=0 scancode=108 mFlags=8}
由此可見,按鍵事件KeyEvent可以獲得重復(fù)按鍵的次數(shù)和這個歷史過程相關(guān)的數(shù)據(jù)信息,在一個按鍵的響應(yīng)過程中,通常是先發(fā)送一個或若干個Down事件,最后是一個Up事件。
按鍵事件的響應(yīng)有以下幾個需要注意的細(xì)節(jié):
(1)對于普通按鍵,通常響應(yīng)onKeyUp()事件,表示按下的時候不再處理,按鍵抬起時才有效果;
(2)onKeyDown()可以連續(xù)響應(yīng)一個按鍵按下的情況,并且通過KeyEvent的getRepeatCount()可以為重復(fù)按鍵增加特殊的處理。
(3)在按鍵處理方法的最后,調(diào)用super.onKeyDown()和super.onKeyUp()表示由父類進(jìn)行默認(rèn)的按鍵處理。
2.3.2 運(yùn)動事件的處理
觸摸屏(TouchScreen)和軌跡球(TrackBall)是Android中除鍵盤之外的主要輸入設(shè)備。如果需要使用觸摸屏和軌跡球,可以通過使用運(yùn)動事件(MotionEvent)來接收它們的信息。
對于觸摸屏,一般通過絕對坐標(biāo)描述它的事件;對于軌跡球,一般通過相對坐標(biāo)描述它的事件。在Android系統(tǒng)中,鼠標(biāo)設(shè)備的處理方式類似于軌跡球。
MotionEvent是android.view包中的類,用于表示運(yùn)動事件的坐標(biāo)、動作等信息。MotionEvent中主要的幾個方法如下所示:
public final float getX() // 獲得X坐標(biāo) public final float getY() // 獲得Y坐標(biāo) public final int getAction() // 獲得動作 public final float getRawX() // 獲得原始的X坐標(biāo) public final float getRawY() // 獲得原始的Y坐標(biāo)
對于這種運(yùn)動事件,最重要的屬性為運(yùn)動事件發(fā)生的坐標(biāo)(X,Y),運(yùn)動的動作表示按下、按住移動、抬起等信息。getRawX()和getRawY()表示沒有根據(jù)窗口或者控件做出調(diào)整的原始坐標(biāo)。
運(yùn)動事件有其歷史屬性,MotionEvent中相關(guān)的方法如下所示:
public final int getHistorySize() // 獲得歷史的點(diǎn)數(shù) public final float getHistoricalX(int pos) // 獲得歷史的X坐標(biāo) public final float getHistoricalY(int pos) // 獲得歷史的Y坐標(biāo)
在Android API級別5版本之后,MotionEvent類中還包含了多點(diǎn)觸摸的相關(guān)內(nèi)容,當(dāng)有多個觸點(diǎn)同時起作用的時候,可以獲得觸點(diǎn)的數(shù)目和每一個觸點(diǎn)的坐標(biāo)。
public final int getPointerCount() // 獲得觸點(diǎn)的數(shù)目 public final float getX(int pointerIndex) // 獲得某個觸點(diǎn)的X坐標(biāo) public final float getY(int pointerIndex) // 獲得某個觸點(diǎn)的Y坐標(biāo)
MotionEvent可以獲得多點(diǎn)的基礎(chǔ)信息,而沒有包含手勢解釋的功能。
在Activity和View中,都具有以下兩個方法,用于接收運(yùn)動事件:
public boolean onTouchEvent(MotionEvent event) public boolean onTrackballEvent(MotionEvent event)
在以上兩個方法中,MotionEvent類作為參數(shù)傳入,在這個參數(shù)中可以獲得運(yùn)動事件的各種信息。對于觸摸屏設(shè)備的事件,通常使用onTouchEvent()獲取信息,對于軌跡球、鼠標(biāo)的事件,使用onTrackballEvent()獲取信息。
Activity中的以下兩個方法用于階段觸摸屏和軌跡球的事件:
public boolean dispatchTouchEvent (MotionEvent ev) public boolean dispatchTrackballEvent (MotionEvent ev)
通過dispatchTouchEvent()和dispatchTrackballEvent()兩個方法,可以使得運(yùn)動事件不再向窗口中傳遞。
1.在活動中響應(yīng)運(yùn)動事件
運(yùn)動事件可以屬于整個窗口,在活動中響應(yīng)運(yùn)動事件表示的就是對全窗口的運(yùn)動事件進(jìn)行處理。其方法為在Activity中重新實(shí)現(xiàn)的onTouchEvent()和onTrackballEvent()方法,接收觸摸屏和軌跡球等事件。
下面是處理簡單的運(yùn)動事件的處理程序,這個程序在UI的界面中,顯示當(dāng)前的MotionEvent的動作和位置。觸摸事件程序的運(yùn)行效果如圖2-5所示。

圖2-5 觸摸事件程序運(yùn)行效果
Action=0
為ACTION_DOWN,按下動作
Action=1
為ACTION_UP,抬起動作
Action=2
為ACTION_MOVE,移動動作
布局文件testmotionevent.xml中包含了兩個簡單的TextView,分別用于顯示運(yùn)動事件的坐標(biāo)和動作。本例程序的Java代碼如下所示:
import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.view.MotionEvent; import android.widget.TextView; public class TestMotionEvent extends Activity { private static final String TAG = "TestMotionEvent"; TextView mAction; TextView mPostion; @Override
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.testmotionevent); mAction = (TextView)findViewById(R.id.action); mPostion = (TextView)findViewById(R.id.postion); } @Override public boolean onTouchEvent(MotionEvent event) { // 觸摸屏事件的處理 int Action = event.getAction(); float X = event.getX(); float Y = event.getY(); mAction.setText("Action = "+ Action); // 顯示觸摸的動作 mPostion.setText("Postion = ("+X+","+Y+")"); // 顯示觸摸的位置 return true; } @Override public boolean onTrackballEvent(MotionEvent event) { // 軌跡球事件的處理 int Action = event.getAction(); float X = event.getX(); float Y = event.getY(); mAction.setText("Trackball Action = "+ Action); mPostion.setText("Trackball Postion = ("+X+","+Y+")"); return true; }}
對于運(yùn)行事件中的信息,軌跡球提供的是相對坐標(biāo),信息比較單一。對于觸摸屏,有以下幾個注意點(diǎn):
(1)Activity的onTouchEvent()中獲得的MotionEvent,是相對硬件屏幕的坐標(biāo),因此getX()和getRawX()及getY()和getRawY()的含義一般是相同的。
(2)Activity是包含標(biāo)題欄的,因此對于這種響應(yīng)方式,觸摸標(biāo)題欄也可以觸發(fā)Activity的觸摸事件。
(3)狀態(tài)欄不屬于一個Activity,由于狀態(tài)欄的存在,在一個Activity中獲得的最小y值,可能不是0。
2.控件響應(yīng)運(yùn)動事件
除了響應(yīng)整個活動的運(yùn)動事件,也有專門屬于某一個區(qū)域的運(yùn)動事件的情況。在這種情況下運(yùn)動事件屬于一個控件。為了實(shí)現(xiàn)這種情況,就需要在Vi e w類中進(jìn)行處理。
其中一種方式是實(shí)現(xiàn)View.OnTouchListener()接口,然后將其設(shè)置給某個控件,表示由其中的onTouch()方法處理這個控件內(nèi)的觸摸事件。
以下程序接收一個控件內(nèi)部的觸摸事件,并將結(jié)果顯示到文本框和標(biāo)題欄當(dāng)中。程序運(yùn)行效果如圖2-6所示。

圖2-6 控件內(nèi)的觸摸屏運(yùn)行效果
Action=0
為ACTION_DOWN,按下動作
Action=1
為ACTION_UP,抬起動作
Action=2
為ACTION_MOVE,移動動作
本例在布局文件中留出邊緣,白色的區(qū)域?yàn)橐粋€控件,它并未充滿整個屏幕,當(dāng)其發(fā)生觸摸事件的時候,程序界面中的文本框顯示的是當(dāng)前觸摸事件的坐標(biāo);而標(biāo)題欄中顯示的是觸摸時間的原始坐標(biāo)。從中可以看出坐標(biāo)的原點(diǎn)是控件的左上角,原始坐標(biāo)的原點(diǎn)是屏幕的左上角。
上述程序?qū)崿F(xiàn)的核心內(nèi)容如下所示:
public class TestMotionEvent1 extends Activity implements View.OnTouchListener{ private static final String TAG = "TestMotionEvent1"; private TextView mAction; private TextView mPosition; private View mView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.testmotionevent1); mAction = (TextView) findViewById(R.id.action); mPosition = (TextView) findViewById(R.id.position); mView = findViewById(R.id.touchview); mView.setOnTouchListener(this); // 設(shè)置監(jiān)聽器為當(dāng)前的Activity } public boolean onTouch(View v, MotionEvent event) { // OnTouchListener的方法 int action = event.getAction(); float x = event.getX(); // 獲得坐標(biāo) float y = event.getY(); float rawx = event.getRawX(); // 獲得原始坐標(biāo) float rawy = event.getRawY(); Log.v(TAG, "Action = "+ action ); // 獲得原始坐標(biāo) Log.v(TAG, "("+x+","+y+")"); Log.v(TAG, "("+rawx+","+rawy+")"); setTitle("A = " + action + " Raw ["+ rawx +","+ rawy +"]"); // 顯示原始坐標(biāo) mAction.setText("Action = "+ action); mPosition.setText( "Position = ("+x+","+y+")"); // 顯示坐標(biāo) return true; } }
控件僅僅對其區(qū)域內(nèi)的觸摸事件做出響應(yīng),因此在上述程序中,只有白色區(qū)域的部分能做出響應(yīng)。一般情況下,對于一個控件,使用getX()和getY()得到相對其左上角的坐標(biāo)的情況比較多,getRawX()和getRawY()沒有必要使用。
控件響應(yīng)事件的另外一種方式,就是讓控件自己去實(shí)現(xiàn)onTouchEvent()和onTrackballEvent()兩個方法。
以下示例程序用標(biāo)題欄不同顏色的點(diǎn)表示運(yùn)動事件的類型,程序的結(jié)果如圖2-7所示。

圖2-7 控件自己實(shí)現(xiàn)并響應(yīng)觸摸事件的運(yùn)行結(jié)果(按下、移動、抬起)
當(dāng)觸摸屏按下、移動、抬起的時候,在坐標(biāo)處繪制不同顏色的點(diǎn),在標(biāo)題欄中顯示當(dāng)時的動作和坐標(biāo)。
這里使用的程序的核心內(nèi)容如下所示:
public class TestMotionEvent2 extends Activity { private static final String TAG = "TestMotionEvent2"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(new TestMotionView(this)); // 設(shè)置View為Activity的內(nèi)容 } public class TestMotionView extends View { // 繼承實(shí)現(xiàn)一個新的View private Paint mPaint = new Paint(); private int mAction; private float mX; private float mY; public TestMotionView(Context c) { super(c); mAction = MotionEvent.ACTION_UP; // 初始化一些數(shù)值 mX = 0; mY = 0; } @Override protected void onDraw(Canvas canvas) { Paint paint = mPaint; canvas.drawColor(Color.WHITE); if(MotionEvent.ACTION_MOVE == mAction) { // 移動動作 paint.setColor(Color.RED); }else if(MotionEvent.ACTION_UP == mAction) { // 抬起動作 paint.setColor(Color.GREEN); }else if(MotionEvent.ACTION_DOWN == mAction) { // 按下動作 paint.setColor(Color.BLUE); } canvas.drawCircle(mX, mY,10, paint); // 繪制一個點(diǎn)
setTitle("A = " + mAction + " ["+ mX +","+ mY +"]"); // 標(biāo)題欄顯示 } @Override public boolean onTouchEvent(MotionEvent event) { mAction = event.getAction(); // 獲得動作 mX = event.getX(); // 獲得坐標(biāo) mY = event.getY(); Log.v(TAG, "Action = "+ mAction ); Log.v(TAG, "("+mX+","+mY+")"); invalidate(); // 重新繪制 return true; } } }
本程序使用了“自定義控件”的方法,重新實(shí)現(xiàn)控件內(nèi)部onTouchEvent()方法來接收觸摸事件,接收到它,并且記錄發(fā)生事件的坐標(biāo)(x,y)和動作(action)。調(diào)用invalidate()重新進(jìn)行繪制。繪制在onDraw()中完成,根據(jù)不同的事件,繪制不同顏色的點(diǎn),并設(shè)置標(biāo)題欄。
在這個例子中,雖然自定義的Vi e w充滿了窗口,但是依然不包括標(biāo)題欄的部分。標(biāo)題欄不在控件范圍內(nèi),不會產(chǎn)生這個控件的觸摸事件。
- 寬帶接入技術(shù)
- 用萬用表檢修液晶電視機(jī)一學(xué)就會
- 密碼之謎
- cdma2000 1x/EV-DO通信網(wǎng)絡(luò)規(guī)劃與設(shè)計
- 通信工程設(shè)計與案例
- 第一行代碼:Android(第3版)
- EPON/GPON技術(shù)問答
- 光伏發(fā)電系統(tǒng)智能化故障診斷技術(shù)
- iOS開發(fā)快速進(jìn)階與實(shí)戰(zhàn)
- 全程圖解變頻器應(yīng)用與檢測技能
- 5G 移動性管理技術(shù)
- 愛上單片機(jī)(第3版)
- 電信行業(yè)競爭分析方法與實(shí)踐
- LED照明設(shè)計與案例精選
- 輕松玩轉(zhuǎn)DSP:基于TMS320F2833x