---
title: 'Kısıt Tabanlı Düzen'
source: 'https://academia.sh/tr/kurslar/compose/kisit-tabanli-duzen'
course: 'Jetpack Compose ile Arayüz'
language: tr
updated: '2026-10-21T05:02:38+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Kısıt tabanlı düzende bir ögenin konumunun kardeşine bağlı olduğunu açıklar'
    - 'Kardeşin konumunun önce düzen ebeveynine çıktığını, sonra çocuğa indiğini ölçer'
    - 'Kardeşten kardeşe doğrudan hareketin ağaçta kurulamadığını gösterir'
    - 'Kılavuz çizgisinin bu ilişkide bir kardeş gibi davrandığını ayırt eder'
---

# Kısıt Tabanlı Düzen

Kardeşe bağlanan ögelerin konumunun ebeveyn üzerinden hesaplandığının ve kardeşten kardeşe doğrudan hareketin kurulamadığının ölçümü.

Önceki derste sütunun çocukları kaynakta yazıldığı sırayla dizildi. Her çocuk konumunu yalnız ebeveyninden aldı; kardeşlerinin nerede olduğunu bilmesi gerekmedi. Ama bazı arayüzlerde bir ögenin yeri, başka bir ögenin yerine bağlıdır: ad, simgenin sağına, rozet adın altına gelir. Bu bağlılığı iç içe sütun ve satırlarla kurmak mümkündür, ama her bağlılık bir düzey ekler. Kısıt tabanlı düzen (`ConstraintLayout`) bunu tek düzeyde çözer: ögeler birbirine, ebeveyne ve kılavuz çizgilerine bağlanır.

Bu ders, bir kardeşe bağlanmanın arayüz ağacında ne anlama geldiğini ölçer. Kardeşler aynı ebeveynin çocuklarıdır ve ağaçta aralarında bir kenar yoktur. Bir ögenin konumu başka bir ögeye bağlıysa, bu bilginin hangi yoldan gittiği sorusu ortaya çıkar.

```kotlin
// 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 }}")
    }
}
```

## Ögeleri Birbirine Bağlamak

Bir kısıt tabanlı düzende ögeler önce referans olarak oluşturulur, sonra her biri kenarlarını başka bir referansın kenarına bağlar. Yatay eksende bir ögenin başlangıcı, bir başkasının bitişine; dikey eksende üstü, bir başkasının altına bağlanabilir. Aralarına boşluk konabilir. Kılavuz çizgisi ise ekranda görünmeyen bir yardımcıdır: ebeveynin genişliğinin bir oranında durur ve ögeler ona bağlanabilir.

```kotlin
// Platform kodu: kardese baglanan ogeler ve kilavuz cizgisi
import androidx.constraintlayout.compose.ConstraintLayout

@Composable
fun ProfileRow() {
    ConstraintLayout(Modifier.fillMaxWidth()) {
        val (avatar, name, badge) = createRefs()
        val guide = createGuidelineFromStart(0.3f)
        Box(Modifier.size(48.dp).constrainAs(avatar) {
            start.linkTo(parent.start)
            top.linkTo(parent.top)
        })
        Text("Ada", Modifier.constrainAs(name) {
            start.linkTo(avatar.end, margin = 8.dp)
            top.linkTo(parent.top)
        })
        Text("New", Modifier.constrainAs(badge) {
            start.linkTo(guide)
            top.linkTo(name.bottom)
        })
    }
}
```

Bu parçada üç ögenin üçü de `ConstraintLayout`'un çocuğudur; `name`, `avatar`'ın bitişine, `badge` ise `name`'in altına ve kılavuz çizgisine bağlanır. Yazım, bağlılığı ögenin kendi lambdasında tutar: `name`, `avatar`'ı kendi kenarı üzerinden okur. Ağaçta ise `name` ile `avatar` kardeştir; kaynakta görünen bağ, ağaçta bir kenar değildir.

