تشخیص اشاره‌های رایج

امتحان کردن روش «نوشتن»
‫Jetpack Compose جعبه‌ابزار واسط کاربر توصیه‌شده برای Android است. با نحوه استفاده از لمس و ورودی در «نوشتن» آشنا شوید.

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

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

کلاس‌های AndroidX

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

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

وقتی کاربر یک یا چند انگشت را روی صفحه‌نمایش قرار می‌دهد، این کار باعث راه‌اندازی بازخوانی onTouchEvent() در نمای دریافت‌کننده رویدادهای لمسی می‌شود. برای هر توالی رویدادهای لمسی—مانند موقعیت، فشار، اندازه، و اضافه شدن انگشت دیگر—که به‌عنوان اشاره شناسایی می‌شود، 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;
    }
});

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

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

تشخیص اشاره‌ها

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

شناسایی همه اشاره‌های پشتیبانی‌شده

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

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

اگر قطعه کد زیر را در برنامه آزمایشی اجرا کنید، می‌توانید متوجه شوید که کنش‌ها چگونه هنگام تعامل با صفحه لمسی راه‌اندازی می‌شوند و محتوای 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.OnGestureListener، GestureDetector.SimpleOnGestureListener را گسترش دهید.

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

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

کاتلین

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;
        }
    }
}

منابع بیشتر