Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Android

Cách phát hiện hướng vuốt (trái/phải, lên/xuống) trên Android bằng Kotlin

Giới thiệu

Bài viết hướng dẫn cách phát hiện cử chỉ vuốt (swipe) theo 4 hướng: trái, phải, lên, xuống trên Android sử dụng GestureDetector và Kotlin.

Các bước thực hiện

Bước 1: Tạo dự án mới

Android Studio > File > New Project > hoàn tất.

Bước 2: Layout giao diện (activity_main.xml)

Một RelativeLayout đơn giản với TextView hướng dẫn:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
    android:id="@+id/relativeLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="70dp"
        android:background="#008080"
        android:padding="5dp"
        android:text="TutorialsPoint"
        android:textColor="#fff"
        android:textSize="24sp"
        android:textStyle="bold" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="Vuốt để phát hiện sự kiện"
        android:textAlignment="center"
        android:textColor="@android:color/holo_purple"
        android:textSize="24sp"
        android:textStyle="bold" />

</RelativeLayout>

Bước 3: Logic xử lý vuốt (MainActivity.kt)

Triển khai OnSwipeTouchListener dựa trên GestureDetector.SimpleOnGestureListener:

import android.content.Context
import android.os.Bundle
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import kotlin.math.abs

class MainActivity : AppCompatActivity() {
    private var onSwipeTouchListener: OnSwipeTouchListener? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        title = "KotlinApp"
        onSwipeTouchListener = OnSwipeTouchListener(this, findViewById(R.id.relativeLayout))
    }

    inner class OnSwipeTouchListener internal constructor(
        ctx: Context,
        mainView: View
    ) : View.OnTouchListener {

        private val gestureDetector: GestureDetector
        private var context: Context
        private lateinit var onSwipe: OnSwipeListener

        companion object {
            private const val SWIPE_THRESHOLD = 100
            private const val SWIPE_VELOCITY_THRESHOLD = 100
        }

        init {
            gestureDetector = GestureDetector(ctx, GestureListener())
            mainView.setOnTouchListener(this)
            context = ctx
        }

        override fun onTouch(v: View, event: MotionEvent): Boolean {
            return gestureDetector.onTouchEvent(event)
        }

        inner class GestureListener : GestureDetector.SimpleOnGestureListener() {
            override fun onDown(e: MotionEvent): Boolean = true

            override fun onFling(
                e1: MotionEvent,
                e2: MotionEvent,
                velocityX: Float,
                velocityY: Float
            ): Boolean {
                var result = false
                try {
                    val diffY = e2.y - e1.y
                    val diffX = e2.x - e1.x

                    if (abs(diffX) > abs(diffY)) {
                        // Vuốt ngang
                        if (abs(diffX) > SWIPE_THRESHOLD && abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
                            if (diffX > 0) onSwipeRight() else onSwipeLeft()
                            result = true
                        }
                    } else {
                        // Vuốt dọc
                        if (abs(diffY) > SWIPE_THRESHOLD && abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
                            if (diffY > 0) onSwipeBottom() else onSwipeTop()
                            result = true
                        }
                    }
                } catch (e: Exception) {
                    e.printStackTrace()
                }
                return result
            }
        }

        // Các callback hướng vuốt
        internal fun onSwipeRight() {
            Toast.makeText(context, "Vuốt phải", Toast.LENGTH_SHORT).show()
            onSwipe.swipeRight()
        }
        internal fun onSwipeLeft() {
            Toast.makeText(context, "Vuốt trái", Toast.LENGTH_SHORT).show()
            onSwipe.swipeLeft()
        }
        internal fun onSwipeTop() {
            Toast.makeText(context, "Vuốt lên", Toast.LENGTH_SHORT).show()
            onSwipe.swipeTop()
        }
        internal fun onSwipeBottom() {
            Toast.makeText(context, "Vuốt xuống", Toast.LENGTH_SHORT).show()
            onSwipe.swipeBottom()
        }

        internal interface OnSwipeListener {
            fun swipeRight()
            fun swipeTop()
            fun swipeBottom()
            fun swipeLeft()
        }
    }
}

Bước 4: AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
    package="com.example.swipedetection">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>
</manifest>

Chạy và kiểm tra

Chạy app, vuốt màn hình theo 4 hướng. Mỗi hướng sẽ hiển thị Toast tương ứng. Bạn có thể mở rộng interface OnSwipeListener để xử lý logic nghiệp vụ cụ thể (chuyển tab, xóa item, v.v.).

Giải thích ngưỡng (Threshold)

  • SWIPE_THRESHOLD = 100: Khoảng cách tối thiểu (pixel) để coi là một cử chỉ vuốt.
  • SWIPE_VELOCITY_THRESHOLD = 100: Tốc độ tối thiểu (pixel/giây) để phân biệt vuốt với kéo thả chậm.
  • Có thể điều chỉnh các giá trị này tùy theo độ nhạy mong muốn.