Model, platformun bu kuralını kurar. Model, platformun kendisi değildir. Yardımcı dosya, ögeleri bağımlılık sırasıyla alır: bir ögenin başlangıcı, bağlandığı ögenin başlangıcı, genişliği ve aradaki boşluğun toplamıdır; hiçbir şeye bağlanmayan ögenin başlangıcı yalnız boşluktur. Bağımlılık sırası ögelerin verilişinden gelir; platformun bağımlılık grafiğini çözmesi modelde yoktur.

```kotlin
// Anchors.kt — kardese baglanan ogelerin konumu (modelin hesabi)
class Item(val name: String, val width: Int, val anchor: String?, val gap: Int)
class Placement(val name: String, val x: Int, val anchor: String?)

fun solveAnchors(items: List<Item>): List<Placement> {
    val placed = mutableMapOf<String, Int>()
    val widths = items.associate { it.name to it.width }
    return items.map { item ->
        val anchor = item.anchor
        val x = if (anchor == null) item.gap else placed.getValue(anchor) + widths.getValue(anchor) + item.gap
        placed[item.name] = x
        Placement(item.name, x, anchor)
    }
}
```

## Kardeşin Konumu Önce Yukarı Çıkar

Bir kardeşe bağlanan ögenin konumu iki adımda oluşur. Önce kardeşin konumu düzen ebeveynine bildirilir: düzen, hangi ögenin nerede olduğunu bilmek zorundadır, çünkü yerleştirme adımı onundur. Sonra ebeveyn, bağlanan ögeye onun yeni konumunu verir. İlk hareket çocuktan ebeveyne, yani yukarıdır; ikincisi ebeveynden çocuğa, yani aşağıdır.

Ağaç `Screen`'in altında `ConstraintLayout`, onun altında üç öge kurar: `Avatar`, `Name`, `Badge`. İlk ölçüm, ilk iki ögeyi yerleştirir. `Avatar` ebeveyne bağlıdır; `Name`, `Avatar`'a bağlanır.

```kotlin
// Main.kt — kardesin konumu ebeveyne cikar, ebeveynden iner
fun main() {
    val tree = Tree(mapOf(
        "Screen" to "App", "ConstraintLayout" to "Screen",
        "Avatar" to "ConstraintLayout", "Name" to "ConstraintLayout", "Badge" to "ConstraintLayout",
    ))
    val placements = solveAnchors(listOf(Item("Avatar", 48, null, 0), Item("Name", 120, "Avatar", 8)))
    val first = placements[0]
    val second = placements[1]
    println("${first.name}: x=${first.x}")
    println("${second.name}: x=${second.x}")
    val moves = listOf(
        Move("konum-bildir-avatar", first.name, "ConstraintLayout", tree),
        Move("konum-ver-ad", "ConstraintLayout", second.name, tree),
    )
    for (move in moves) {
        move.describe()
    }
    countDirections(moves)
}
```

```text
Avatar: x=0
Name: x=56
konum-bildir-avatar: kimden=Avatar | kime=ConstraintLayout -> yukari
konum-ver-ad: kimden=ConstraintLayout | kime=Name -> asagi
asagi: 1
yukari: 1
derin: 0
dis: 0
```

`Name`, `x=56` konumunu aldı: `Avatar`'ın genişliği 48 ve aradaki boşluk 8'dir. Bu bilgi `Avatar`'dan `Name`'e doğrudan gitmedi. Önce `Avatar` kendi konumunu düzene bildirdi, yukarı; düzen bunu kullanıp `Name`'in konumunu hesapladı ve ona verdi, aşağı. Kaynakta tek satırlık bir bağ gibi görünen şey, ağaçta iki hareketlik bir yoldur.

**AD1.** Model ögeleri bağımlılık sırasıyla alır ve yalnız yatay eksende yerleştirir. Dikey bağlar, birden çok ögeye aynı anda bağlanma ve bağlılıkların döngü kurduğu durum ölçülmedi.

## Zincir Boyunca İki Hareketli Adımlar

Zincir uzadığında her bağ aynı iki hareketi tekrar eder. `Badge`, `Name`'e bağlanır; `Name`'in konumu önce düzene çıkar, sonra `Badge`'e iner. Bu, üç ögeli bir zincirde `Name`'in iki farklı rol oynadığı anlamına gelir: `Avatar`'a göre bağlanan, `Badge` tarafından okunan öge.

