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

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

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

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

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

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

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

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

سرعت ردیابی

شما می توانید یک ژست مبتنی بر حرکت داشته باشید که بر اساس مسافت یا جهتی است که نشانگر طی می کند. با این حال، سرعت اغلب یک عامل تعیین کننده در ردیابی ویژگی های یک ژست یا تصمیم گیری در مورد اینکه آیا ژست رخ داده است یا خیر است. برای آسان‌تر کردن محاسبه سرعت، اندروید کلاس 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;
    }
}

از گرفتن اشاره گر استفاده کنید

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

درخواست گرفتن اشاره گر

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

برای درخواست گرفتن اشاره گر، متد requestPointerCapture() را در view فراخوانی کنید. مثال کد زیر نحوه درخواست گرفتن اشاره گر را هنگامی که کاربر روی نما کلیک می کند نشان می دهد:

کاتلین

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

جاوا

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

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

رویدادهای نشانگر گرفته شده را مدیریت کنید

هنگامی که یک view با موفقیت ثبت اشاره گر را به دست آورد، 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() عکس‌برداری را از view دور کند، معمولاً به این دلیل که سلسله‌مراتب view حاوی نمایی که درخواست ضبط را می‌دهد، فوکوس را از دست می‌دهد.