ژست های رایج را تشخیص دهید

روش نوشتن را امتحان کنید
Jetpack Compose ابزار رابط کاربری پیشنهادی برای اندروید است. یاد بگیرید که چگونه از لمس و ورودی در Compose استفاده کنید.

یک حرکت لمسی زمانی رخ می‌دهد که کاربر یک یا چند انگشت خود را روی صفحه لمسی قرار می‌دهد و برنامه شما این الگوی لمس را به عنوان یک حرکت تفسیر می‌کند. تشخیص حرکت دو مرحله دارد:

  1. جمع‌آوری داده‌های رویداد لمسی
  2. تفسیر داده‌ها برای تعیین اینکه آیا معیارهای مربوط به حرکاتی که برنامه شما پشتیبانی می‌کند را برآورده می‌کنند یا خیر.

کلاس‌های AndroidX

مثال‌های این سند از کلاس‌های GestureDetectorCompat و MotionEventCompat استفاده می‌کنند. این کلاس‌ها در کتابخانه AndroidX قرار دارند. در صورت امکان از کلاس‌های AndroidX برای سازگاری با دستگاه‌های قدیمی‌تر استفاده کنید. MotionEventCompat جایگزینی برای کلاس MotionEvent نیست . در عوض، متدهای کاربردی استاتیکی ارائه می‌دهد که شما شیء MotionEvent خود را برای دریافت اکشن مرتبط با آن رویداد به آنها ارسال می‌کنید.

جمع‌آوری داده‌ها

وقتی کاربر یک یا چند انگشت خود را روی صفحه نمایش قرار می‌دهد، این کار باعث فراخوانی تابع onTouchEvent() در view می‌شود که رویدادهای لمسی را دریافت می‌کند. برای هر دنباله از رویدادهای لمسی - مانند موقعیت، فشار، اندازه و اضافه کردن انگشت دیگر - که به عنوان یک حرکت شناسایی می‌شود، onTouchEvent() چندین بار اجرا می‌شود.

این حرکت از زمانی شروع می‌شود که کاربر برای اولین بار صفحه را لمس می‌کند، با ردیابی موقعیت انگشت یا انگشتان کاربر توسط سیستم ادامه می‌یابد و با ثبت رویداد نهایی خروج آخرین انگشت کاربر از صفحه نمایش به پایان می‌رسد. در طول این تعامل، MotionEvent که به onTouchEvent() تحویل داده می‌شود، جزئیات هر تعامل را ارائه می‌دهد. برنامه شما می‌تواند از داده‌های ارائه شده توسط MotionEvent برای تعیین اینکه آیا حرکتی که برایش مهم است اتفاق می‌افتد یا خیر، استفاده کند.

ثبت رویدادهای لمسی برای یک فعالیت یا نمایش

برای رهگیری رویدادهای لمسی در یک Activity یا View ، تابع فراخوانی onTouchEvent() را بازنویسی کنید.

قطعه کد زیر از getAction() برای استخراج عملی که کاربر انجام می‌دهد از پارامتر event استفاده می‌کند. این به شما داده‌های خامی را می‌دهد که برای تعیین اینکه آیا حرکتی که برایتان مهم است رخ می‌دهد یا خیر، نیاز دارید.

کاتلین

class MainActivity : Activity() {
    ...
    // This example shows an Activity. You can use the same approach if you are 
    // subclassing a View.
    override fun onTouchEvent(event: MotionEvent): Boolean {
        return when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                Log.d(DEBUG_TAG, "Action was DOWN")
                true
            }
            MotionEvent.ACTION_MOVE -> {
                Log.d(DEBUG_TAG, "Action was MOVE")
                true
            }
            MotionEvent.ACTION_UP -> {
                Log.d(DEBUG_TAG, "Action was UP")
                true
            }
            MotionEvent.ACTION_CANCEL -> {
                Log.d(DEBUG_TAG, "Action was CANCEL")
                true
            }
            MotionEvent.ACTION_OUTSIDE -> {
                Log.d(DEBUG_TAG, "Movement occurred outside bounds of current screen element")
                true
            }
            else -> super.onTouchEvent(event)
        }
    }
}

جاوا

