Jetpack Compose için Kotlin

Jetpack Compose, Kotlin etrafında geliştirildi. Kotlin bazı durumlarda özel daha kolay bir şekilde yazabileceğiniz deyimler. Başka bir Kotlin'e çevirirseniz, kulağa hoş geldiniz, büyük olasılıkla Compose'un gücünden yararlanma fırsatından yararlanabilecektir ve anlamak zordur. Daha fazla kazanıyorum Kotlin’in tarzına aşina olmak bu tehlikelerden kaçınmanıza yardımcı olabilir.

Varsayılan bağımsız değişkenler

Kotlin işlevi yazarken, işlev bağımsız değişkenleri için varsayılan değerler belirtebilirsiniz. Bu değerler, çağıran bu değerleri açıkça iletmezse kullanılır. Bu özellik, Bu da, aşırı yüklenmiş işlevler ihtiyacını ortadan kaldırır.

Örneğin, kare çizen bir işlev yazmak istediğinizi varsayalım. O fonksiyonunun uzunluğunun belirtildiği sideLength adlı tek bir gerekli parametresi olabilir temsil eder. Bu parametrede thickness gibi isteğe bağlı parametreler kullanılabilir. edgeColor vb.; arayan kişi bunları belirtmezse işlevi varsayılan değerleri kullanır. Diğer dillerde birkaç işlev yazmanız gerekebilir:

// We don't need to do this in Kotlin!
void drawSquare(int sideLength) { }

void drawSquare(int sideLength, int thickness) { }

void drawSquare(int sideLength, int thickness, Color edgeColor) { }

Kotlin'de tek bir işlev yazabilir ve şunları sağlar:

fun drawSquare(
    sideLength: Int,
    thickness: Int = 2,
    edgeColor: Color = Color.Black
) {
}

Bu, sizi gereksiz birden fazla işlev yazma zahmetinden kurtarmanın yanı sıra, özelliği, kodunuzun daha kolay okunmasını sağlar. Arayan bir bağımsız değişken için değer belirtmezse varsayılan değeri kullanmak istediğini gösterir. Ayrıca, adlandırılmış parametreler neler olduğunu daha kolay görmenizi sağlar. Kodu incelediğinizde aşağıdaki gibi bir işlev çağrısı görürseniz drawSquare() kodunu kontrol etmeden parametrelerin ne anlama geldiğini bilemeyebilirsiniz:

drawSquare(30, 5, Color.Red);

Bunun aksine, bu kod kendini belgelemektedir:

drawSquare(sideLength = 30, thickness = 5, edgeColor = Color.Red)

Çoğu Compose kitaplığı varsayılan bağımsız değişkenleri kullanır ve aynı işlevi görür. Bu sayede, composable'ları özelleştirilebilir, ancak yine de varsayılan davranışın çağrılmasını basitleştirir. Dolayısıyla, örneğin, şunun gibi basit bir metin öğesi oluşturabilirsiniz:

Text(text = "Hello, Android!")

Bu kod aşağıdakiyle aynı etkiye sahiptir. Çok daha ayrıntılı olan bu kod, hakkında daha fazla Text Parametreler açık bir şekilde ayarlanır:

Text(
    text = "Hello, Android!",
    color = Color.Unspecified,
    fontSize = TextUnit.Unspecified,
    letterSpacing = TextUnit.Unspecified,
    overflow = TextOverflow.Clip
)

İlk kod snippet'i çok daha basit ve okunması kolay olmasının yanı sıra kendi belgeleme sürecidir. Yalnızca text parametresini belirterek diğer tüm parametreleri istiyorsanız varsayılan değerleri kullanmanız gerekir. Buna karşın ikinci snippet, bu etiketler için değerleri açıkça ayarlamak istediğinizi diğer parametreleri kullanabilirsiniz, ancak ayarladığınız değerler işleve geri dönelim.

Üst düzey işlevler ve lambda ifadeleri

Kotlin, üst sırayı destekler işlevlerinin parametre olarak diğer işlevleri alırlar. Oluşturma, bu yaklaşımı temel alır. Örneğin, Button birleşebilir işlevi bir onClick lambda parametresi sağlar. Değer parametresi bir fonksiyondur ve kullanıcı tıkladığında düğme tarafından çağrılır:

Button(
    // ...
    onClick = myClickFunction
)
// ...

Daha yüksek düzeydeki işlevler lambda ifadeleri ve ifadelerle doğal bir şekilde eşlenir. bu da bir fonksiyona dönüşüyor. İşleve yalnızca bir kez ihtiyaç duyarsanız üst düzey işleve aktarmak üzere başka bir yerde tanımlamak için kullanılır. Bunun yerine fonksiyonu bir lambda ifadesiyle tanımlamanız yeterlidir. Önceki örnekte, myClickFunction()'ün başka bir yerde tanımlandığı varsayılmaktadır. Ancak yalnızca fonksiyonunu bir lambda ile satır içi olarak tanımlamak daha basittir ifade:

Button(
    // ...
    onClick = {
        // do something
        // do something else
    }
) { /* ... */ }

Sondaki lambdalar

Kotlin, son parametresi bir lambda parametresidir. Bu şekilde bir lambda ifadesi iletmek parametresini kullanmak için trailing lambda söz dizimini kullanın. Lambda ifadesini parantez içine almak yerine bu ifadeyi daha fazla bilgi edineceksiniz. Bu durum, Compose'da sık karşılaşılan bir durumdur. ile uyumlu hale getirebilirsiniz.

Örneğin, tüm düzenlerin son parametresi; Column() composable işlev, content, alt kullanıcı arayüzünü yayan bir işlevdir öğeler. Üç metin öğesi içeren bir sütun oluşturmak istediğinizi varsayalım, ve biraz biçimlendirme uygulamanız gerekir. Bu kod çalışır ancak zahmetli:

Column(
    modifier = Modifier.padding(16.dp),
    content = {
        Text("Some text")
        Text("Some more text")
        Text("Last text")
    }
)

content parametresi işlev imzasında son parametre olduğundan ve değerini lambda ifadesi olarak ilettiğimizde parantezlerden çıkarabiliriz:

Column(modifier = Modifier.padding(16.dp)) {
    Text("Some text")
    Text("Some more text")
    Text("Last text")
}

İki örnek de aynı anlama sahiptir. Küme ayraçları lambda'yı tanımlar content parametresine aktarılan bir ifadedir.

Aslında, ilettiğiniz tek parametre bu son lambda ise (yani son parametre bir lambda ise ve başka parametre iletmiyorsanız) parantezleri tamamen atlayabilirsiniz. Örneğin, Arkadaş Bitkiler projenizde Column öğesine bir değiştirici iletmeniz gerekmiyordu. Kodu şöyle yazabilirsiniz: bu:

Column {
    Text("Some text")
    Text("Some more text")
    Text("Last text")
}

Bu söz dizimi, Compose'da oldukça yaygındır. Özellikle Column Son parametre, öğenin alt öğeleri (alt öğeler) ise fonksiyon çağrısından sonra küme ayraçları içinde belirtilir.

Nişan dürbünleri ve alıcılar

Bazı yöntemler ve özellikler yalnızca belirli bir kapsamda kullanılabilir. Sınırlı kapsam, gerektiğinde işlevsellik sunmanızı ve kazara erişimi önlemenizi sağlar. o işlevin uygun olmadığı durumlarda kullanmayı düşünebilirsiniz.

Compose'da kullanılan bir örneği düşünün. Row düzenini çağırdığınızda composable, içeriğinizin lambda değeri bir RowScope içinde otomatik olarak çağrılır. Bu sayede Row, yalnızca Row içinde geçerli olan işlevleri sunabilir. Aşağıdaki örnekte, Row'ün align değiştiricisi için satıra özgü bir değeri nasıl gösterdiği gösterilmektedir:

Row {
    Text(
        text = "Hello world",
        // This Text is inside a RowScope so it has access to
        // Alignment.CenterVertically but not to
        // Alignment.CenterHorizontally, which would be available
        // in a ColumnScope.
        modifier = Modifier.align(Alignment.CenterVertically)
    )
}

Bazı API'ler, alıcı kapsamında çağrılan lambdaları kabul eder. Bu lambda'lar veya başka bir yerde tanımlanan özellik ve işlevlere parametre bildirimi:

Box(
    modifier = Modifier.drawBehind {
        // This method accepts a lambda of type DrawScope.() -> Unit
        // therefore in this lambda we can access properties and functions
        // available from DrawScope, such as the `drawRectangle` function.
        drawRect(
            /*...*/
            /* ...
        )
    }
)

Daha fazla bilgi için şununla birlikte işlev değişmez değerleri: alıcı hakkındaki makalemizi incelemenizi öneririz.

Yetki verilen mülkler

Kotlin, Google'dan yetki verilmiş özellikleri hakkında daha fazla bilgi edinin. Bu mülkler alan gibi çağrılır ancak değerleri bir ifadenin değerlendirilmesiyle dinamik olarak belirlenir. Bunları tanıyabilirsiniz özelliklerini kullanarak by söz dizimini kullanır:

class DelegatingClass {
    var name: String by nameGetterFunction()

    // ...
}

Diğer kod, mülküne şu kodla erişebilir:

val myDC = DelegatingClass()
println("The name property is: " + myDC.name)

println() yürütüldüğünde, değeri döndürmek için nameGetterFunction() çağrılır seçin.

Bu yetki verilmiş özellikler özellikle devlet destekli mülkler:

var showDialog by remember { mutableStateOf(false) }

// Updating the var automatically triggers a state change
showDialog = true

Veri sınıflarını imha etme

Bir verileri tanımlarsanız sınıfına girip bu verilere erişimi indirim aracı beyan başlıklı makaleyi inceleyin. Örneğin, Örneğin, bir Person sınıfı tanımladığınızı varsayalım:

data class Person(val name: String, val age: Int)

Bu türde bir nesneniz varsa değerlerine aşağıdaki gibi kodla erişebilirsiniz: bu:

val mary = Person(name = "Mary", age = 35)

// ...

val (name, age) = mary

Oluşturma işlevlerinde genellikle bu tür bir kod görürsünüz:

Row {

    val (image, title, subtitle) = createRefs()

    // The `createRefs` function returns a data object;
    // the first three components are extracted into the
    // image, title, and subtitle variables.

    // ...
}

Veri sınıfları, birçok başka faydalı işlev sunar. Örneğin, bir veri sınıfı tanımlarsanız derleyici, equals() ve copy(). Daha fazla bilgiyi veriler sınıfları dokümanlarına göz atın.

Singleton nesneleri

Kotlin, her zaman bir ve yalnızca bir örnek. Bu single'lar, object anahtar kelimesiyle belirtilir. Oluşturma işleminde genellikle bu tür nesneler kullanılır. Örneğin, MaterialTheme tek bir nesne olarak tanımlanır; MaterialTheme.colors, shapes ve typography özelliklerinin tümü geçerli temanın değerlerini içerir.

Tür açısından güvenli oluşturucular ve DSL'ler

Kotlin, tür açısından güvenli oluşturucularla alana özgü diller (DSL'ler) oluşturmanıza olanak tanır. DSL'ler karmaşık hiyerarşik verilerin oluşturulmasına olanak tanır daha sürdürülebilir ve okunabilir bir şekilde sunabilirsiniz.

Jetpack Compose, LazyRow ve LazyColumn.

@Composable
fun MessageList(messages: List<Message>) {
    LazyColumn {
        // Add a single item as a header
        item {
            Text("Message List")
        }

        // Add list of messages
        items(messages) { message ->
            Message(message)
        }
    }
}

Kotlin, aşağıdakileri kullanarak tür güvenli derleyiciler oluşturur: alıcıyla işlev değişmez değerini kontrol edin. Canvas composable, DrawScope alıcı onDraw: DrawScope.() -> Unit olarak görünür ve kod bloğunun DrawScope içinde tanımlanan üye işlevlerini çağırın.

Canvas(Modifier.size(120.dp)) {
    // Draw grey background, drawRect function is provided by the receiver
    drawRect(color = Color.Gray)

    // Inset content by 10 pixels on the left/right sides
    // and 12 by the top/bottom
    inset(10.0f, 12.0f) {
        val quadrantSize = size / 2.0f

        // Draw a rectangle within the inset bounds
        drawRect(
            size = quadrantSize,
            color = Color.Red
        )

        rotate(45.0f) {
            drawRect(size = quadrantSize, color = Color.Blue)
        }
    }
}

Tür güvenli derleyiciler ve DSL'ler hakkında daha fazla bilgi için Kotlin belgeleri.

Kotlin eş yordamları

Eş yordamlar, Kotlin. Eş yordamlar, ileti dizilerini engellemeden yürütmeyi askıya alabilir. CEVAP duyarlı kullanıcı arayüzü, doğası gereği eşzamansızdır ve Jetpack Compose bu sorunu eş yordamları API düzeyinde benimsemek

Jetpack Compose, kullanıcı arayüzü katmanında iş parçacıklarını güvenli bir şekilde kullanmanızı sağlayan API'ler sunar. rememberCoroutineScope işlevi, etkinlik işleyicilerde veCoroutineScope Askıya alma API'leri oluşturun. Şunu kullanarak aşağıdaki örneğe bakın: ScrollState animateScrollTo API.

// Create a CoroutineScope that follows this composable's lifecycle
val composableScope = rememberCoroutineScope()
Button(
    // ...
    onClick = {
        // Create a new coroutine that scrolls to the top of the list
        // and call the ViewModel to load data
        composableScope.launch {
            scrollState.animateScrollTo(0) // This is a suspend function
            viewModel.loadData()
        }
    }
) { /* ... */ }

Görev akışları, kod bloğunu varsayılan olarak sıralı olarak yürütür. Koşu askıya alma işlevini çağıran eş yordam, askıya alma işlevi geri gelir. Askıya alma işlevi farklı bir CoroutineDispatcher çalıştırmasını sağlayın. Önceki örnekte, animateScrollTo işlevi döndürülene kadar loadData yürütülmez.

Kodu eşzamanlı olarak yürütmek için yeni eş yordamların oluşturulması gerekir. Örnekte ekranın üst kısmına doğru kaydırmak ve viewModel, iki eş yordam gerekli.

// Create a CoroutineScope that follows this composable's lifecycle
val composableScope = rememberCoroutineScope()
Button( // ...
    onClick = {
        // Scroll to the top and load data in parallel by creating a new
        // coroutine per independent work to do
        composableScope.launch {
            scrollState.animateScrollTo(0)
        }
        composableScope.launch {
            viewModel.loadData()
        }
    }
) { /* ... */ }

Eş yordamlar, eşzamansız API'lerin birleştirilmesini kolaylaştırır. Sonraki örneğin, pointerInput değiştiricisini animasyon API'leriyle birleştirerek Kullanıcı ekrana dokunduğunda bir öğenin konumuna animasyon uygulayın.

@Composable
fun MoveBoxWhereTapped() {
    // Creates an `Animatable` to animate Offset and `remember` it.
    val animatedOffset = remember {
        Animatable(Offset(0f, 0f), Offset.VectorConverter)
    }

    Box(
        // The pointerInput modifier takes a suspend block of code
        Modifier
            .fillMaxSize()
            .pointerInput(Unit) {
                // Create a new CoroutineScope to be able to create new
                // coroutines inside a suspend function
                coroutineScope {
                    while (true) {
                        // Wait for the user to tap on the screen
                        val offset = awaitPointerEventScope {
                            awaitFirstDown().position
                        }
                        // Launch a new coroutine to asynchronously animate to
                        // where the user tapped on the screen
                        launch {
                            // Animate to the pressed position
                            animatedOffset.animateTo(offset)
                        }
                    }
                }
            }
    ) {
        Text("Tap anywhere", Modifier.align(Alignment.Center))
        Box(
            Modifier
                .offset {
                    // Use the animated offset as the offset of this Box
                    IntOffset(
                        animatedOffset.value.x.roundToInt(),
                        animatedOffset.value.y.roundToInt()
                    )
                }
                .size(40.dp)
                .background(Color(0xff3c1361), CircleShape)
        )
    }

Eş yordamlar hakkında daha fazla bilgi edinmek için Android'de Kotlin eş yordamları rehberi.