ConstraintLayout'un ne olduğunu öğreniyoruz — iç içe LinearLayout ve RelativeLayout yerine kısıtlamalar (constraints) kullanarak düz görünüm hiyerarşileri oluşturmayı sağlayan, Android için esnek bir konumlandırma sistemi. ConstraintLayout, "düzen iç içe geçme cehennemi" (layout nesting hell) sorununu çözerek hiyerarşi derinliğini tek bir seviyeye indirir ve ekran oluşturmayı hızlandırır. Kütüphane Jetpack'in bir parçasıdır ve Android 2.3'ten (API 9) itibaren support-library aracılığıyla kullanılabilir. Temel mekanizmalar resmi Android dokümantasyonunda açıklanmıştır.
Anahtar Noktalar
ConstraintLayout, AndroidX ConstraintLayout kütüphanesinden bir ViewGroup'tur ve bildirimsel kısıtlamalar aracılığıyla esnek ve yüksek performanslı arayüzler oluşturmak için tasarlanmıştır. Öğeleri bir satırda düzenleyen LinearLayout veya öğeleri komşularına göre konumlandıran RelativeLayout'un aksine, ConstraintLayout her öğeyi diğer öğelere ve üst öğeye aynı anda sabitlemeyi sağlar.
Kütüphane, Google I/O 2016'da karmaşık ekranların oluşturulmasını hızlandırmak için bir çözüm olarak duyuruldu. ConstraintLayout'un çözdüğü temel sorun düzen iç içe geçmesidir. Her iç içe ViewGroup en az iki measure geçişi ve bir layout geçişi ekler. 4 iç içe geçme seviyesine sahip bir ekran 8 measure geçişi gerçekleştirir; aynı işlevselliğe sahip ConstraintLayout yalnızca 2 geçiş gerçekleştirir. Google'a (Android Performance Blog, 2017) göre, üç iç içe LinearLayout'u bir ConstraintLayout ile değiştirmek, onMeasure süresini %40 azaltır.
Mevcut sürüm ConstraintLayout 2.1.4, Android 2.3+ (API 9) üzerinde AndroidX aracılığıyla istikrarlı bir şekilde çalışır. Sürüm 2.0, dairesel konumlandırma, Flow (otomatik öğe sarma) ve MotionLayout desteğini getirdi. ConstraintLayout, modern Android geliştirmeyi anlamak için gereklidir — Jetpack Compose'da değiştiricilerin temel konsepti olarak, Android Studio varsayılan şablonlarında ve Material Design 3'te kullanılır.
ConstraintLayout'un düz hiyerarşisi, tüm alt görünümlerin aynı iç içe geçme seviyesinde olduğu anlamına gelir. A öğesini bir LinearLayout'a ve LinearLayout'u bir RelativeLayout'a yerleştirmek yerine, tüm öğeler doğrudan üst ConstraintLayout'a veya nitelikler aracılığıyla birbirine bağlanır. Bu şunları sağlar: daha düşük bellek tüketimi (her ViewGroup Java yığınında bir nesnedir), daha hızlı layout geçişi (daha az özyinelemeli çağrı) ve ekran boyutları değiştiğinde daha öngörülebilir davranış.
Kısıtlama (constraint), bir View'in kenarı (veya merkezi) ile başka bir View'in veya üst öğenin kenarı arasındaki bağlantıdır. Her View'in en fazla 8 kısıtlaması olabilir: left, top, right, bottom, start, end, baseline ve center. Konumlandırma için en az iki dik kısıtlama yeterlidir (örneğin, top + left).
Nitelik biçimi: app:layout_constraint[Source]_to[Target]Of="[id]" — burada Source bağlı kenardır (Left, Right, Top, Bottom, Start, End, Baseline) ve Target hedef kenardır. Örnek: app:layout_constraintTop_toBottomOf="@+id/header" "geçerli öğenin üst kenarının header öğesinin alt kenarına bağlı olduğu" anlamına gelir. Üst öğeye bağlanmak için parent kimliği kullanılır.
Bias (eğim), zıt kısıtlamalar (left + right veya top + bottom) mevcut olduğunda çalışan bir parametredir. Değerler 0 ile 1 arasında değişir: 0 — sol/üst kenara bastırılmış, 0.5 — ortalanmış, 1 — sağ/alt kenara. Nitelikler: layout_constraintHorizontal_bias (0.0–1.0) ve layout_constraintVertical_bias. Kenar boşlukları standart android:layout_margin* ile ayarlanır, ancak kısıtlamalar ve kenar boşlukları bağımsız çalışır: kenar boşluğu, kısıtlamadan olan uzaklıktır, komşu View'den değil.
ConstraintLayout 1.1+'dan itibaren, layout_constraintWidth_percent ve layout_constraintHeight_percent aracılığıyla yüzde tabanlı boyutlandırma desteği eklendi. 0.3 değeri, üst öğenin genişliğinin/yüksekliğinin %30'u anlamına gelir. Bias ile birleştirildiğinde, bu programlama olmadan uyarlanabilir düzenler oluşturmayı sağlar.
Chain (zincir), çift yönlü kısıtlamalarla (A, B'ye bağlı, B, A'ya bağlı) bağlanan iki veya daha fazla View grubudur. Zincirler, alanı öğeler arasında modlardan birine göre otomatik olarak dağıtır: spread (eşit şekilde, kenar boşlukları dikkate alınarak), spread_inside (eşit şekilde, dış öğeler kenar boşluğu olmadan), packed (öğeler ortak bir bias ile birbirine bastırılmış). Mod, app:layout_constraintHorizontal_chainStyle veya layout_constraintVertical_chainStyle niteliği aracılığıyla ayarlanır.
Guideline (kılavuz çizgisi), çalışma zamanında görünmeyen, bağlantı için bir çizgi tanımlayan yardımcı bir View'dir. Bir Guideline yatay veya dikey olabilir, dp, yüzde (app:layout_constraintGuide_percent) veya kenardan uzaklık (app:layout_constraintGuide_begin/end) ile konumlandırılabilir. Guideline'lar uyarlanabilir düzenler için vazgeçilmezdir — örneğin, cihaz boyutundan bağımsız olarak ekranı iki eşit yarıya bölmek için.
Google I/O 2017'ye göre, spread_inside ile zincirler, ağırlık hesaplaması için gereken çift measure geçişinden kaçındıkları için ağırlıklı iç içe LinearLayout'lardan %15–20 daha verimlidir.
Barrier (bariyer), bir öğe grubunun boyutuna göre konumunu dinamik olarak ayarlayan sanal bir View'dir. Sabit konumlu bir Guideline'ın aksine, bir Barrier gruptaki en geniş öğe tarafından "itilir". Örneğin, bilinmeyen uzunluklarda bir başlık ve açıklamanız varsa, en geniş metnin sağ kenarına bağlı bir Barrier, hemen sonrasına bir simge yerleştirmenizi sağlar. Nitelikler: app:barrierDirection (left, right, top, bottom, start, end) ve app:constraint_referenced_ids (virgülle ayrılmış kimlik listesi).
Group, birden çok View'in görünürlüğünü aynı anda yöneten sanal bir kapsayıcıdır. Her bir öğe için ayrı ayrı setVisibility çağırmak yerine, tek bir Group'un görünürlüğünü değiştirmek yeterlidir. Group konumlandırmayı etkilemez — yalnızca görünürlüğü etkiler. Flow, "akışkan" düzenler oluşturmak için sanal bir yardımcıdır: alan tükendiğinde öğeler, bir paragraftaki metin gibi otomatik olarak yeni bir satıra/sütuna sarılır. Flow, wrapMode: none, chain ve aligned'i destekler.
Bu araçlara (Barrier, Group, Flow, Guideline) sanal yardımcılar denir çünkü klasik anlamda View değildirler — hiyerarşide yer kaplamazlar ve odak veya dokunma olaylarına katılmazlar. Amaçları, iç içe kapsayıcılar eklemeden karmaşık düzenlerin bakımını basitleştirmektir.
E-posta alanı, şifre alanı ve bir düğmeden oluşan basit bir giriş formu. Düğme dışındaki tüm öğeler üst öğeye bağlıdır — düğme şifre alanının altındadır. Düz hiyerarşi kullanılır — üç öğe de aynı seviyededir.
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/email_input"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="32dp"
android:layout_marginHorizontal="16dp" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/password_input"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/email_input"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="16dp"
android:layout_marginHorizontal="16dp" />
<Button
android:id="@+id/login_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/password_input"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="24dp"
android:layout_marginHorizontal="16dp"
android:text="Giriş yap" />
</androidx.constraintlayout.widget.ConstraintLayout>
Tüm öğelerin genişliği 0dp (match_constraint)'dir, yani yatay kenar boşlukları dikkate alınarak start kısıtlamasından end kısıtlamasına kadar uzanırlar. Bu, kenar boşluklarıyla match_parent'a eşdeğerdir, ancak iç içe geçme olmadan.
Kenar boşluklarıyla yatay olarak eşit şekilde dağıtılmış üç düğme. spread_inside zinciri, dış düğmeleri kenarlara ve ortadaki düğmeyi aralarında ortalanmış şekilde yerleştirir.
<Button
android:id="@+id/btn_left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/btn_center"
android:text="Sol" />
<Button
android:id="@+id/btn_center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toRightOf="@+id/btn_left"
app:layout_constraintRight_toLeftOf="@+id/btn_right"
android:text="Merkez" />
<Button
android:id="@+id/btn_right"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toRightOf="@+id/btn_center"
app:layout_constraintRight_toRightOf="parent"
android:text="Sağ" />
Öğeler çift yönlü kısıtlamalara sahip olduğunda zincir otomatik olarak oluşturulur. spread_inside modu, app:layout_constraintHorizontal_chainStyle="spread_inside" aracılığıyla zincirin herhangi bir öğesinde ayarlanır. Bu, weightSum ve layout_weight ile LinearLayout ihtiyacını ortadan kaldırır.
%50'de dikey bir Guideline kullanarak iki eşit sütun oluşturma. Sol öğe, sol üst öğeye ve sağ kenarı guideline'a bağlıdır; sağ öğe, sol kenarı guideline'a ve sağ üst öğeye bağlıdır.
<androidx.constraintlayout.widget.Guideline
android:id="@+id/gl_midpoint"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />
<TextView
android:id="@+id/left_card"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/gl_midpoint"
android:layout_margin="8dp"
android:background="@color/card_background" />
<TextView
android:id="@+id/right_card"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@+id/gl_midpoint"
app:layout_constraintRight_toRightOf="parent"
android:layout_margin="8dp"
android:background="@color/card_background" />
Yüzdesi 0.5 olan bir Guideline otomatik olarak ekran genişliğine uyum sağlar. Hem tablette hem de telefonda sütun oranı 50/50 kalır. Sol/sağ adlandırması için RTL uyumluluğu için start/end niteliklerini kullanın.
Android geliştirme için üç ana ViewGroup'un karşılaştırma tablosu: ConstraintLayout, LinearLayout ve RelativeLayout. Kriterler: esneklik, performans, kod karmaşıklığı ve kullanım durumları.
| Özellik | ConstraintLayout | LinearLayout | RelativeLayout |
|---|---|---|---|
| İç içe geçme | Düz (tek seviye) | Karmaşık düzenler için iç içe geçme gerektirir | Tek seviye, ancak sınırlı esneklik |
| Measure performansı | 2 geçiş (~%40 daha hızlı) | Weight ile 4+ geçiş | 2 geçiş |
| Yüzde boyutlar | Evet (guide_percent, width_percent) | Sadece weight/frame ile | Hayır |
| RTL desteği | Yerleşik (start/end) | Yerleşik | start/end ile (API 17+) |
| Barrier/Group/Flow | Evet (sanal yardımcılar) | Hayır | Hayır |
| MotionLayout animasyonları | Evet | Hayır | Hayır |
| Ne zaman kullanılır | Tüm karmaşık düzenler, >5 öğeli ekranlar | Basit tek yönlü listeler, düğmeli satırlar | Basit göreli düzenler (eski kod) |
Android Vitals'a (Google, 2025) göre, ConstraintLayout'u ana kapsayıcı olarak kullanan uygulamalar, iç içe LinearLayout kullanan uygulamalara kıyasla karmaşık ekranları oluştururken ortalama %18 daha az takılma karesi gösterir. IT Sectr'da, 2018'de tüm XML düzenleri için standart olarak ConstraintLayout'a geçtik — bu, ortalama ekran hiyerarşi derinliğini 4,2'den 1,8 seviyesine düşürdü ve yeni formların geliştirilmesini %25 hızlandırdı.
Sıkça Sorulan Sorular
match_parent ConstraintLayout'da her zamanki gibi çalışır — View'i üst öğenin boyutuna kadar uzatır. 0dp (match_constraint), View'in boyutunun kısıtlamalardan hesaplandığı anlamına gelir: kenar boşluklarıyla left ve right kısıtlamaları ayarlanmışsa, genişlik = parent — marginLeft — marginRight. Davranıştaki fark: match_parent bias'ı yok sayar ve animasyon sırasında taşabilir; match_constraint tüm kısıtlamalara doğru şekilde uyar ve Google tarafından ConstraintLayout için birincil mod olarak önerilir.
Bir kombinasyon kullanın: ekranın bir kısmını kaplaması gereken öğeler için yüzde boyutları (layout_constraintWidth_percent); ekranı bölgelere ayırmak için yüzdeli Guideline; dinamik içeriğe göre konumlandırma için Barrier; kartları yeni bir satıra sarmak için wrapMode ile Flow. Alternatif bir yaklaşım, tabletlerde master-detail arayüzleri için ConstraintLayout ile birlikte SlidingPaneLayout kullanmaktır.
Jetpack Compose, ConstraintLayout'u bir ViewGroup olarak kullanmaz, ancak Kotlin DSL'de aynı API ile ConstraintLayout'un compose sürümünü (androidx.constraintlayout:constraintlayout-compose) sağlar: createRefFor(), constrainAs(), linkTo(), chain(), guideFrom(). Bu, Column/Row'dan ziyade kısıtlamalar yoluyla tanımlanması daha kolay olan karmaşık düzenler için kullanışlıdır. Ancak, Compose'da Column/Row/Box ile başlamanız ve yalnızca karmaşık göreli konumlandırma gerektiğinde ConstraintLayout'a geçmeniz önerilir.
Android Studio'da Layout Inspector'ı (Tools → Layout Inspector) açın, çalışan uygulamayı seçin ve sorunlu öğenin üzerine gelin. Tüm kısıtlamaları, kenar boşluklarını, dolguyu ve bias'ı 3B bir gösterimde göreceksiniz. XML için düzen düzenleyicisindeki Design panelini kullanın — kısıtlama çakışmalarını sarı, eksik kısıtlamaları kırmızı ile vurgular. Kodda, her View'in iki dik kısıtlaması olduğundan emin olun, aksi takdirde öğe (0,0) konumunda olur.
Özet
Anahtar teslim bir mobil uygulama geliştireceğiz
IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.
Ayrıca okuyun