Jetpack Compose Giriş

Halil ÖZCAN
5 min readOct 29, 2019

XML sevdiğimiz bir abimizdi. Janti adamdı. Activity ile bağlanırken çok crash ederdi. Mesela ben etmem. Activity, abi bana şu öğeyi verir misin dediğinde dalga geçerdi. Ben geçmem… (JetpackCompose Dayı — Jilet XML)

Jetpack Compose XML’i ortadan kaldırıp daha az kod ile kullanıcı arayüzü geliştirme hızını arttırmak ve bu işlemi basitleştirmek amacıyla geliştiricilere sunulan bir araçtır.

Jetpack Compose ile geliştirme yapabilmek için Android Studio nun Preview sürümünü indirmeniz gereklidir.

Buradan Android Studio’nun Preview sürümünü indirebilirsiniz. Bu sürümü indirdikten sonra yeni proje oluştururken Activity tipi seçme ekranında Empty Compose Activity i seçerek geliştirme yapmaya başlayabilirsiniz.

Jetpack Compose Android 5.0(API 21) sürümüne kadar desteklenmektedir ancak şu anda Developer Preview da olduğu için Production da kullanılmaması tavsiye edilmektedir.

Goygoyumuzu yaptığımıza ve kısaca JetpackCompose uda tanıttığımıza göre artık kod yazarak Jetpack Compose un derinliklerine girelim.

JetpackCompose ile kullanıcı arayüzünde bir şeyler oluşturma işlemi composable fonksiyonlar aracılığıyla yapılır. Composable fonksiyonlar @Composable notasyonu ile belirtilir. Composable fonksiyonlar sadece Composable fonksiyonlar içerisinden çağrılabilirler.

class TutorialActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Text(text = "Hello Android!")
}
}
}

Buradaki Text() ekrana text yazdırılmasını sağlayan composable bir fonksiyondur. Normal kullanıcı arayüzü geliştirmedeki TextView’a benzetilebilir. setContent() Activity’e yazılmış bir extension fonksiyondur. Parametre olarak composable bir fonksiyon alır. Kendisine parametre olarak gelen composable fonksiyon Activity nin content’i olur.

Yukarıdaki gibi bir kodlama yapmak aslında yanlıştır. Kullanıcı arayüzünde gösterilecek ortak işlemi yapan her yapının ayrı bir fonksiyona alınması gereklidir. Jetpack Compose un en önemli detaylarından bir tanesi de budur. Eğer böyle yapılmazsa çok fazla nested yapı oluşabilir. Bu da “Clean Code” un tersine bir örnek olur.

Burada bir composable fonksiyon oluşturup, ekrana “Hello Android” yazdıran Text() fonksiyonunu bu fonksiyonun içine almak gerekir. Daha sonra bu fonksiyon setContent() fonksiyonu içerisinden çağırılır.

class TutorialActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
SayHello("Hello Android!")
}
}
}

@Composable
fun SayHello(message: String) {
Text(text = message)
}

Yukarıda gösterilen kod parçası bir önceki ekran çıktısı ile aynı çıktıyı verir. Tek fark; kodun daha düzgün hale getirilmesi olmuştur.

Şimdi ilk Composable fonksiyonumuzu oluşturduk ve Activity içerisinden çağırdık. Peki ekrana birden fazla Text yazdırmak istedik diyelim. Aşağıdaki gibi bir kodlama yaparsak nasıl bir çıktı olur inceleyelim.

@Composable
fun SayHello(developerMessage: String, androidMessage:String) {
Text(text = developerMessage)
Text(text = androidMessage)
}

Yukarıdaki gibi bir kodlama yapıldığında overlap (üst üste gelme) oluştuğunu görmüş olduk. Bunun nedeni bir layout (yerleşim) yapısı kullanmamamızdır. XML de hatırlayacağınız üzere ConstraintLayout, LinearLayout ve RelativeLayout gibi layoutlar bulunmaktaydı. JetpackCompose da bunlara benzer yapılara sahiptir. Bu yapıları daha detaylı olarak öğrenmek isterseniz buradan ulaşabilirsiniz.

Şimdi vertical LinearLayout gibi bir yapı oluşturmaya çalışalım ve ekrana yazdıracağımız text ler alt alta olacak şekilde yerleştirelim.

Vertical LinearLayout yapısını kullanmak için Column() fonksiyonu mevcuttur. Column fonksiyonunun içerisine ekrana yazdırılacak textlerin konulmasıyla istenilen yapı oluşturulur.

@Composable
fun SayHello(developerMessage: String, androidMessage:String) {
Column {
Text(text = developerMessage)
Text(text = androidMessage)
}
}

Şimdi biz burada istediğimiz yapıyı oluşturduk. Peki biraz boşluk vermek istersek bunu nasıl yapacağız onu görelim.

