İçeriğe geç
academia.sh

Ders 10 / 17

Tembel Listeler

Tembel listede yalnız görünen satırların arayüz ağacına girmesinin, kaydırma konumunun sahibine çıkışının ve ağaçta olmayan satıra giden verinin ölçümü.

Yayın Güncelleme

Bu derste

  • Tembel listenin yalnız görünen aralıktaki satırları ağaca aldığını ölçer
  • Kaydırma konumunun listeden durumun sahibine yukarı gittiğini gösterir
  • Ekranda olmayan satıra giden verinin ağaç dışında kaldığını ayırt eder
  • Öge anahtarının satırın kimliğini konumdan ayırdığını açıklar
İçindekiler

Önceki derste ögelerin hepsi ağaçtaydı ve bir öge ötekine ancak ebeveyn üzerinden bağlanabiliyordu. Bir satır listesinde bu varsayım pahalıya gelir: bin satırlık bir sepeti bin düğümle kurmak, ekranda beş satır görünürken de bin satırı ölçmek ve yerleştirmek demektir. Tembel liste bunu yapmaz. Yalnız görünen aralıktaki satırlar için düğüm oluşturur; kullanıcı kaydırdıkça aralık kayar, ekrandan çıkan satırın düğümü ağaçtan düşer ve yeni giren satır için yenisi kurulur.

Bu durum ağaç ölçüsünü doğrudan etkiler. Ağaçta olmayan bir düğüme veri gönderilemez. Bu ders, bir satırın ağaçta bulunup bulunmamasının, ona giden verinin yönünü nasıl değiştirdiğini ölçer. Aynı derste ters yöndeki bir veri de ölçülür: listenin kaydırma konumu, listeyi kullanan ekrana nasıl ulaşır?

// Direction.kt — bir verinin arayuz agacinda hangi yone gittigini siniflandirir
class Tree(private val parentOf: Map<String, String>) {
    fun contains(node: String): Boolean = node in parentOf.keys || node in parentOf.values
    fun parent(node: String): String? = parentOf[node]
    fun ancestors(node: String): List<String> {
        val chain = mutableListOf<String>()
        var current = parentOf[node]
        while (current != null) {
            chain.add(current)
            current = parentOf[current]
        }
        return chain
    }
}

class Move(val label: String, val from: String, val to: String, tree: Tree) {
    val direction: String = when {
        !tree.contains(from) || !tree.contains(to) -> "dis"
        to in tree.ancestors(from) -> "yukari"
        tree.parent(to) == from -> "asagi"
        from in tree.ancestors(to) -> "derin"
        else -> error("akrabasiz: $from ve $to")
    }

    fun describe() {
        println("$label: kimden=$from | kime=$to -> $direction")
    }
}

fun countDirections(moves: List<Move>) {
    for (name in listOf("asagi", "yukari", "derin", "dis")) {
        println("$name: ${moves.count { it.direction == name }}")
    }
}

Tembel Liste ve Öge

LazyColumn, dikey tembel listedir; yatay karşılığı LazyRow’dur. Satırlar items ile tanımlanır: bir koleksiyon ve her öge için bir bileşen işlevi verilir. Sütun ve satır düzenleri çocuklarını bir kerede ölçerken tembel liste, ölçmeyi görünen aralıkla sınırlar. Kaydırma konumu bir durum nesnesinde tutulur (LazyListState); bu nesneyi oluşturan ve hatırlayan bileşen işlevi, konumu okuyabilir.

// Platform kodu: tembel liste, ogeler ve kaydirma durumu
@Composable
fun CartList(products: List<Product>) {
    val listState = rememberLazyListState()
    LazyColumn(state = listState) {
        items(products, key = { it.id }) { product ->
            ProductRow(product)
        }
    }
    val first = listState.firstVisibleItemIndex
}

Burada CartList, listState’in sahibidir: durumu o oluşturur ve hatırlar. LazyColumn bu durumu parametre olarak alır, kaydırma değiştikçe onu günceller. Satırlar items lambdasında oluşturulur, bu yüzden ağaçta LazyColumn’un çocuklarıdır. CartList, LazyColumn’un ebeveynidir.

Model, platformun bu kuralını kurar. Model, platformun kendisi değildir. Yardımcı dosya iki şey yapar: toplam satır sayısı, ilk görünen satırın sırası ve ekrana sığan satır sayısından görünen aralığı çıkarır; sonra bu aralıktaki satırlardan bir arayüz ağacı kurar. Ağaç Screen’in altında LazyList’i, onun altında yalnız görünen satırları taşır. Ölçümlerin uçlarını da iki işlev verir: rowName bir satırın düğüm adını üretir, scrollOwner listenin ebeveynini ağaçtan okur. Bu derste hareketin uçları elle yazılmaz; en az bir ucu bu işlevlerden gelir.

// Window.kt — tembel listenin gorunen satirlari (modelin hesabi)
fun visibleRows(total: Int, firstVisible: Int, viewportRows: Int): List<String> =
    (firstVisible until minOf(total, firstVisible + viewportRows)).map { rowName(it) }