public class MainActivity extends Activity {
...
// This example shows an Activity. You can use the same approach if you are
// subclassing a View.
@Override
public boolean onTouchEvent(MotionEvent event){
    switch(event.getAction()) {
        case (MotionEvent.ACTION_DOWN) :
            Log.d(DEBUG_TAG,"Action was DOWN");
            return true;
        case (MotionEvent.ACTION_MOVE) :
            Log.d(DEBUG_TAG,"Action was MOVE");
            return true;
        case (MotionEvent.ACTION_UP) :
            Log.d(DEBUG_TAG,"Action was UP");
            return true;
        case (MotionEvent.ACTION_CANCEL) :
            Log.d(DEBUG_TAG,"Action was CANCEL");
            return true;
        case (MotionEvent.ACTION_OUTSIDE) :
            Log.d(DEBUG_TAG,"Movement occurred outside bounds of current screen element");
            return true;
        default :
            return super.onTouchEvent(event);
    }
}

این کد با ضربه زدن، لمس کردن و نگه داشتن و کشیدن کاربر، پیام‌هایی مانند پیام‌های زیر را در Logcat تولید می‌کند:

GESTURES D   Action was DOWN
GESTURES D   Action was UP
GESTURES D   Action was MOVE

برای حرکات سفارشی، می‌توانید پردازش خودتان را روی این رویدادها انجام دهید تا مشخص شود که آیا آنها نمایانگر حرکتی هستند که باید مدیریت کنید یا خیر. با این حال، اگر برنامه شما از حرکات رایج مانند دوبار ضربه زدن، لمس و نگه داشتن، پرتاب کردن و غیره استفاده می‌کند، می‌توانید از کلاس GestureDetector بهره ببرید. GestureDetector تشخیص حرکات رایج را بدون پردازش رویدادهای لمسی جداگانه برای شما آسان‌تر می‌کند. این موضوع در بخش تشخیص حرکات بیشتر مورد بحث قرار گرفته است.

رویدادهای لمسی را برای یک نمای واحد ضبط کنید

به عنوان جایگزینی برای onTouchEvent() ، می‌توانید با استفاده از متد setOnTouchListener() یک شیء View.OnTouchListener را به هر شیء View متصل کنید. این کار امکان گوش دادن به رویدادهای لمسی را بدون زیرکلاس‌سازی یک View موجود فراهم می‌کند، همانطور که در مثال زیر نشان داده شده است:

کاتلین

findViewById<View>(R.id.my_view).setOnTouchListener { v, event ->
    // Respond to touch events.
    true
}

جاوا

View myView = findViewById(R.id.my_view);
myView.setOnTouchListener(new OnTouchListener() {
    public boolean onTouch(View v, MotionEvent event) {
        // Respond to touch events.
        return true;
    }
});

مراقب ایجاد شنونده‌ای باشید که برای رویداد ACTION_DOWN false برمی‌گرداند. اگر این کار را انجام دهید، شنونده برای توالی رویدادهای ACTION_MOVE و ACTION_UP بعدی فراخوانی نمی‌شود. دلیل این امر این است که ACTION_DOWN نقطه شروع همه رویدادهای لمسی است.

اگر در حال ایجاد یک نمای سفارشی هستید، می‌توانید onTouchEvent() همانطور که قبلاً توضیح داده شد، بازنویسی کنید.

تشخیص حرکات

اندروید کلاس GestureDetector را برای تشخیص حرکات رایج ارائه می‌دهد. برخی از حرکاتی که پشتیبانی می‌کند عبارتند از onDown() ، onLongPress() و onFling() . می‌توانید GestureDetector همراه با متد onTouchEvent() که قبلاً توضیح داده شد، استفاده کنید.

تشخیص تمام حرکات پشتیبانی شده

وقتی یک شیء GestureDetectorCompat نمونه‌سازی می‌کنید، یکی از پارامترهایی که می‌گیرد، کلاسی است که رابط GestureDetector.OnGestureListener را پیاده‌سازی می‌کند. GestureDetector.OnGestureListener هنگام وقوع یک رویداد لمسی خاص به کاربران اطلاع می‌دهد. برای اینکه شیء GestureDetector شما بتواند رویدادها را دریافت کند، متد onTouchEvent() مربوط به view یا activity را بازنویسی کنید و تمام رویدادهای مشاهده شده را به نمونه detector ارسال کنید.

در قطعه کد زیر، مقدار بازگشتی true از متدهای on <TouchEvent> نشان می‌دهد که رویداد لمس مدیریت شده است. مقدار بازگشتی false رویدادها را تا زمانی که لمس با موفقیت مدیریت شود، از طریق پشته نما (view stack) به پایین منتقل می‌کند.

اگر قطعه کد زیر را در یک برنامه آزمایشی اجرا کنید، می‌توانید نحوه‌ی اجرای اکشن‌ها هنگام تعامل با صفحه لمسی و محتوای MotionEvent برای هر رویداد لمسی را درک کنید. سپس خواهید دید که برای تعاملات ساده چه مقدار داده تولید می‌شود.

کاتلین

private const val DEBUG_TAG = "Gestures"

class MainActivity :
        Activity(),
        GestureDetector.OnGestureListener,
        GestureDetector.OnDoubleTapListener {

    private lateinit var mDetector: GestureDetectorCompat

    // Called when the activity is first created.
    public override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // Instantiate the gesture detector with the
        // application context and an implementation of
        // GestureDetector.OnGestureListener.
        mDetector = GestureDetectorCompat(this, this)
        // Set the gesture detector as the double-tap
        // listener.
        mDetector.setOnDoubleTapListener(this)
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        return if (mDetector.onTouchEvent(event)) {
            true
        } else {
            super.onTouchEvent(event)
        }
    }

