Xử lý sự kiện trong Android (Event Handling)

0
Bài này thuộc phần 10 của 16 phần trong series Tự học lập trình Android trong 24 giờ

Các sự kiện (event) là một cách hữu ích để tương tác giữa người dùng với các thành phần của ứng dụng. Ví dụ như sự kiện nhấn vào một nút hoặc chạm vào màn hình… Bài hướng dẫn này sẽ hướng dẫn một số phương pháp để bắt và xử lý sự kiện trong Android.

Để có thể phát hiện và xử lý một sự kiện từ người dùng, mình giới thiệu 5 cách:

  • Tạo riêng một member Class
  • Tạo riêng một Interface
  • Sử dụng Anonymous Inner Class
  • Implement Event Listeners trực tiếp từ Activity
  • Cuối cùng là dùng thuộc tính onClick trong xml layout

Cả 5 cách này về bản chất là đều implement các Event Interface. Tùy vào kiến trúc dự án, thói quen lập trình mà bạn có thể chọn một cách để làm.

Chúng ta bắt đầu nhé!

Đôi điều về xử lý sự kiện trong Android

Tương ứng với mỗi một sự kiện của người dùng, sẽ có hàm tương ứng để xử lý và lắng nghe.

Mình lấy ví dụ:

  • onTouch(): Dùng khi người dùng chạm vào màn hình với nhiều kịch bản chạm.
  • onLongClick(): Khi người dùng chạm và giữ tay ở màn hình.
  • onFocusChange(): Khi focus hoặc mất focus vào một view.
  • onClick(): Tương tự như hàm onTouch() nhưng chỉ có kịch bản chạm và nhấc tay lên.

Trong số các hàm sự kiện này, có lẽ onclick( )là hàm được dùng nhiều nhất.

Bài viết này mình sẽ tập trung trình bày cách khác nhau để bắt và xử lý sự kiện onClick(). Với các sự kiện khác hoàn toàn tương tự.

Để minh họa cho bài viết, mình sẽ tạo một layout activity_main.xml có TextView và Button như bên dưới:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.codinglisteners.MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Click a button."
        android:id="@+id/textView"
        android:textSize="20sp" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button 1"
        android:id="@+id/button1"
        android:layout_below="@+id/textView"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true" />
</RelativeLayout>

Nếu bạn muốn tìm hiểu sâu hơn về cách thiết kế layout thì đọc bài viết này: Layout trong Android và cách thiết kế layout hỗ trợ đa màn hình

Bài viết này sẽ thực hiện một ví dụ đơn giản để minh họa như sau: Mỗi khi người dùng chạm vào Button thì sẽ thay đổi nội dung dưới TextView. Trong MainActivity.kt, bạn sẽ import các thư viện cơ bản sau

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
Protip: Android Studio có thể tự import cho bạn những viện cần thiết bằng cách đưa chuột vào chỗ text nào bị gạch chân màu đỏ rồi nhất Alt+ Enter.

OK, bộ khung đã xong, chúng ta sẽ cùng thử lần lượt các cách phát hiện và xử lý sự kiện trong Android.

Các cách xử lý sự kiện (Event Handling) trong Android

Cách 1: Tạo riêng Member Class

Chúng ta tạo một inner class trong Activity và đặt tên là HandleClick. HandleClick sẽ implemetent một interface có tên là OnClickListener. Cách này rất hữu ích khi có một vài listener cần xử lý giống nhau.

class MainActivity:AppCompatActivity() {
  protected fun onCreate(savedInstanceState:Bundle) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    //attach an instance of HandleClick to the Button
    findViewById(R.id.button1).setOnClickListener(HandleClick())
  }
  private inner class HandleClick:View.OnClickListener {
    fun onClick(arg0:View) {
      val btn = arg0 as Button //cast view to a button
      // update the TextView text
      (findViewById(R.id.textView) as TextView).setText("You pressed " + btn.getText())
    }
  }
}

Cách 2. Tạo riêng Interface

Chúng ta khai báo một biến kiểu OnClickListener bằng câu lệnh object:View.OnClickListener(){...}. Cách làm này cũng gần giống với cách 1 ở trên.

class MainActivity:AppCompatActivity() {
  private val handleClick = object:View.OnClickListener() {
    fun onClick(arg0:View) {
      val btn = arg0 as Button
      val tv = findViewById(R.id.textView) as TextView
      tv.setText("You pressed " + btn.getText())
    }
  }
  protected fun onCreate(savedInstanceState:Bundle) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    //attach an instance of HandleClick to the Button
    findViewById(R.id.button1).setOnClickListener(handleClick)
  }
}