fun rowName(index: Int): String = "Row$index"

fun scrollOwner(tree: Tree): String = tree.parent("LazyList") ?: error("liste kokte")

fun listTree(rows: List<String>): Tree {
    val parentOf = mutableMapOf("Screen" to "App", "LazyList" to "Screen")
    for (row in rows) {
        parentOf[row] = "LazyList"
    }
    return Tree(parentOf)
}

Görünen Aralık

İlk ölçüm bin satırlık bir koleksiyonu ele alır. Ekrana beş satır sığar ve liste en başta durur. Ağaçta kaç satır olduğu, ve ilk görünen satıra giden verinin yönü basılır.

// Main.kt — bin satirlik listede agactaki satirlar
fun main() {
    val rows = visibleRows(1000, 0, 5)
    val tree = listTree(rows)
    println("agactaki satir: ${rows.size}")
    println("satirlar: ${rows.joinToString(",")}")
    val moves = listOf(Move("veri-ilk-satir", "LazyList", rows.first(), tree))
    for (move in moves) {
        move.describe()
    }
}
agactaki satir: 5
satirlar: Row0,Row1,Row2,Row3,Row4
veri-ilk-satir: kimden=LazyList | kime=Row0 -> asagi

Bin satırdan beşi ağaçtadır. Listenin ilk satıra verdiği veri, ebeveynden çocuğa, yani aşağı gitti. Bu, önceki derslerdeki sütun çocuğuyla aynı hareketin tembel bir listedeki biçimidir.

AD1. Model ekrana sığan satır sayısını sabit alır ve satır yüksekliklerini hesaba katmaz. Platformun görünür aralığın hemen ötesindeki satırları önceden hazırlayabileceği de modelde yoktur.

Kaydırma Konumu Sahibine Çıkar

Kullanıcı kaydırınca ilk görünen satır değişir. Bu bilgiyi liste üretir: kaydırma jestini işleyen liste, durum nesnesine yeni konumu yazar. Durumun sahibi Screen ya da onun altındaki bir bileşen işleviyse, konum listeden sahibine gitmiş olur. Liste, sahibinin altındadır, yani sahibi listenin atasıdır; hareket çocuktan ataya gider: yukarı.

Aşağıdaki ölçüm listeyi üç satır kaydırır. Aralık Row3’ten Row7’ye kayar; Row0 ağaçtan çıkar, Row5…Row7 ise öncekinde olmayan yeni satırlardır. Kaydırma konumu Screen’e gider.

// Main.kt — kaydirma sonrasi aralik ve konumun sahibine cikisi
fun main() {
    val before = visibleRows(1000, 0, 5)
    val after = visibleRows(1000, 3, 5)
    val tree = listTree(after)
    println("once: ${before.joinToString(",")}")
    println("sonra: ${after.joinToString(",")}")
    println("ilk gorunen sira: 3")
    val moves = listOf(Move("kaydirma-konumu", "LazyList", scrollOwner(tree), tree))
    for (move in moves) {
        move.describe()
    }
}
once: Row0,Row1,Row2,Row3,Row4
sonra: Row3,Row4,Row5,Row6,Row7
ilk gorunen sira: 3
kaydirma-konumu: kimden=LazyList | kime=Screen -> yukari

Aralık beş satırlık kaldı, ama içerik değişti: Row0, Row1 ve Row2 ağaçtan çıktı; Row5, Row6 ve Row7 girdi. Kaydırma konumu LazyList’ten Screen’e yukarı gitti. Bu hareket kullanıcının parmağından değil, listenin kendi durumundan gelir; ekran bu sayede “kullanıcı beşinci satıra yaklaştı” gibi bir bilgiyi okuyabilir.

AD2. Model kaydırma konumunu tek bir tamsayı, ilk görünen satırın sırası olarak taşır. Platformun satır içindeki kaydırma kaymasını da ayrıca tuttuğu bu derste ölçülmedi.

Anahtar Kimliktir

Ağaçtan düşen ve yeniden giren bir satırın durumu, hangi satır olduğuna göre korunmalıdır. items çağrısına verilen key, satırın kimliğini belirler. Anahtar verilmezse platform kimliği satırın konumundan türetir; listenin başına yeni bir ögenin eklenmesi her satırın konumunu bir kaydırır ve konuma bağlı durumlar yanlış satıra geçer. Anahtar verilirse durum ögeyle birlikte hareket eder. Anahtar, listede benzersiz olmalıdır.

Bu bölüm bir ölçüm içermez. Kimliğin konumdan ayrılması bir yön sorusu değil, bir eşleştirme sorusudur: aynı veri bir satırdan ötekine mi geçti, yoksa aynı satırda mı kaldı? Böyle bir eşleştirme, ağaç ilişkisini değil, düğümün kimliğini ilgilendirir. Bu dersin ekseni olan yön, anahtar değişse de değişmez: satıra veri hâlâ listeden gider.

Ağaçta Olmayan Satır