    override fun onDown(event: MotionEvent): Boolean {
        Log.d(DEBUG_TAG, "onDown: $event")
        return true
    }

    override fun onFling(
            event1: MotionEvent,
            event2: MotionEvent,
            velocityX: Float,
            velocityY: Float
    ): Boolean {
        Log.d(DEBUG_TAG, "onFling: $event1 $event2")
        return true
    }

    override fun onLongPress(event: MotionEvent) {
        Log.d(DEBUG_TAG, "onLongPress: $event")
    }

    override fun onScroll(
            event1: MotionEvent,
            event2: MotionEvent,
            distanceX: Float,
            distanceY: Float
    ): Boolean {
        Log.d(DEBUG_TAG, "onScroll: $event1 $event2")
        return true
    }

    override fun onShowPress(event: MotionEvent) {
        Log.d(DEBUG_TAG, "onShowPress: $event")
    }

    override fun onSingleTapUp(event: MotionEvent): Boolean {
        Log.d(DEBUG_TAG, "onSingleTapUp: $event")
        return true
    }

    override fun onDoubleTap(event: MotionEvent): Boolean {
        Log.d(DEBUG_TAG, "onDoubleTap: $event")
        return true
    }

    override fun onDoubleTapEvent(event: MotionEvent): Boolean {
        Log.d(DEBUG_TAG, "onDoubleTapEvent: $event")
        return true
    }

    override fun onSingleTapConfirmed(event: MotionEvent): Boolean {
        Log.d(DEBUG_TAG, "onSingleTapConfirmed: $event")
        return true
    }

}

جاوا

public class MainActivity extends Activity implements
        GestureDetector.OnGestureListener,
        GestureDetector.OnDoubleTapListener{

    private static final String DEBUG_TAG = "Gestures";
    private GestureDetectorCompat mDetector;

    // Called when the activity is first created.
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // Instantiate the gesture detector with the
        // application context and an implementation of
        // GestureDetector.OnGestureListener.
        mDetector = new GestureDetectorCompat(this,this);
        // Set the gesture detector as the double-tap
        // listener.
        mDetector.setOnDoubleTapListener(this);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event){
        if (this.mDetector.onTouchEvent(event)) {
            return true;
        }
        return super.onTouchEvent(event);
    }

    @Override
    public boolean onDown(MotionEvent event) {
        Log.d(DEBUG_TAG,"onDown: " + event.toString());
        return true;
    }

    @Override
    public boolean onFling(MotionEvent event1, MotionEvent event2,
            float velocityX, float velocityY) {
        Log.d(DEBUG_TAG, "onFling: " + event1.toString() + event2.toString());
        return true;
    }

    @Override
    public void onLongPress(MotionEvent event) {
        Log.d(DEBUG_TAG, "onLongPress: " + event.toString());
    }

    @Override
    public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
            float distanceY) {
        Log.d(DEBUG_TAG, "onScroll: " + event1.toString() + event2.toString());
        return true;
    }

    @Override
    public void onShowPress(MotionEvent event) {
        Log.d(DEBUG_TAG, "onShowPress: " + event.toString());
    }

    @Override
    public boolean onSingleTapUp(MotionEvent event) {
        Log.d(DEBUG_TAG, "onSingleTapUp: " + event.toString());
        return true;
    }

    @Override
    public boolean onDoubleTap(MotionEvent event) {
        Log.d(DEBUG_TAG, "onDoubleTap: " + event.toString());
        return true;
    }

    @Override
    public boolean onDoubleTapEvent(MotionEvent event) {
        Log.d(DEBUG_TAG, "onDoubleTapEvent: " + event.toString());
        return true;
    }

    @Override
    public boolean onSingleTapConfirmed(MotionEvent event) {
        Log.d(DEBUG_TAG, "onSingleTapConfirmed: " + event.toString());
        return true;
    }
}

