Hareketleri anlama

Anlaşılması gereken birkaç terim ve kavram vardır harekete geçme olasılığının daha yüksek olduğunu gördük. Bu sayfada, geçerli olan ve farklı soyutlamayı tanıtıyor. hareketler için düzeyler. Ayrıca etkinlik tüketimi ve yayılımıdır.

Tanımlar

Bu sayfadaki çeşitli kavramları anlamak için bazı kavramları anlamanız gerekir. tam olarak aynısı:

  • İşaretçi: Uygulamanızla etkileşimde bulunmak için kullanabileceğiniz fiziksel bir nesne. Mobil cihazlarda, en yaygın işaretçi parmağınızın dokunun. Alternatif olarak, parmağınızın yerine ekran kalemi de kullanabilirsiniz. Büyük ekranlarda ise mobil cihazlarla dolaylı olarak etkileşim kurmak için görebilirsiniz. Giriş cihazı, "işaretleme" özelliğini kullanabilmelidir. koordineli olacak şekilde dolayısıyla, örneğin klavye bir işaretçi olarak tıklayın. Oluşturma sekmesinde işaretçi türü, PointerType.
  • İşaretçi etkinliği: Bir veya daha fazla işaretçinin alt düzey etkileşimini tanımlar ilişkili olması gerekir. Her bir işaretçi etkileşimi (örneğin, veya fareyi sürüklemek bir etkinliği tetikler. İçinde Oluşturma, bu tür bir olaya ilişkin tüm bilgilerin PointerEvent sınıfı.
  • Hareket: Tek bir hareket olarak yorumlanabilecek işaretçi etkinlikleri dizisi eyleme dökülebilir. Örneğin, dokunma hareketi bir aşağı hareket dizisi olarak kabul edilebilir ardından bir yukarı etkinlik vardır. Pek çok kişi tarafından kullanılan yaygın hareketler vardır. dokunma, sürükleme veya dönüştürme gibi uygulamaların yanı sıra kendi özel klasörlerinizi de oluşturabilirsiniz. hareket ettirebilirsiniz.

Farklı soyutlama düzeyleri

Jetpack Compose, hareketleri kullanmak için farklı soyutlama düzeyleri sunar. Üst düzeyde bileşen desteği bulunur. Button gibi besteler hareket desteğini otomatik olarak dahil eder. Özel öğeye hareket desteği eklemek için bileşenlerine odaklanmak yerine clickable gibi hareket değiştiriciler ekleyebilirsiniz composables. Son olarak, özel bir harekete ihtiyacınız varsa pointerInput değiştiricisi.

Kural olarak, soruyu çözen en üst düzey soyutlamanın üzerine işlevlerinden yararlanabilirsiniz. Böylece, buradaki en iyi uygulamalardan yararlanabilirsiniz var. Örneğin, Button daha fazla anlamsal bilgi içerir, daha fazla bilgi içeren clickable değerine kıyasla daha yüksek pointerInput uygulaması.

Bileşen desteği

Oluşturma'daki kullanıma hazır bileşenlerin çoğunda bir tür dahili hareket bulunur ele alacağız. Örneğin, bir LazyColumn sürükleme hareketlerine şu şekilde yanıt verir: içeriğini kaydırdığınızda, Button içeriğe bastığınızda bir dalga SwipeToDismiss bileşeni bir öğeyi kapatmak için kaydırma mantığı öğesine dokunun. Bu tür hareket işleme otomatik olarak çalışır.

Dahili hareket işlemenin yanı sıra, birçok bileşen için çağrı yapan kullanıcının hareketi kullan. Örneğin, bir Button dokunmaları otomatik olarak algılar ve bir tıklama etkinliğini tetikler. Button yönüne giden onClick lambdayı harekete tepki verebilir. Benzer şekilde, onValueChange Kullanıcının kaydırma çubuğu tutma yerini sürüklemesine tepki vermek için Slider.

Kullanım alanınıza uyduğunda, bileşenlere dahil edilen hareketleri tercih edin. odak ve erişilebilirlik için kullanıma hazır destek içerir. olarak test edilmiştir. Örneğin, bir Button özel bir şekilde işaretlenir; erişilebilirlik hizmetlerinin bunu yalnızca herhangi bir öğe yerine bir düğme olarak tanımlaması tıklanabilir öğe:

// Talkback: "Click me!, Button, double tap to activate"
Button(onClick = { /* TODO */ }) { Text("Click me!") }
// Talkback: "Click me!, double tap to activate"
Box(Modifier.clickable { /* TODO */ }) { Text("Click me!") }

Compose'da erişilebilirlik hakkında daha fazla bilgi edinmek için Erişilebilirlik Oluştur'u tıklayın.

Değiştiricilerle rastgele composable'lara belirli hareketler ekleme

Rastgele istediğiniz composable'a hareket değiştiricileri uygulayarak hareketleri dinleyebilirsiniz. Örneğin, genel bir Box dokunma hareketlerini clickable yaparak tutun veya Column dikey kaydırmayı yönetmek için verticalScroll uygulayın.

Farklı hareket türlerini işlemek için birçok değiştirici vardır:

Kural olarak, özel hareket işleme yerine kullanıma hazır hareket değiştiricileri tercih edin. Değiştiriciler, işaretçi etkinlik işlemenin yanı sıra daha fazla işlev sunar. Örneğin, clickable değiştiricisi yalnızca basmaları algılamayı değil, aynı zamanda dokunmaların yanı sıra anlamsal bilgiler ve etkileşimlerle ilgili görsel işaretler de fareyle üzerine gelme, odaklanma ve klavye desteği. Kaynak kodunu kontrol edebilirsiniz inceleyin, işlevselliğin nasıl olduğunu görmek için clickable ekleniyor.

pointerInput değiştiriciyle rastgele composable'lara özel hareket ekleyin

Her hareket, kullanıma hazır bir hareket değiştiriciyle uygulanmaz. Örneğin, Örneğin, uzun bastıktan sonra sürüklemeye tepki vermek için bir değiştirici kontrol tuşunu basılı tutarken tıklamanızı veya üç parmakla dokunmanızı öneririz. Bunun yerine kendi hareketinizi yazabilirsiniz işleyiciyi kullanarak bu özel hareketleri tanımlayabilirsiniz. Hareket işleyici oluşturmak için Ham işaretçiye erişmenizi sağlayan pointerInput değiştiricisi etkinlikler.

Aşağıdaki kod, ham işaretçi etkinliklerini dinler:

@Composable
private fun LogPointerEvents(filter: PointerEventType? = null) {
    var log by remember { mutableStateOf("") }
    Column {
        Text(log)
        Box(
            Modifier
                .size(100.dp)
                .background(Color.Red)
                .pointerInput(filter) {
                    awaitPointerEventScope {
                        while (true) {
                            val event = awaitPointerEvent()
                            // handle pointer event
                            if (filter == null || event.type == filter) {
                                log = "${event.type}, ${event.changes.first().position}"
                            }
                        }
                    }
                }
        )
    }
}

Bu snippet'i bölerseniz temel bileşenler şu şekilde olur:

  • pointerInput değiştiricisi. Bir veya daha fazla anahtar geçirirsiniz. değeri değişirse, değiştirici içeriği lambda'sı yeniden yürütülür. Örnek, composable'a isteğe bağlı bir filtre iletir. Eğer değeri değişirse işaretçi etkinlik işleyicinin yeniden yürütülür.
  • awaitPointerEventScope, şunları yapmak için kullanılabilecek, eş değer bir kapsam oluşturur: işaretçi etkinliklerini bekleyin.
  • awaitPointerEvent, eş yordamı sonraki işaretçi etkinliğine kadar askıya alır gerçekleşir.

Ham giriş etkinliklerini dinlemek güçlü olsa da yazmak da karmaşıktır. bu ham verilere dayalı özel bir harekettir. Özel öğeler oluşturulmasını kolaylaştırmak için hareketlerinde birçok yardımcı yöntem kullanılabilir.

Tüm hareketleri algıla

Ham işaretçi etkinliklerini işlemek yerine belirli hareketleri dinleyebilirsiniz. ve uygun şekilde yanıt vermesi gerekir. AwaitPointerEventScope, dinleme yöntemleri:

Bunlar üst düzey algılayıcılardır. Dolayısıyla, tek bir algılayıcıya birden çok algılayıcı ekleyemezsiniz. pointerInput değiştiricisi. Aşağıdaki snippet yalnızca dokunmaları algılar, sürükler:

var log by remember { mutableStateOf("") }
Column {
    Text(log)
    Box(
        Modifier
            .size(100.dp)
            .background(Color.Red)
            .pointerInput(Unit) {
                detectTapGestures { log = "Tap!" }
                // Never reached
                detectDragGestures { _, _ -> log = "Dragging" }
            }
    )
}

Dahili olarak detectTapGestures yöntemi eş yordamı engeller ve ikinci algılayıcıya hiçbir zaman ulaşılmadı. Görüşmeye birden fazla hareket işleyici eklemeniz gerekiyorsa bunun yerine ayrı pointerInput değiştirici örnekleri kullanın:

var log by remember { mutableStateOf("") }
Column {
    Text(log)
    Box(
        Modifier
            .size(100.dp)
            .background(Color.Red)
            .pointerInput(Unit) {
                detectTapGestures { log = "Tap!" }
            }
            .pointerInput(Unit) {
                // These drag events will correctly be triggered
                detectDragGestures { _, _ -> log = "Dragging" }
            }
    )
}

Hareket başına etkinlik işleme

Hareketler, tanımı gereği aşağı işaretçi etkinliğiyle başlar. URL parametrelerinin Google tarafından nasıl ele alınmasını istediğinizi belirtmek için Bu durumda while(true) döngüsü yerine awaitEachGesture yardımcı yöntemi her bir ham etkinlikten geçer. awaitEachGesture yöntemi, yeniden başlatılacak tüm işaretçiler kaldırıldığında hareketin "yapıldığını" belirten blok tamamlandı:

@Composable
private fun SimpleClickable(onClick: () -> Unit) {
    Box(
        Modifier
            .size(100.dp)
            .pointerInput(onClick) {
                awaitEachGesture {
                    awaitFirstDown().also { it.consume() }
                    val up = waitForUpOrCancellation()
                    if (up != null) {
                        up.consume()
                        onClick()
                    }
                }
            }
    )
}

Pratikte, kullanmadığınız sürece neredeyse her zaman awaitEachGesture Hareketleri tanımlamadan işaretçi etkinliklerine yanıt verme. Buna örnek olarak hoverable: İşaretçi aşağı veya yukarı hareketlerine yanıt vermez. Yalnızca bilmesi gerekir.

Belirli bir etkinliği veya alt hareketi bekleme

Hareketlerin yaygın kısımlarını tanımlamaya yardımcı olan bir dizi yöntem vardır:

Çok noktalı etkinliklere ilişkin hesaplamaları uygulama

Bir kullanıcı birden fazla işaretçi kullanarak çoklu dokunma hareketi yaptığında ham değerlere göre gerekli dönüşümün anlaşılması karmaşıktır. transformable değiştiricisi veya detectTransformGestures yöntemlerinin kullanım alanınız için yeterince ayrıntılı kontrol sağlamadığını ham etkinlikleri dinleyip bunlara hesaplamalar yapabilirsiniz. Bu yardımcı yöntemler şunlardır: calculateCentroid, calculateCentroidSize, calculatePan, calculateRotation ve calculateZoom.

Etkinlik gönderme ve isabet testi

Her işaretçi etkinliği her pointerInput değiştiriciye gönderilmez. Etkinlik işlevi şu şekilde çalışır:

  • İşaretçi etkinlikleri bir composable hiyerarşiye gönderilir. Bir kullanıcının yeni işaretçi ilk işaretçi etkinliğini tetikler. Sistem, isabet testine "uygun" composables. Bir composable aşağıdaki durumlarda uygun kabul edilir: İşaretçi girişi işleme özellikleri. Kullanıcı arayüzünün üst kısmından isabet testi akışları bir plan yapın. Bir composable, "isabet"tir. İşaretçi etkinliği gerçekleştiğinde emin olmanız gerekir. Bu işlem, her öğenin composable'ların olumlu sonuçlar verdiğini görüyor.
  • Varsayılan olarak yalnızca en yüksek Z-endeksine sahip composable "isabet"tir. Örneğin, Örneğin, bir Box öğesine çakışan iki Button composable eklediğinizde, bu işlem yalnızca en üste çizilen etkinlik, tüm işaretçi etkinliklerini alır. Teorik olarak Kendi PointerInputModifierNode öğenizi oluşturarak bu davranışı geçersiz kılın uygulama ve sharePointerInputWithSiblings değerini "doğru" olarak ayarlama.
  • Aynı işaretçi için diğer etkinlikler aynı işaretleyiciye composable'ların yanı sıra etkinlik yayılım mantığına göre akış gösterir. Sistem artık bu işaretçi için isabet testi yapmaz. Bunun anlamı, her bir composable, zincirdeki composable, bunlar, composable'ın sınırlarının dışında gerçekleşir. Özelleştirilebilir zincirinde işaretçi etkin olmasa bile hiçbir zaman işaretçi dışına çıkamazlar.

Fareyle veya ekran kaleminin üzerine gelmesiyle tetiklenen fareyle üzerine gelme etkinlikleri, kurallarından birini seçin. Fareyle üzerine gelme etkinlikleri, isabet ettikleri composable'lara gönderilir. ODK Kullanıcı işaretçiyi bir composable'ın sınırlarından diğerine geçtiğinde, etkinlikler, bu ilk composable'a gönderilmek yerine yeni composable.

Etkinlik tüketimi

Birden fazla composable'a hareket işleyici atanmışsa çakışmamalıdır. Örneğin, şu kullanıcı arayüzüne bir göz atın:

Resim, iki metin içeren bir sütun ve düğme içeren liste öğesi.

Kullanıcı yer işareti düğmesine dokunduğunda, düğmenin onClick lambda'sı bunu işler hareketi yapın. Kullanıcı, liste öğesinin başka bir bölümüne dokunduğunda ListItem Ardından bu hareketi gerçekleştirir ve makaleye gider. İşaretçi girdisi olarak, Düğmenin bu etkinliği kullanması gerekir. Böylece, ebeveynin tepki verebilir. Kullanıma hazır bileşenlerde bulunan hareketler genel hareket değiştiriciler bu tüketim davranışını içerir, ancak hareketinizi yazarken etkinlikleri manuel olarak kullanmanız gerekir. Şunu yapacaksınız: PointerInputChange.consume yöntemiyle:

Modifier.pointerInput(Unit) {

    awaitEachGesture {
        while (true) {
            val event = awaitPointerEvent()
            // consume all changes
            event.changes.forEach { it.consume() }
        }
    }
}

Bir etkinliğin kullanılması, etkinliğin diğer composable'lara yayılmasını durdurmaz. CEVAP composable'ın, tüketilen etkinlikleri açıkça yoksayması gerekir. Yazarken bir etkinliğin başka bir etkinlik tarafından kullanılıp kullanılmadığını öğe:

Modifier.pointerInput(Unit) {
    awaitEachGesture {
        while (true) {
            val event = awaitPointerEvent()
            if (event.changes.any { it.isConsumed }) {
                // A pointer is consumed by another gesture handler
            } else {
                // Handle unconsumed event
            }
        }
    }
}

Etkinlik yayılımı

Daha önce de belirtildiği gibi, işaretçi değişiklikleri, ulaştığı her composable'a iletilir. Peki, böyle birden fazla composable varsa etkinlikler hangi sırayla yayılır mı? Son bölümdeki örneği alırsak bu kullanıcı arayüzü, yalnızca ListItem ve Button öğesinin yanıt verdiği aşağıdaki kullanıcı arayüzü ağacı işaretçi etkinlikleri:

Ağaç yapısı. Üst katman ListItem; ikinci katmanda Resim, Sütun ve Düğme bulunur ve Sütun iki Metne bölünmüştür. ListItem ve Button vurgulanmış.

İşaretçi etkinlikleri, bu composable'ların her birinden üç kez, üçüncü sırada "passes":

  • İlk geçişte etkinlik, kullanıcı arayüzü ağacının tepesinden başlayıp tıklayın. Bu akış, çocuk henüz müdahale etmeden bir ebeveynin etkinliğe müdahale etmesine olanak tanır. onu tüketirim. Örneğin, ipuçlarının bir meta veri içeriğini çocuklara uzun basmak yerine Şurada: örnek olarak ListItem, etkinliği Button öncesinde alır.
  • Ana kartta etkinlik, kullanıcı arayüzü ağacının yaprak düğümlerinden kök olacaktır. Bu aşamada normalde hareketleri tüketirsiniz ve varsayılan geçiş kartıdır. Bu karttaki hareketler işleniyor yaprak düğümlerinin, üst düğümlerine göre öncelikli olduğu anlamına gelir. çoğu hareket için en mantıklı davranıştır. Örneğimizde Button, ListItem öncesindeki etkinliğe.
  • Son geçişte, etkinlik kullanıcı arayüzünün üst tarafından bir kez daha akar. yaprak düğümlerine taşımaya başladı. Bu akış, yığında daha yüksek olan öğelerin ve ebeveynleri tarafından tüketimlerine yanıt vermelerine yardımcı olur. Örneğin, bir düğme kaydırma çubuğu, kaydırılabilir üst öğesinin sürüklemesine dönüştüğünde dalga göstergesi.

Etkinlik akışı görsel olarak aşağıdaki gibi gösterilebilir:

Bir giriş değişikliği kullanıldığında, bu bilgi yeni bir noktada:

Kodda, ilgilendiğiniz kartı belirtebilirsiniz:

Modifier.pointerInput(Unit) {
    awaitPointerEventScope {
        val eventOnInitialPass = awaitPointerEvent(PointerEventPass.Initial)
        val eventOnMainPass = awaitPointerEvent(PointerEventPass.Main) // default
        val eventOnFinalPass = awaitPointerEvent(PointerEventPass.Final)
    }
}

Bu kod snippet'inde, her bir kod snippet'inde aynı etkinliği bunlar yöntem çağrılarını bekliyor, ancak tüketimle ilgili veriler değiştirildi.

Hareketleri test edin

Test yöntemlerinizde, performTouchInput yöntemini kullanabilirsiniz. Bu, ister üst düzey ister tam hareketler (ör. sıkıştırma veya uzun tıklama) ya da düşük seviyeli hareketler (ör. imleci belirli bir piksel kadar hareket ettirdiğinizde):

composeTestRule.onNodeWithTag("MyList").performTouchInput {
    swipeUp()
    swipeDown()
    click()
}

Daha fazla örnek için performTouchInput belgelerine bakın.

Daha fazla bilgi

Jetpack Compose'daki hareketler hakkında daha fazla bilgiyi aşağıdaki bölümlerde bulabilirsiniz. kaynaklar:

ziyaret edin. ziyaret edin.