```kotlin
// Main.kt — uc ogelik zincirin hareketleri
fun main() {
    val tree = Tree(mapOf(
        "Screen" to "App", "ConstraintLayout" to "Screen",
        "Avatar" to "ConstraintLayout", "Name" to "ConstraintLayout", "Badge" to "ConstraintLayout",
    ))
    val placements = solveAnchors(listOf(
        Item("Avatar", 48, null, 0), Item("Name", 120, "Avatar", 8), Item("Badge", 32, "Name", 8),
    ))
    val moves = mutableListOf<Move>()
    for (p in placements) {
        println("${p.name}: x=${p.x}")
        val anchor = p.anchor ?: continue
        moves.add(Move("konum-bildir-${anchor.lowercase()}", anchor, "ConstraintLayout", tree))
        moves.add(Move("konum-ver-${p.name.lowercase()}", "ConstraintLayout", p.name, tree))
    }
    for (move in moves) {
        move.describe()
    }
    countDirections(moves)
}
```

```text
Avatar: x=0
Name: x=56
Badge: x=184
konum-bildir-avatar: kimden=Avatar | kime=ConstraintLayout -> yukari
konum-ver-name: kimden=ConstraintLayout | kime=Name -> asagi
konum-bildir-name: kimden=Name | kime=ConstraintLayout -> yukari
konum-ver-badge: kimden=ConstraintLayout | kime=Badge -> asagi
asagi: 2
yukari: 2
derin: 0
dis: 0
```

Üç öge, iki bağ ve dört hareket: iki yukarı, iki aşağı. `Badge` `x=184` konumunu aldı: `Name`'in başlangıcı 56, genişliği 120 ve boşluk 8'dir. Yukarı hareketlerin sayısı, bağların sayısına eşittir; her bağ, bağlanılan kardeşin konumunu bir kez ebeveyne çıkarır.

**AD2.** Model bağın bağlandığı ögenin sağ kenarını kullanır. Kılavuz çizgisine bağlanma ayrı bir ilişkidir ve ölçülmedi.

## Kılavuz Çizgisi Bir Kardeştir

Kılavuz çizgisi, ögelerle aynı düzeyde duran ve görünmeyen bir yardımcıdır. Ögeler ona bağlanırken konum bilgisi yine ebeveyn üzerinden akar: çizginin konumu ebeveynin genişliğinin oranından hesaplanır ve çizgiye bağlanan ögeye düzen verir. Kılavuz çizgisi bu açıdan bir kardeşle aynıdır: ağaçta ögelerin yanındadır ve onlarla arasında doğrudan bir kenar yoktur. Bu derste ölçülen model çizgiyi ayrı bir öge olarak kurmadı; bu cümle platform davranışının anlatımıdır, ölçüm sonucu değil.

## Kardeşten Kardeşe Kurulmaz

Kaynak kodda `name`'in `avatar`'a doğrudan bağlandığı satırı yazmak kolaydır. Ağaçta ise iki kardeşin birbirine veri göndermesi, ortak ataya çıkmadan ve ondan inmeden mümkün değildir. Çekirdek bu durumu bir yön olarak değil, hata olarak sınıflar: ne aşağı, ne yukarı, ne derin, ne de dış. Hareketi kurmaya çalışan kod `akrabasiz` hatasıyla durur.

```kotlin
// Main.kt — kardesler arasi dogrudan hareket reddedilir
fun main() {
    val tree = Tree(mapOf(
        "Screen" to "App", "ConstraintLayout" to "Screen",
        "Avatar" to "ConstraintLayout", "Name" to "ConstraintLayout",
    ))
    val direct = runCatching { Move("dogrudan", "Avatar", "Name", tree) }
    println("kuruldu=${direct.isSuccess}")
    println(direct.exceptionOrNull()?.message)
    val viaParent = listOf(
        Move("yukari-yol", "Avatar", "ConstraintLayout", tree),
        Move("asagi-yol", "ConstraintLayout", "Name", tree),
    )
    for (move in viaParent) {
        move.describe()
    }
}
```