تشخیص زیرمجموعه‌ای از حرکات پشتیبانی‌شده

اگر فقط می‌خواهید چند حرکت را پردازش کنید، می‌توانید به جای پیاده‌سازی رابط GestureDetector.SimpleOnGestureListener ، از GestureDetector.OnGestureListener ارث‌بری کنید.

GestureDetector.SimpleOnGestureListener با برگرداندن false برای همه متدهای on <TouchEvent> ، پیاده‌سازی‌ای برای همه آنها فراهم می‌کند. این به شما امکان می‌دهد فقط متدهایی را که برایتان مهم هستند، بازنویسی کنید. برای مثال، قطعه کد زیر کلاسی ایجاد می‌کند که GestureDetector.SimpleOnGestureListener را ارث‌بری می‌کند و onFling() و onDown() را بازنویسی می‌کند.

چه از GestureDetector.OnGestureListener استفاده کنید و چه GestureDetector.SimpleOnGestureListener ، بهترین روش این است که یک متد onDown() پیاده‌سازی کنید که true را برمی‌گرداند. دلیل این امر آن است که همه حرکات با یک پیام onDown() شروع می‌شوند. اگر از onDown() false را برگردانید، همانطور که GestureDetector.SimpleOnGestureListener به طور پیش‌فرض انجام می‌دهد، سیستم فرض می‌کند که شما می‌خواهید بقیه حرکت را نادیده بگیرید و سایر متدهای GestureDetector.OnGestureListener فراخوانی نمی‌شوند. این ممکن است باعث مشکلات غیرمنتظره‌ای در برنامه شما شود. فقط در صورتی از onDown() false را برگردانید که واقعاً می‌خواهید کل یک حرکت را نادیده بگیرید.

کاتلین

private const val DEBUG_TAG = "Gestures"

class MainActivity : Activity() {

    private lateinit var mDetector: GestureDetectorCompat

    public override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        mDetector = GestureDetectorCompat(this, MyGestureListener())
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        mDetector.onTouchEvent(event)
        return super.onTouchEvent(event)
    }

    private class MyGestureListener : GestureDetector.SimpleOnGestureListener() {

        override fun onDown(event: MotionEvent): Boolean {
            Log.d(DEBUG_TAG, "onDown: $event")
            return true
        }

        override fun onFling(
                event1: MotionEvent,
                event2: MotionEvent,
                velocityX: Float,
                velocityY: Float
        ): Boolean {
            Log.d(DEBUG_TAG, "onFling: $event1 $event2")
            return true
        }
    }
}

جاوا

public class MainActivity extends Activity {

    private GestureDetectorCompat mDetector;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mDetector = new GestureDetectorCompat(this, new MyGestureListener());
    }

    @Override
    public boolean onTouchEvent(MotionEvent event){
        if (this.mDetector.onTouchEvent(event)) {
              return true;
        }
        return super.onTouchEvent(event);
    }

    class MyGestureListener extends GestureDetector.SimpleOnGestureListener {
        private static final String DEBUG_TAG = "Gestures";

        @Override
        public boolean onDown(MotionEvent event) {
            Log.d(DEBUG_TAG,"onDown: " + event.toString());
            return true;
        }

        @Override
        public boolean onFling(MotionEvent event1, MotionEvent event2,
                float velocityX, float velocityY) {
            Log.d(DEBUG_TAG, "onFling: " + event1.toString() + event2.toString());
            return true;
        }
    }
}

منابع اضافی