基于監聽的事件處理機制
在前面的章節我們都是以開發布局為主,涉及到的邏輯非常少,這樣安排是因為編寫 UI 會更加直觀,寫完即能看到效果,可以增加我們的學習興趣,并能夠對 Android 開發有一個直觀的感受。在我們設計出精美的 UI 之后,需要讓它服務于我們的應用程序,這就需要有事件處理機制了,讓各個 View 進行操作的時候它會執行相應的邏輯,完成我們給它分配的任務。
1. 事件處理
事件對應一個行為,它通常發生在用戶和App進行交互的時候,比如輸入文字、點擊按鈕、手勢等等。Android系統將事件處理設計成了一種先進先出(FIFO)的隊列形式,所以我們可以按照用戶操作的順序來依次處理用戶事件
2. 事件監聽
在系統發生了一個事件之后,我們如何接收到這個事件呢?這就需要在事件發生之前提前向系統注冊一個事件監聽器,告訴 Android 系統我關心那些事件,那么系統就會在事件發生的相應時間點給你一個回調通知,常見的事件監聽器有以下幾個:
- OnClickListener:
用來監聽控件的點擊事件,即在用戶點擊某個 View 的時候回調此接口。(這也是開發過程中最最最常見的接口,一定要牢牢掌握!) - OnLongClickListener:
顧名思義,在 View 被長按的時候回調 - OnFocusChangeListener:
當控件的焦點發生變化的時候回調 - OnKeyListener:
當用戶點擊手機上的按鍵的時候回調此接口,通常可以用來攔截按鍵事件,然后針對特殊場景做特殊處理 - OnTouchListener:
當用戶觸摸屏幕的時候回調,此接口會發生在OnClickListener
回調的前面,所以我們可以在Touch事件進行一些更早期的預處理事務。 - OnMenuItemClickListener:
當用戶點擊菜單的時候調用
以上就是 Android 系統提供的常用事件處理監聽器,其中最為常見的就是OnClickListener
,未來的開發中會大量的使用到,所以必須掌握。所以接下來會以OnClickListener
為例子來演示如何完成事件處理,其他的監聽器使用方式也都大同小異。
3. 事件處理方式
事件處理要經過以下 4 大步驟:
- 注冊監聽器
- 用戶進行相應操作,系統將事件入隊
- 事件經過系統層層分發,最終回調步驟 1 中注冊的接口
- 執行回調中的邏輯,完成事件處理
Android 中所有的事件處理都會經過以上 4 個步驟,但是具體的處理方式會有所不同,接下來介紹一下幾種不同的處理方式,最終達到的效果是每次點擊 Button 的時候彈出一個 Toast,如下圖:
3.1 聲明內部類
通過新增內部類的形式實現OnClickListener
接口,代碼如下:
package com.emercy.myapplication;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = findViewById(R.id.bottom);
button.setOnClickListener(new EventHandle());
}
private class EventHandle implements View.OnClickListener {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "Button被點擊了", Toast.LENGTH_LONG).show();
}
}
}
3.2 匿名內部類
匿名內部類的寫法會比較簡單直接,但是缺點是只能用一次,并且代碼會集中在方法體內,如果處理邏輯過于復雜會導致方法代碼冗余。所以通常在只需要使用一次并且內部邏輯不太復雜的時候使用。
package com.emercy.myapplication;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = findViewById(R.id.button);
// 創建匿名內部類綁定點擊監聽器
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 在回調中處理點擊事件
Toast.makeText(MainActivity.this, "Button被點擊了", Toast.LENGTH_LONG).show();
}
});
}
}
3.3 外部類
如果你的事件處理邏輯需要在多個類中使用,那么以上兩種方式都無法滿足,這時候就需要聲明一個外部類來實現OnClickListener
接口了:
package com.emercy.myapplication;
import android.content.Context;
import android.view.View;
import android.widget.Toast;
public class EventHandle implements View.OnClickListener {
Context mContext;
public EventHandle(Context context) {
mContext = context;
}
@Override
public void onClick(View v) {
// 點擊回調中處理事件
Toast.makeText(mContext.getApplicationContext(), "Button被點擊了", Toast.LENGTH_LONG).show();
}
}
由于需要彈 Toast,所以這里在構造器中傳入了一個 Context 對象,這樣一來 MainActivity 就可以更整潔一些了:
package com.emercy.myapplication;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = findViewById(R.id.button);
// 綁定點擊監聽器
button.setOnClickListener(new EventHandle(this));
}
}
3.4 Activity 自身實現接口
我們也可以讓 Activity 去實現 OnClickListener
接口,這樣就可以直接在 Activity 中覆寫 OnClick
方法,將所有的邏輯都封裝在了 Activity 內部:
package com.emercy.myapplication;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends Activity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = findViewById(R.id.button);
// 直接綁定Activity即可
button.setOnClickListener(this);
}
@Override
public void onClick(View v) {
// 在回調中處理點擊事件
Toast.makeText(MainActivity.this, "Button被點擊了", Toast.LENGTH_LONG).show();
}
}
3.5 通過 xml 標簽指定
以上四種本質上其實都是通過實現OnClickListener
接口去監聽點擊事件的,除此之外還可以在通過布局文件中添加onClick
標簽的方式靜態綁定點擊事件。這種寫法非常少見,在某些場景下可以幫助簡化很多代碼,但是它不太靈活,大家了解一下即可:
首先在 xml 中找到對應的 <Button/>
標簽,然后添加onClick
屬性:
<?xml version="1.0" encoding="utf-8"?>
<Button xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:onClick="eventHandle"
android:text="點擊事件處理">
</Button>
接著在 Activity 中聲明eventHandle
方法,這樣就不需要手動獲取 Button 實例,也不用綁定點擊事件了。(注意eventHandle的方法簽名必須是固定的)
package com.emercy.myapplication;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
// Java代碼中無需綁定,直接實現處理函數即可
public void eventHandle(View v) {
Toast.makeText(getApplicationContext(), "Button被點擊了", Toast.LENGTH_LONG).show();
}
}
4. 總結
本節介紹了 Android 的事件處理機制以及主要常用的集中事件,并以最常用的OnClickListener
為例詳細講解了集中實現方式,其他的集中事件幾乎都是照壺畫瓢,大家有興趣的也可以模仿本節示例自行實現一下。在本節之前的內容大多是為 UI 布局為主,本節之后大家將會見到很多事件相應及邏輯控制,只有將 UI 和事件處理和在一起,才能寫出各式各樣的App,你打算寫一個什么樣的呢?