🤗 Dành cho bạn: Intent trong Android: Vai trò và cách sử dụng

Cách 3. Sử dụng Anonymous Inner Class

Cách này đơn giản là chúng ta khởi tạo luôn một instance của OnClickListener trong đối số của của hàm setOnClickListener.

Cách làm này rất hữu dụng khi bạn muốn viết code nhanh, và phần xử lý sự kiện này không tái xử dụng ở đâu trong dự án. Một số bạn chưa có kinh nghiệm có thể thấy code này khá khó hiểu. Nhưng thực ra, về bản chất cũng giống như các cách trên là đều tạo instance của View.OnClickListener(){...} bằng lệnh object:View.OnClickListener

class MainActivity:AppCompatActivity() {
  protected fun onCreate(savedInstanceState:Bundle) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    //attach an instance of HandleClick to the Button
    findViewById(R.id.button1).setOnClickListener(object:View.OnClickListener() {
      fun onClick(arg0:View) {
        val btn = arg0 as Button
        val tv = findViewById(R.id.textView) as TextView
        tv.setText("You pressed " + btn.getText())
      }
    })
  }
}

Cách 4. Implement trực tiếp trên Activity

Activity cũng có thể implement luôn OnClickListener. Với cách này thì Activity sẽ phải override lại hàm onClick(). Nhược điểm của cách này là chúng ta không thể tái sử dụng phần xử lý sự kiện onClick()ở các class khác.

class main:AppCompatActivity(), View.OnClickListener {
  protected fun onCreate(savedInstanceState:Bundle) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    //attach an instance of HandleClick to the Button
    findViewById(R.id.button1).setOnClickListener(this)
  }
  fun onClick(arg0:View) {
    val btn = arg0 as Button
    val tv = findViewById(R.id.textView) as TextView
    tv.setText("You pressed " + btn.getText())
  }
}

Cách 5. Sử dụng thuộc tính onCLick trong View Layout

Từ API level 4 (Android 1.6) trở đi, tên của một hàm có thể được gán vào thuộc tính android:onClick trong layout.

<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Button 1"
    android:id="@+id/button1"
    android:layout_below="@+id/textView"
    android:layout_alignParentLeft="true"
    android:layout_alignParentStart="true"
    android:onClick="handleClick" />

Sau đó thì hàm đó được viết trong Activity như sau

class MainActivity:AppCompatActivity() {
  fun onCreate(savedInstanceState:Bundle) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
  }
  fun handleClick(arg0:View) {
    val btn = arg0 as Button
    val tv = findViewById(R.id.textView) as TextView
    tv.setText("You pressed " + btn.getText())
  }
}
Lưu ý: Tham số của hàm được định nghĩa trong thuộc tính của layout luôn là View. Ví dụ: trong layout mình đặt tên hàm là handleClick thì trong Activity hàm đó phải là handleClick(View arg)

Với cách này thì code của bạn sẽ khá gọn, không cần phải gọi findViewById()hay setOnClickListener(). Tuy nhiên, người khác đọc code của bạn sẽ khó hiểu và rối nếu bạn đặt tên hàm “củ chuối”.

Cuối cùng, cách làm này chỉ áp dụng cho sự kiện onClick.

Tổng kết

Như vậy, mình đã chia sẻ với các bạn các cách phổ biến nhất để xử lý sự kiện trong Android. Với cá nhân mình, thì cách 4 là hay sử dụng nhất. Các Listener nên đóng gói trong Activity và không nên share Listener giữa các Activity.

Các bạn có thể tham khảo các bài viết khác trong khóa học: Tự học lập trình Android trong 24 giờ của mình để hiểu thêm nhé. Bài viết sau sẽ là hướng dẫn toàn tập về Service trong Android.

Nếu các bạn có cách xử lý sự kiện trong Android nào khác hay ho hơn thì chia sẻ bên dưới để mọi người cùng học hỏi nhé. Đừng quên share bài viết để mình có động lực tiếp tục hoàn thành khóa học nhé

Xem tiếp các bài trong Series
Phần trước: Intent trong Android: Vai trò và cách sử dụngPhần kế tiếp: Service trong Android là gì? Các loại service trong android
Dịch vụ phát triển ứng dụng mobile giá rẻ - chất lượng

Bình luận. Cùng nhau thảo luận nhé!

avatar
  Theo dõi bình luận  
Thông báo