@Composable
fun SayHello(developerMessage: String, androidMessage:String) {
Column(modifier = Spacing(all = 16.dp)) {
Text(text = developerMessage)
HeightSpacer(height = 16.dp)
Text(text = androidMessage)
}
}

Composable fonksiyonlar default parametrelere sahiptirler. Bundan dolayı istediğiniz özelliklerini named olarak verebilirsiniz. Modifier isimli parametre, çeşitli biçimlendirme işlemlerini yapmamıza olanak sağlar. Biz burada boşluk vermek istediğimiz için Spacing() olarak tanımladık. dp() Int sınıfına yazılan bir extension fonksiyondur. Column içerisindeki modifier parametresine verilen değer ile taraftan 16 dp lik boşluk verilmiştir. HeightSpacer() ile de 16 dp lik dikey boşluk bırakabilirsiniz. Yazmış olduğumuz kodun çıktısı aşağıdaki gibi olur.

Evet… Boşluklarımızı ayarladık. Şimdi bir tane resim ekleyelim.

@Composable
fun SayHello(developerMessage: String, androidMessage:String) {
val imageResource = +imageResource(R.drawable.android)
Column(
modifier = Spacing(all = 16.dp),
crossAxisSize = LayoutSize.Expand,
mainAxisSize = LayoutSize.Expand) {
DrawImage(image = imageResource)
Text(text = developerMessage)
HeightSpacer(height = 16.dp)
Text(text = androidMessage)
}
}

Resmimizi ekledik ama yine overlap durumunun oluştuğunu gördük. Resim vb. içerikleri ekranda gösterirken overlap i engellemek için küçük bir nüans vardır. Bu nüans; ekrana çizdirilecek resmin bir Container içerisine konulmasıdır.

@Composable
fun SayHello(developerMessage: String, androidMessage:String) {
val imageResource = +imageResource(R.drawable.android)
Column(
modifier = Spacing(all = 16.dp),
crossAxisSize = LayoutSize.Expand,
mainAxisSize = LayoutSize.Expand) {
Container(expanded = true, height = 360.dp) {
DrawImage(image = imageResource)
}
HeightSpacer(height = 16.dp)
Text(text = developerMessage)
HeightSpacer(height = 16.dp)
Text(text = androidMessage)
}
}

Burada ekstra olarak gösterilen crossAxisSize ve mainAxisSize parametreleri kullanılan composable fonksiyona göre değişiklik gösterir. Burada crossAxisSize Column için yatay eksendir. mainAxisSize ise düşey eksendir. Çünkü Column un temel amacı elemanları düşeyde sırasıyla ekrana yazdırmaktır. mainAxis de böylece düşey olur. Örneğin Row() fonksiyonu da Column fonksiyonun tam tersidir böylece de axis ler tam tersi olur. Bu axislerin size ı Expand olarak verildiği için XML deki “match parent” gibi davranır.

Şimdi bu örneğimizi son rötuşlarımızı yapıp bitirelim.

@Composable
fun SayHello(developerMessage: String, androidMessage: String) {
val imageResource = +imageResource(R.drawable.android)
MaterialTheme {
Column(
crossAxisSize = LayoutSize.Expand,
mainAxisSize = LayoutSize.Expand
) {
Container(expanded = true, height = 360.dp) {
DrawImage(image = imageResource)
}
Text(text = developerMessage,
modifier = Spacing(left = 16.dp, top = 16.dp),
style = (+themeTextStyle { h6 }).withOpacity(0.87f)
)
Text(
text = androidMessage,
modifier = Spacing(left = 16.dp, top = 16.dp),
style = (+themeTextStyle { body1 }).withOpacity(0.61f)
)
}
}
}

Jetpack Compose ile Material Theme uygulamak daha kolay hale gelmiştir. Burada kullanılan Material theme de composable bir fonksiyondur. İçinde bulunan her bir widget a Material Theme uygulanmış olur. Burada Text lere opacity ile birlikte yazı tipi verilmiştir.

Material Theme ile birlikte Jetpack Compose’da Material Component lar fazlasıyla yer almaktadır. Örneğin bir ActionBar oluşturmak için hazır component bulunmaktadır. Böylece bu component leri oluşturmak daha kolay gelmiştir.

Material Component lar hakkında detaylı olarak buradan bilgi alabilirsiniz.

Aşağıdaki WhatsApp klon tarzında bir ekran yapmaya çalıştım. Yazdığım kodların açıklamasını yapmayacağım. Dokümantasyonlar ile beraber kod okuması yaparak rahatça neyin, nerede ve ne için kullanıldığını rahatça anlayabileceğinizi düşünüyorum.

Kodun çıktısı

Evet arkadaşlar, elimden geldiğince JetpackCompose’u giriş seviyesinde anlatmaya çalıştım. Gördüğünüz eksikler ve bu eksikler ile alakalı önerileriniz için sosyal medya hesaplarımdan ulaşabilirsiniz.

The more code, the more happiness

--

--