Dokunma ve işaretçi hareketlerini izleme

Compose yöntemini deneyin
Jetpack Compose, Android için önerilen kullanıcı arayüzü araç setidir. Compose'da dokunma ve giriş işlemlerini nasıl kullanacağınızı öğrenin.

Bu derste, dokunma etkinliklerindeki hareketin nasıl izleneceği açıklanmaktadır.

Mevcut dokunma temasının konumu, basıncı veya boyutu her değiştiğinde ACTION_MOVE etkinliğiyle yeni bir onTouchEvent() tetiklenir. Genel hareketleri algılama bölümünde açıklandığı gibi, bu etkinliklerin tümü onTouchEvent() parametresinin MotionEvent parametresine kaydedilir.

Parmakla dokunma her zaman en hassas etkileşim şekli olmadığından, dokunma etkinliklerinin algılanması genellikle basit temastan ziyade harekete dayanır. Android, uygulamaların harekete dayalı hareketler (ör. kaydırma) ile harekete dayalı olmayan hareketler (ör. tek dokunma) arasında ayrım yapmasına yardımcı olmak için dokunma eğimi kavramını içerir. Dokunma toleransı, kullanıcının dokunuşunun harekete dayalı bir hareket olarak yorumlanmadan önce kaç piksel hareket edebileceğini ifade eder. Bu konu hakkında daha fazla bilgi için ViewGroup'taki dokunma etkinliklerini yönetme başlıklı makaleyi inceleyin.

Uygulamanızın ihtiyaçlarına bağlı olarak, bir hareketteki hareketi izlemenin çeşitli yolları vardır. Örnekler:

  • Bir işaretçinin başlangıç ve bitiş konumu (ör. ekrandaki bir nesneyi A noktasından B noktasına taşıma).
  • İşaretçinin X ve Y koordinatları tarafından belirlenen hareket yönü.
  • Geçmiş'e dokunun. MotionEvent yöntemini çağırarak bir hareketin geçmişinin boyutunu bulabilirsiniz getHistorySize(). Daha sonra, hareket etkinliğinin getHistorical<Value> yöntemlerini kullanarak geçmiş etkinliklerin her birinin konumlarını, boyutlarını, zamanını ve basınçlarını elde edebilirsiniz. Geçmiş, kullanıcının parmağının izini oluştururken (ör. dokunarak çizim yaparken) kullanışlıdır. Ayrıntılar için MotionEvent referansına bakın.
  • İşaretçinin dokunmatik ekranda hareket ederken sahip olduğu hız.

Aşağıdaki ilgili kaynaklara göz atın:

Parça hızı

İşaretçinin katettiği mesafeye veya yöne dayalı, harekete dayalı bir hareket kullanabilirsiniz. Ancak hız, genellikle bir hareketin özelliklerini izlemede veya hareketin gerçekleşip gerçekleşmediğine karar vermede belirleyici bir faktördür. Android, hız hesaplamasını kolaylaştırmak için VelocityTracker sınıfını sağlar. VelocityTracker, dokunma etkinliklerinin hızını izlemenize yardımcı olur. Bu, hızın hareket ölçütlerinden biri olduğu hareketler (ör. hızlıca kaydırma) için kullanışlıdır.

VelocityTracker API'sindeki yöntemlerin amacını gösteren bir örneği aşağıda bulabilirsiniz:

Kotlin

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

Java

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

İşaretçi yakalama özelliğini kullanma

Oyunlar, uzaktan masaüstü ve sanallaştırma istemcileri gibi bazı uygulamalar, fare işaretçisinin kontrolünü ele geçirmekten yararlanır. İşaretçi yakalama, Android 8.0 (API düzeyi 26) ve sonraki sürümlerde kullanılabilen bir özelliktir. Bu özellik, tüm fare etkinliklerini uygulamanızdaki odaklanmış bir görünüme ileterek bu kontrolü sağlar.

İşaretçi yakalama isteğinde bulunma

Uygulamanızdaki bir görünüm, yalnızca kendisini içeren görünüm hiyerarşisi odaklandığında işaretçi yakalama isteğinde bulunabilir. Bu nedenle, görünümde belirli bir kullanıcı işlemi olduğunda (ör. onClick() etkinliği sırasında veya etkinliğinizin onWindowFocusChanged() etkinlik işleyicisinde) işaretçi yakalama isteğinde bulunun.

İşaretçi yakalama isteğinde bulunmak için görünümde requestPointerCapture() yöntemini çağırın. Aşağıdaki kod örneğinde, kullanıcı bir görünümü tıkladığında işaretçi yakalama isteğinde bulunma işlemi gösterilmektedir:

Kotlin

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

Java

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

İşaretçiyi yakalama isteği başarılı olduğunda Android, onPointerCaptureChange(true) işlevini çağırır. Sistem, fare etkinliklerini, yakalamayı isteyen görünümle aynı görünüm hiyerarşisinde olduğu sürece uygulamanızdaki odaklanılmış görünüme iletir. Diğer uygulamalar, yakalama serbest bırakılana kadar ACTION_OUTSIDE etkinlikleri de dahil olmak üzere fare etkinliklerini almayı durdurur. Android, fare dışındaki kaynaklardan gelen işaretçi etkinliklerini normal şekilde iletir ancak fare işaretçisi artık görünmez.

Yakalanan işaretçi etkinliklerini işleme

Bir görünüm işaretçi yakalamayı başarıyla elde ettiğinde Android, fare etkinliklerini sunar. Odaklanmış görünümünüz, aşağıdaki görevlerden birini gerçekleştirerek etkinlikleri işleyebilir:

Aşağıdaki kod örneğinde onCapturedPointerEvent(MotionEvent) nasıl uygulanacağı gösterilmektedir:

Kotlin

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
}

Java

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

Aşağıdaki kod örneğinde, OnCapturedPointerListener öğesinin nasıl kaydedileceği gösterilmektedir:

Kotlin

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
}

Java

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

İster özel bir görünüm kullanın ister bir dinleyici kaydedin, görünümünüz, X veya Y deltas gibi göreli hareketleri belirten işaretçi koordinatlarına sahip bir MotionEvent alır. Bu koordinatlar, trackball cihazı tarafından sağlanan koordinatlara benzer. Koordinatları getX() ve getY() kullanarak alabilirsiniz.

İşaretçi yakalama özelliğini bırakma

Uygulamanızdaki görünüm, aşağıdaki kod örneğinde gösterildiği gibi releasePointerCapture() çağrısı yaparak işaretçi yakalamayı serbest bırakabilir:

Kotlin

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

Java

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

Sistem, genellikle yakalama isteğinde bulunan görünümü içeren görünüm hiyerarşisi odağını kaybettiği için releasePointerCapture() işlevini açıkça çağırmadan yakalamayı görünümden kaldırabilir.