ردگیری حرکات لمس و اشاره‌گر

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

این درس نحوه ردیابی حرکت در رویدادهای لمسی را شرح می‌دهد.

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

چون لمس با انگشت همیشه دقیق‌ترین شکل تعامل نیست، تشخیص رویدادهای لمسی اغلب بیشتر براساس حرکت است تا تماس ساده. برای کمک به برنامه‌ها در تشخیص اشاره‌های مبتنی بر حرکت (مانند تند کشیدن) و اشاره‌های غیرحرکتی (مانند تک‌ضربه)، Android مفهوم لغزش لمس را دربرمی‌گیرد. «لغزش لمس» به فاصله‌ای برحسب پیکسل اشاره دارد که لمس کاربر می‌تواند قبل‌از اینکه اشاره به‌عنوان اشاره مبتنی بر حرکت تفسیر شود، جابه‌جا شود. برای اطلاعات بیشتر درباره این موضوع، به مدیریت رویدادهای لمسی در ViewGroup مراجعه کنید.

بسته به نیازهای برنامه‌تان، روش‌های مختلفی برای ردیابی حرکت در اشاره وجود دارد. موارد زیر نمونه‌هایی از این نوع محتوا هستند:

  • موقعیت شروع و پایان اشاره‌گر، مانند جابه‌جا کردن شیء روی صفحه از نقطه الف به نقطه ب.
  • جهتی که اشاره‌گر در آن حرکت می‌کند، که با مختصات X و Y تعیین می‌شود.
  • سابقه کلیک کنید. می‌توانید اندازه سابقه اشاره را با فراخوانی روش MotionEvent getHistorySize() پیدا کنید. سپس می‌توانید موقعیت، اندازه، زمان، و فشار هرکدام از رویدادهای گذشته را بااستفاده از روش‌های getHistorical<Value> رویداد حرکتی به‌دست آورید. سابقه هنگام ارائه رد انگشت کاربر، مثلاً برای طراحی لمسی، مفید است. برای جزئیات، به مرجع MotionEvent مراجعه کنید.
  • سرعت اشاره‌گر هنگام حرکت در صفحه لمسی.

به منابع مرتبط زیر مراجعه کنید:

سرعت قطعه

می‌توانید اشاره‌ای مبتنی بر حرکت داشته باشید که براساس فاصله یا جهتی است که اشاره‌گر طی می‌کند. بااین‌حال، سرعت اغلب عاملی تعیین‌کننده در ردیابی ویژگی‌های اشاره یا تصمیم‌گیری درباره وقوع اشاره است. برای آسان‌تر کردن محاسبه سرعت، Android کلاس VelocityTracker را ارائه می‌دهد. ‫VelocityTracker به شما کمک می‌کند سرعت رویدادهای لمسی را ردیابی کنید. این برای اشاره‌هایی که سرعت بخشی از معیار اشاره است، مثل اشاره تند، مفید است.

در اینجا مثالی آورده شده است که هدف روش‌ها را در VelocityTracker API نشان می‌دهد:

کاتلین

private const val DEBUG_TAG = "Velocity"

class MainActivity : Activity() {
    private var mVelocityTracker: VelocityTracker? = null

    override fun onTouchEvent(event: MotionEvent): Boolean {

        when (event.actionMasked) {
            MotionEvent.ACTION_DOWN -> {
                // Reset the velocity tracker back to its initial state.
                mVelocityTracker?.clear()
                // If necessary, retrieve a new VelocityTracker object to watch
                // the velocity of a motion.
                mVelocityTracker = mVelocityTracker ?: VelocityTracker.obtain()
                // Add a user's movement to the tracker.
                mVelocityTracker?.addMovement(event)
            }
            MotionEvent.ACTION_MOVE -> {
                mVelocityTracker?.apply {
                    val pointerId: Int = event.getPointerId(event.actionIndex)
                    addMovement(event)
                    // When you want to determine the velocity, call
                    // computeCurrentVelocity(). Then, call getXVelocity() and
                    // getYVelocity() to retrieve the velocity for each pointer
                    // ID.
                    computeCurrentVelocity(1000)
                    // Log velocity of pixels per second. It's best practice to
                    // use VelocityTrackerCompat where possible.
                    Log.d("", "X velocity: ${getXVelocity(pointerId)}")
                    Log.d("", "Y velocity: ${getYVelocity(pointerId)}")
                }
            }
            MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
                // Return a VelocityTracker object back to be re-used by others.
                mVelocityTracker?.recycle()
                mVelocityTracker = null
            }
        }
        return true
    }
}

جاوا

public class MainActivity extends Activity {
    private static final String DEBUG_TAG = "Velocity";
        ...
    private VelocityTracker mVelocityTracker = null;
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int index = event.getActionIndex();
        int action = event.getActionMasked();
        int pointerId = event.getPointerId(index);

        switch(action) {
            case MotionEvent.ACTION_DOWN:
                if(mVelocityTracker == null) {
                    // Retrieve a new VelocityTracker object to watch the
                    // velocity of a motion.
                    mVelocityTracker = VelocityTracker.obtain();
                }
                else {
                    // Reset the velocity tracker back to its initial state.
                    mVelocityTracker.clear();
                }
                // Add a user's movement to the tracker.
                mVelocityTracker.addMovement(event);
                break;
            case MotionEvent.ACTION_MOVE:
                mVelocityTracker.addMovement(event);
                // When you want to determine the velocity, call
                // computeCurrentVelocity(). Then call getXVelocity() and
                // getYVelocity() to retrieve the velocity for each pointer ID.
                mVelocityTracker.computeCurrentVelocity(1000);
                // Log velocity of pixels per second. It's best practice to use
                // VelocityTrackerCompat where possible.
                Log.d("", "X velocity: " + mVelocityTracker.getXVelocity(pointerId));
                Log.d("", "Y velocity: " + mVelocityTracker.getYVelocity(pointerId));
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                // Return a VelocityTracker object back to be re-used by others.
                mVelocityTracker.recycle();
                break;
        }
        return true;
    }
}