Kaydırmadan sonra Row0 ağaçta değildir. Listenin elinde Row0’a ait veri hâlâ vardır, çünkü koleksiyon listenin dışında tutulur. Ancak o veriyi alacak düğüm yoktur. Çekirdek, uçlardan birinin ağaçta bulunmadığı her hareketi dış olarak sınıflar.

// Main.kt — gorunen ve gorunmeyen satira ayni veri
fun main() {
    val tree = listTree(visibleRows(1000, 3, 5))
    val moves = listOf(
        Move("veri-satir-3", "LazyList", rowName(3), tree),
        Move("veri-satir-7", "LazyList", rowName(7), tree),
        Move("veri-satir-0", "LazyList", rowName(0), tree),
    )
    for (move in moves) {
        move.describe()
    }
    countDirections(moves)
}
veri-satir-3: kimden=LazyList | kime=Row3 -> asagi
veri-satir-7: kimden=LazyList | kime=Row7 -> asagi
veri-satir-0: kimden=LazyList | kime=Row0 -> dis
asagi: 2
yukari: 0
derin: 0
dis: 1

Aynı kaynaktan çıkan aynı biçimdeki veri, görünen iki satıra aşağı gitti; ağaçta olmayan satıra dış gitti. Yönü değiştiren şey verinin içeriği ya da kaynağı değil, hedefin ağaçta bulunup bulunmamasıdır. Bu yüzden ekranda olmayan bir satırın durumunu okumak ya da ona doğrudan bir değişiklik yazmak mümkün değildir. O satırla ilgili her şey, satır ağaca yeniden girdiğinde, listenin elindeki koleksiyondan yeniden verilir.

AD3. Model ağaçtan düşen satırın durumunun ne olduğunu kurmaz. Satırın kendi hatırladığı değerlerin ağaçtan düşünce kaybolup kaybolmadığı ve kaydedilebilir durumun bunu nasıl değiştirdiği bu derste ölçülmedi.

Sınırlayıcı Ölçüm

Dersin yönünü, listenin iki ucundaki verilerden hangisinin ders konusuna özgü olduğu belirler. Bir öge verisinin görünen satıra gitmesi, önceki derslerdeki sütun çocuğuyla aynıdır: aşağı. Tembel listeye özgü olan iki şeydir: ağaçta olmayan satıra giden verinin dış kalması ve kaydırma konumunun sahibine çıkması. Aşağıdaki ölçüm bunları bir araya getirir.

// Main.kt — listenin dort hareketinin yon dokumu
fun main() {
    val rows = visibleRows(1000, 3, 5)
    val tree = listTree(rows)
    val moves = listOf(
        Move("veri-ilk-satir", "LazyList", rows.first(), tree),
        Move("veri-son-satir", "LazyList", rows.last(), tree),
        Move("veri-gorunmeyen", "LazyList", rowName(0), tree),
        Move("kaydirma-konumu", "LazyList", scrollOwner(tree), tree),
    )
    for (move in moves) {
        move.describe()
    }
    countDirections(moves)
}
veri-ilk-satir: kimden=LazyList | kime=Row3 -> asagi
veri-son-satir: kimden=LazyList | kime=Row7 -> asagi
veri-gorunmeyen: kimden=LazyList | kime=Row0 -> dis
kaydirma-konumu: kimden=LazyList | kime=Screen -> yukari
asagi: 2
yukari: 1
derin: 0
dis: 1

İki aşağı, bir yukarı, bir dış. Aşağı giden iki hareket, ebeveynin çocuğuna verdiği olağan veridir. Dış hareket, listenin tembel olmasının bedelidir. Yukarı hareket ise tembel listenin ekran için taşıdığı bilgidir: konum. Satırlara veri vermek için liste zaten çalışır; ekranın listeyi izleyebilmesi içinse konumun yukarı çıkması gerekir. Dersin yönü: yukarı.

AD4. Model dört hareketi tek bir kaydırma konumunda kurar. Konum değiştikçe hareket sayıları da değişir; bu ölçüm yalnız firstVisible 3 iken geçerlidir.

Özet

  • Bu dersin yönü: yukarı. Tembel listeye özgü hareket, kaydırma konumunun listeden durumun sahibine çıkmasıdır.
  • Tembel liste yalnız görünen aralıktaki satırlar için düğüm oluşturur; ağaçtaki satır sayısı toplam sayıdan küçüktür.
  • Listenin görünen satıra verdiği veri aşağı gider.
  • Kaydırma konumu listeden durumun sahibine, yani ataya gider: yukarı.
  • Ağaçta olmayan satıra giden veri dıştır; satır ağaca girene kadar bekler.

Sonraki Adım

Tembel liste, satırlarını kendisi oluşturur ama satırın içeriğini bileşen işlevi belirler. Hazır arayüz bileşenleri bu içeriği önceden kurulmuş parçalarla sağlar; ve bu parçalar içeriklerine kendi renklerini verir. Sonraki ders, düğmenin içeriğine verdiği rengin yönünü ölçer.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ ↵ Esc gezin · aç · kapat