```text
kuruldu=false
akrabasiz: Avatar ve Name
yukari-yol: kimden=Avatar | kime=ConstraintLayout -> yukari
asagi-yol: kimden=ConstraintLayout | kime=Name -> asagi
```

`yukari-yol` ve `asagi-yol` satırlarının ikişer ucu elle yazılmıştır: bu satırlar kuralı göstermek için yazılmıştır, ölçüm değildir. Kardeş kısıtının iki hareket olduğu bulgusu, uçları modelin yerleştirdiği düğümlerden gelen sınırlayıcı ölçüme dayanır.

Doğrudan hareket kurulamadı ve çekirdeğin kendi mesajı `akrabasiz: Avatar ve Name` basıldı. Aynı bilgi ebeveyn üzerinden iki hareketle taşındı: yukarı ve aşağı. Ağaçta bir kardeş kısıtı, bu iki hareketin toplamıdır.

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

Dersin yönü, kardeş kısıtını mümkün kılan hareketten seçilir. Aşağı yöndeki kısıt ve konum hareketleri önceki derste ölçüldü; bu derste yeni olan, bağlanılan kardeşin konumunun ebeveyne çıkmasıdır. Aşağıdaki ölçüm bir bağı hareketlerine ayırır ve doğrudan yolun yerine konan iki hareketi sayar.

```kotlin
// Main.kt — bir kardes kisitinin hareketleri
fun main() {
    val tree = Tree(mapOf(
        "Screen" to "App", "ConstraintLayout" to "Screen",
        "Avatar" to "ConstraintLayout", "Name" to "ConstraintLayout",
    ))
    val placements = solveAnchors(listOf(Item("Avatar", 48, null, 0), Item("Name", 120, "Avatar", 8)))
    val moves = listOf(
        Move("konum-bildir", placements[0].name, "ConstraintLayout", tree),
        Move("konum-ver", "ConstraintLayout", placements[1].name, tree),
    )
    for (move in moves) {
        move.describe()
    }
    countDirections(moves)
}
```

```text
konum-bildir: kimden=Avatar | kime=ConstraintLayout -> yukari
konum-ver: kimden=ConstraintLayout | kime=Name -> asagi
asagi: 1
yukari: 1
derin: 0
dis: 0
```

Bir kardeş kısıtı iki harekettir: biri yukarı, biri aşağı. Aşağı hareket, her ebeveyn çocuk ilişkisinde zaten bulunan konum verişidir. Kısıt tabanlı düzene özgü olan yukarı harekettir, çünkü bu düzenin kardeşe bağlanma yeteneği ancak bu hareketle çalışır. Dersin yönü: **yukarı**.

**AD3.** Model bir bağı iki harekete ayırır. Platformun bu iki adımı gerçekte tek bir çözüm geçişinde birleştirip birleştirmediği ölçülmedi.

## Özet

- Bu dersin yönü: yukarı. Dersin yeni hareketi, bağlanılan kardeşin konumunun ebeveyne çıkmasıdır.
- Kısıt tabanlı düzende bir ögenin konumu başka bir ögeye bağlanabilir; ağaçta bu iki ögenin kardeş olması demektir.
- Kardeşten kardeşe doğrudan hareket kurulamaz; çekirdek bunu `akrabasiz` hatasıyla reddeder.
- Bir kardeş kısıtı iki harekettir: kardeşin konumu ebeveyne yukarı, ebeveynden bağlanan ögeye aşağı.
- Kılavuz çizgisi ögelerle aynı düzeyde durur ve konum bilgisi onun için de ebeveynden akar.

## Sonraki Adım

Kısıt tabanlı düzen, ögelerin hepsinin bir arada görünür olduğunu varsayar. Binlerce ögenin yer aldığı bir listede bu varsayım bozulur: ekranda olmayan ögeler için ağaçta düğüm de olmaz. Sonraki ders, yalnız görünen ögelerin oluşturulduğu tembel listeyi ve kaydırma konumunun sahibine çıkışını ölçer.