استفاده از ضبط اشاره‌گر

برخی‌از برنامه‌ها، مانند بازی‌ها و رایانه از دور و کلاینت‌های مجازی‌سازی، از کنترل اشاره‌گر موشواره بهره می‌برند. ضبط اشاره‌گر ویژگی‌ای است که در Android 8.0 (سطح API‏ ۲۶) و بالاتر دردسترس است و این کنترل را با ارائه همه رویدادهای موشواره به نمای متمرکز در برنامه شما فراهم می‌کند.

درخواست ضبط اشاره‌گر

نمایی در برنامه‌تان می‌تواند درخواست ضبط اشاره‌گر کند، فقط وقتی‌که سلسله‌مراتب نمایی که آن را دربردارد تمرکز داشته باشد. به همین دلیل، زمانی که کنش کاربر خاصی در نما وجود دارد، مانند رویداد onClick() یا در گرداننده رویداد onWindowFocusChanged() فعالیت شما، درخواست ضبط اشاره‌گر کنید.

برای درخواست ضبط اشاره‌گر، requestPointerCapture() را در نما فراخوانی کنید. نمونه کد زیر نشان می‌دهد که چگونه می‌توان هنگام کلیک کاربر روی یک نما، درخواست ضبط اشاره‌گر کرد:

کاتلین

fun onClick(view: View) {
    view.requestPointerCapture()
}

جاوا

@Override
public void onClick(View view) {
    view.requestPointerCapture();
}

وقتی درخواست ضبط اشاره‌گر موفقیت‌آمیز باشد، Android onPointerCaptureChange(true) را فراخوانی می‌کند. سیستم رویدادهای موشواره را به نمای متمرکزشده در برنامه‌تان تحویل می‌دهد، به‌شرطی که در همان سلسله‌مراتب نمایی باشد که درخواست ضبط کرده است. برنامه‌های دیگر تا زمانی که ضبط رها نشود، دریافت رویدادهای موشواره را متوقف می‌کنند، ازجمله رویدادهای ACTION_OUTSIDE. Android رویدادهای اشاره‌گر را از منابعی غیراز موشواره به‌صورت عادی ارائه می‌دهد، اما اشاره‌گر موشواره دیگر قابل‌مشاهده نیست.

مدیریت رویدادهای اشاره‌گر ضبط‌شده

وقتی نمای موردنظر باموفقیت ضبط اشاره‌گر را به‌دست آورد، Android رویدادهای موشواره را ارائه می‌دهد. نمای متمرکز شما می‌تواند با انجام یکی از کارهای زیر، رویدادها را مدیریت کند:

نمونه کد زیر نحوه پیاده‌سازی onCapturedPointerEvent(MotionEvent) را نشان می‌دهد:

کاتلین

override fun onCapturedPointerEvent(motionEvent: MotionEvent): Boolean {
    // Get the coordinates required by your app.
    val verticalOffset: Float = motionEvent.y
    // Use the coordinates to update your view and return true if the event is
    // successfully processed.
    return true
}

جاوا

@Override
public boolean onCapturedPointerEvent(MotionEvent motionEvent) {
  // Get the coordinates required by your app.
  float verticalOffset = motionEvent.getY();
  // Use the coordinates to update your view and return true if the event is
  // successfully processed.
  return true;
}

نمونه کد زیر نحوه ثبت OnCapturedPointerListener را نشان می‌دهد:

کاتلین

myView.setOnCapturedPointerListener { view, motionEvent ->
    // Get the coordinates required by your app.
    val horizontalOffset: Float = motionEvent.x
    // Use the coordinates to update your view and return true if the event is
    // successfully processed.
    true
}

جاوا

myView.setOnCapturedPointerListener(new View.OnCapturedPointerListener() {
  @Override
  public boolean onCapturedPointer (View view, MotionEvent motionEvent) {
    // Get the coordinates required by your app.
    float horizontalOffset = motionEvent.getX();
    // Use the coordinates to update your view and return true if the event is
    // successfully processed.
    return true;
  }
});

چه از نمای سفارشی استفاده کنید یا شنونده‌ای را ثبت کنید، نمای شما MotionEvent را با مختصات اشاره‌گر دریافت می‌کند که حرکات نسبی مانند دلتاهای X یا Y را مشخص می‌کند، مشابه مختصاتی که توسط دستگاه گوی پیمایش ارائه می‌شود. می‌توانید مختصات را بااستفاده از getX() و getY() بازیابی کنید.

رها کردن ضبط اشاره‌گر

نمای برنامه شما می‌تواند با فراخوانی releasePointerCapture()، همان‌طور که در نمونه کد زیر نشان داده شده است، ضبط اشاره‌گر را آزاد کند:

کاتلین

override fun onClick(view: View) {
    view.releasePointerCapture()
}

جاوا

@Override
public void onClick(View view) {
    view.releasePointerCapture();
}

سیستم می‌تواند بدون اینکه شما صریحاً releasePointerCapture() را فراخوانی کنید، ضبط را از نما بگیرد، معمولاً به این دلیل که سلسله‌مراتب نمای حاوی نمایی که درخواست ضبط می‌کند، تمرکز را ازدست می‌دهد.