Görünüm'den Compose'a geçiş tamamen kullanıcı arayüzüyle ilgili olsa da güvenli ve artımlı bir taşıma gerçekleştirmek için göz önünde bulundurulması gereken birçok nokta vardır. Bu sayfada, View tabanlı uygulamanızı Compose'a taşırken dikkat etmeniz gereken bazı noktalar yer almaktadır.
Uygulamanızın temasını taşıma
Android uygulamalarında tema oluşturmak için önerilen tasarım sistemi Materyal Tasarım'dır.
Görünüm tabanlı uygulamalar için üç Materyal sürümü vardır:
- AppCompat kitaplığını (ör.
Theme.AppCompat.*
) kullanan Materyal Tasarım 1 - MDC-Android kitaplığının (ör.
Theme.MaterialComponents.*
) kullanıldığı Materyal Tasarım 2 - MDC-Android kitaplığının (ör.
Theme.Material3.*
) kullanıldığı Materyal Tasarım 3
Oluşturma uygulamalarında, Materyal'in iki sürümü vardır:
- Oluşturma Materyali kitaplığının kullanıldığı Materyal Tasarım 2 (ör.
androidx.compose.material.MaterialTheme
) - Compose Material 3 kitaplığını kullanarak Materyal Tasarım 3 (ör.
androidx.compose.material3.MaterialTheme
)
Uygulamanızın tasarım sistemi bunu yapabilecek durumdaysa en son sürümü (Materyal 3) kullanmanızı öneririz. Hem Görünümler hem de Oluşturma için taşıma rehberleri mevcuttur:
- Görünümler'de Materyal 1'den Materyal 2'ye
- Görünümler'de Materyal 2'den Materyal 3'e
- Oluşturma sürecinde Materyal 2'den Materyal 3'e
Compose'da yeni ekranlar oluştururken Material Design'ın hangi sürümünü kullandığınızdan bağımsız olarak, Compose Material kitaplıklarından kullanıcı arayüzü yayınlayan tüm composable'lardan önce MaterialTheme
uyguladığınızdan emin olun. Materyal bileşenler (Button
, Text
vb.) bir MaterialTheme
öğesinin mevcut olmasına bağlıdır ve bu bileşen olmadan davranışları tanımlanamaz.
Tüm Jetpack Compose örneklerinde MaterialTheme
üzerine inşa edilmiş özel bir Oluştur teması kullanılır.
Daha fazla bilgi edinmek için Oluşturma bölümünde sistem tasarlama ve XML temalarını Oluşturma'ya taşıma başlıklı makalelere göz atın.
Navigasyon
Uygulamanızda Gezinme bileşenini kullanıyorsanız daha fazla bilgi için Compose ile Gezinme - Birlikte Çalışabilirlik ve Jetpack Gezinmesini Navigation Compose'a taşıma konularına bakın.
Karma Oluşturma/Görünümler kullanıcı arayüzünüzü test etme
Uygulamanızın bazı bölümlerini Compose'a taşıdıktan sonra herhangi bir şeyi bozmadığınızdan emin olmak için test yapmak çok önemlidir.
Bir etkinlik veya parça, Oluştur işlevini kullandığında ActivityScenarioRule
yerine createAndroidComposeRule
kullanmanız gerekir. createAndroidComposeRule
, Oluşturma ve Kodu görüntüleme özelliklerini aynı anda test etmenizi sağlayan ComposeTestRule
ile ActivityScenarioRule
entegre eder.
class MyActivityTest { @Rule @JvmField val composeTestRule = createAndroidComposeRule<MyActivity>() @Test fun testGreeting() { val greeting = InstrumentationRegistry.getInstrumentation() .targetContext.resources.getString(R.string.greeting) composeTestRule.onNodeWithText(greeting).assertIsDisplayed() } }
Test etme hakkında daha fazla bilgi edinmek için Oluşturma düzeninizi test etme başlıklı makaleye bakın. Kullanıcı arayüzü test çerçeveleriyle birlikte çalışabilme için Espresso ile birlikte çalışabilirlik ve UiAutomator ile birlikte çalışabilirlik sayfalarına göz atın.
Compose'u mevcut uygulama mimarinizle entegre etme
Tek Yönlü Veri Akışı (UDF) mimari kalıpları, Compose ile sorunsuz şekilde çalışır. Uygulama, bunun yerine Model Görüntüleme Sunucusu (MVP) gibi başka mimari kalıpları kullanıyorsa Compose'u kullanmaya başlamadan önce veya kullanırken kullanıcı arayüzünün ilgili bölümünü UDF'ye taşımanızı öneririz.
Compose'da ViewModel
kullanma
Architecture Bileşenleri
ViewModel
kitaplığını kullanıyorsanız Oluşturma ve diğer kitaplıklar bölümünde açıklandığı gibi viewModel()
işlevini çağırarak herhangi bir composable'dan bir ViewModel
dosyasına erişebilirsiniz.
ViewModel
öğeleri Görüntüleme yaşam döngüsü kapsamlarını takip ettiğinden, Compose'u kullanırken aynı ViewModel
türünü farklı composable'larda kullanma konusunda dikkatli olun. Gezinme kitaplığı kullanılırsa kapsam; ana makine etkinliği, parça veya gezinme grafiği olacaktır.
Örneğin, composable'lar bir etkinlikte barındırılıyorsa viewModel()
, her zaman yalnızca etkinlik bittiğinde temizlenen örneği döndürür.
Aşağıdaki örnekte, aynı GreetingViewModel
örneği ana makine etkinliği altındaki tüm composable'larda yeniden kullanıldığından aynı kullanıcı ("user1") iki kez karşılanır. Oluşturulan ilk ViewModel
örnek diğer composable'larda yeniden kullanılıyor.
class GreetingActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { MaterialTheme { Column { GreetingScreen("user1") GreetingScreen("user2") } } } } } @Composable fun GreetingScreen( userId: String, viewModel: GreetingViewModel = viewModel( factory = GreetingViewModelFactory(userId) ) ) { val messageUser by viewModel.message.observeAsState("") Text(messageUser) } class GreetingViewModel(private val userId: String) : ViewModel() { private val _message = MutableLiveData("Hi $userId") val message: LiveData<String> = _message } class GreetingViewModelFactory(private val userId: String) : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") override fun <T : ViewModel> create(modelClass: Class<T>): T { return GreetingViewModel(userId) as T } }
Gezinme grafikleri de ViewModel
öğelerini kapsadığından, bir gezinme grafiğinde hedef olan composable'lar farklı bir ViewModel
örneğine sahiptir.
Bu durumda, ViewModel
hedefin yaşam döngüsüne dahil edilir ve hedef geri yığından kaldırıldığında temizlenir. Aşağıdaki örnekte, kullanıcı Profil ekranına gittiğinde yeni bir GreetingViewModel
örneği oluşturulur.
@Composable fun MyApp() { NavHost(rememberNavController(), startDestination = "profile/{userId}") { /* ... */ composable("profile/{userId}") { backStackEntry -> GreetingScreen(backStackEntry.arguments?.getString("userId") ?: "") } } }
Bilgi veri kaynağı
Kullanıcı arayüzünün bir bölümünde Oluştur'u seçtiğinizde, Compose ve View (Sistem) kodunun verileri paylaşması gerekebilir. Mümkün olduğunda bu paylaşılan durumu, her iki platform tarafından kullanılan UDF en iyi uygulamalarını takip eden başka bir sınıfa (örneğin, veri güncellemeleri yayınlamak için paylaşılan veri akışını açığa çıkaran bir ViewModel
içinde) kullanmanızı öneririz.
Ancak paylaşılacak veriler değişkense veya bir kullanıcı arayüzü öğesine sıkıca bağlıysa bu her zaman mümkün olmayabilir. Bu durumda, bir sistemin bilgi kaynağı olması ve bu sistemin, tüm veri güncellemelerini diğer sistemle paylaşması gerekir. Genel bir kural olarak, bilginin kaynağı, kullanıcı arayüzü hiyerarşisinin köküne daha yakın olan öğeye ait olmalıdır.
Bilgi kaynağı olarak oluşturun
Oluşturma durumunu Oluştur dışı kodlara yayınlamak için SideEffect
composable'ı kullanın. Bu durumda bilginin kaynağı, durum güncellemeleri gönderen bir composable'da saklanır.
Örnek olarak, analiz kitaplığınız sonraki tüm analiz etkinliklerine özel meta veriler (bu örnekte kullanıcı özellikleri) ekleyerek kullanıcı popülasyonunuzu segmentlere ayırmanıza olanak tanıyabilir. Mevcut kullanıcının kullanıcı türünü analiz kitaplığınıza iletmek için SideEffect
değerini kullanarak değerini güncelleyin.
@Composable fun rememberFirebaseAnalytics(user: User): FirebaseAnalytics { val analytics: FirebaseAnalytics = remember { FirebaseAnalytics() } // On every successful composition, update FirebaseAnalytics with // the userType from the current User, ensuring that future analytics // events have this metadata attached SideEffect { analytics.setUserProperty("userType", user.userType) } return analytics }
Daha fazla bilgi edinmek için Oluşturma işleminde yan efektler başlıklı makaleyi inceleyin.
Sistemi doğru kaynak olarak görüntüle
Durum, View sistemine aitse ve Compose ile paylaşılıyorsa Oluşturma için iş parçacığı güvenli hale getirilmesi amacıyla durumu mutableStateOf
nesnelerinde sarmalamanızı öneririz. Bu yaklaşımı kullanırsanız composable işlevler artık bilgi kaynağına sahip olmadıkları için basitleştirilmiştir. Ancak View sisteminin değişken durumu ve bu durumu kullanan Görünümler'i güncellemesi gerekir.
Aşağıdaki örnekte, bir CustomViewGroup
içinde bir TextView
ve içinde bir TextField
composable bulunan ComposeView
bulunuyor. TextView
, kullanıcının TextField
içinde yazdığı içeriğin içeriğini göstermelidir.
class CustomViewGroup @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyle: Int = 0 ) : LinearLayout(context, attrs, defStyle) { // Source of truth in the View system as mutableStateOf // to make it thread-safe for Compose private var text by mutableStateOf("") private val textView: TextView init { orientation = VERTICAL textView = TextView(context) val composeView = ComposeView(context).apply { setContent { MaterialTheme { TextField(value = text, onValueChange = { updateState(it) }) } } } addView(textView) addView(composeView) } // Update both the source of truth and the TextView private fun updateState(newValue: String) { text = newValue textView.text = newValue } }
Paylaşılan kullanıcı arayüzünü taşıma
Kademeli olarak Oluştur'a geçiyorsanız hem Oluştur hem de Görünüm sisteminde paylaşılan kullanıcı arayüzü öğeleri kullanmanız gerekebilir. Örneğin, uygulamanızda özel CallToActionButton
bileşeni varsa bu bileşeni hem Oluşturma hem de Görüntüleme tabanlı ekranlarda kullanmanız gerekebilir.
Compose'da paylaşılan kullanıcı arayüzü öğeleri, öğenin XML kullanılarak biçimlendirilmesine veya özel görünüm olmasına bakılmaksızın uygulama genelinde yeniden kullanılabilecek composable'lar haline gelir. Örneğin, özel harekete geçirici mesaj Button
bileşeniniz için bir CallToActionButton
composable'ı oluşturabilirsiniz.
Görünüme dayalı ekranlarda composable'ı kullanmak için AbstractComposeView
çerçevesini kapsayan özel bir görünüm sarmalayıcı oluşturun. Oluşturduğunuz composable, geçersiz kılınmış Content
composable öğesinin içine aşağıdaki örnekte gösterildiği gibi Compose temanıza sarmalanmış olarak yerleştirilir:
@Composable fun CallToActionButton( text: String, onClick: () -> Unit, modifier: Modifier = Modifier, ) { Button( colors = ButtonDefaults.buttonColors( containerColor = MaterialTheme.colorScheme.secondary ), onClick = onClick, modifier = modifier, ) { Text(text) } } class CallToActionViewButton @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyle: Int = 0 ) : AbstractComposeView(context, attrs, defStyle) { var text by mutableStateOf("") var onClick by mutableStateOf({}) @Composable override fun Content() { YourAppTheme { CallToActionButton(text, onClick) } } }
composable parametrelerin, özel görünüm içinde değişken değişkenlere dönüştüğüne dikkat edin. Bu, özel CallToActionViewButton
görünümünü geleneksel görünüm gibi şişirilebilir ve kullanılabilir hale getirir. Aşağıdaki Görünüm Bağlama ile bunun bir örneğini görebilirsiniz:
class ViewBindingActivity : ComponentActivity() { private lateinit var binding: ActivityExampleBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityExampleBinding.inflate(layoutInflater) setContentView(binding.root) binding.callToAction.apply { text = getString(R.string.greeting) onClick = { /* Do something */ } } } }
Özel bileşen değişken durum içeriyorsa Durum bilgi kaynağı bölümüne bakın.
Sunudan bölme durumunu önceliklendirin
Geleneksel olarak View
durum bilgili olur. View
, nasıl gösterileceğinin yanı sıra nenin gösterileceğini açıklayan alanları da yönetir. Bir View
öğesini Compose'a dönüştürürken eyalet yükseltme bölümünde ayrıntılı olarak açıklandığı gibi, oluşturulan verileri tek yönlü veri akışı sağlayacak şekilde ayırmaya çalışın.
Örneğin View
, görünür olup olmadığını veya kaybolup olmadığını açıklayan visibility
özelliğine sahiptir. Bu, View
öğesinin doğal bir özelliğidir. Diğer kod parçaları bir View
öğesinin görünürlüğünü değiştirebilir ancak yalnızca View
, mevcut görünürlüğünün gerçekten ne olduğunu bilir. Bir View
öğesinin görünür olmasını sağlamanın mantığı hataya açık olabilir ve genellikle View
ile bağlantılıdır.
Buna karşın Compose ise Kotlin'de koşullu mantık kullanarak tamamen farklı composable'ları görüntülemeyi kolaylaştırır:
@Composable fun MyComposable(showCautionIcon: Boolean) { if (showCautionIcon) { CautionIcon(/* ... */) } }
Tasarım gereği, CautionIcon
neden görüntülendiğini bilmesine veya önemsemesine gerek yoktur ve visibility
kavramı yoktur: Hem Bestede yer alır hem de değildir.
Durum yönetimi ve sunum mantığını net bir şekilde ayırarak, durumu kullanıcı arayüzüne dönüştürerek içeriği görüntüleme şeklinizi daha özgürce değiştirebilirsiniz. Gerektiğinde eski durumu kaldırabilme imkanı, composable'ları yeniden kullanılabilir kılmaktadır, çünkü devlet sahipliği daha esnektir.
Kapsüllenmiş ve yeniden kullanılabilir bileşenleri teşvik edin
View
öğeleri genellikle nerede bulunduklarına dair bir fikre sahiptir: Activity
, Dialog
, Fragment
veya başka bir View
hiyerarşisinin içindeki herhangi bir yer. Bunlar genellikle statik düzen dosyalarından şişirildiği için View
'nin genel yapısı çok sabit olur. Bu, daha sıkı bir bağlantı oluşturur ve View
öğesinin değiştirilmesini veya yeniden kullanılmasını zorlaştırır.
Örneğin, bir özel View
, belirli bir türde, belirli bir kimliğe sahip alt görünüme sahip olduğunu varsayabilir ve bir işleme yanıt olarak özelliklerini doğrudan değiştirebilir. Bu işlem, bu View
öğelerini birbiriyle sıkı şekilde birleştirir: Alt öğeyi bulamazsa özel View
kilitlenebilir veya bozulabilir ve alt öğe, özel View
üst öğesi olmadan muhtemelen yeniden kullanılamaz.
Bu, Compose'da yeniden kullanılabilir composable'larla ilgili bir sorun teşkil etmez. Ebeveynler durum ve geri çağırmaları kolayca belirtebilir, böylece tam olarak nerede kullanılacağını bilmek zorunda kalmadan yeniden kullanılabilir composable'lar yazabilirsiniz.
@Composable fun AScreen() { var isEnabled by rememberSaveable { mutableStateOf(false) } Column { ImageWithEnabledOverlay(isEnabled) ControlPanelWithToggle( isEnabled = isEnabled, onEnabledChanged = { isEnabled = it } ) } }
Yukarıdaki örnekte, her üç bölüm de daha kapsamlı ve daha az kuyrukludur:
ImageWithEnabledOverlay
yalnızca mevcutisEnabled
durumunun ne olduğunu bilmelidir.ControlPanelWithToggle
ifadesinin var olduğunu, hatta nasıl kontrol edilebilir olduğunu bile bilmesine gerek yoktur.ControlPanelWithToggle
,ImageWithEnabledOverlay
uygulamasının mevcut olduğunu bilmiyor.isEnabled
sıfır, bir veya daha fazla şekilde gösterilirkenControlPanelWithToggle
için de değişiklik yapılması gerekmez.Üst yayıncı için
ImageWithEnabledOverlay
veyaControlPanelWithToggle
öğelerinin ne kadar derin bir şekilde iç içe geçmiş olduğu önemli değildir. Bu çocuklar animasyon yapıyor, içerikleri değiştiriyor veya diğer çocuklara içerik aktarıyor olabilir.
Bu kalıp, kontrolün ters çevirmesi olarak bilinir. Bu konu hakkında daha fazla bilgiyi CompositionLocal
belgelerinde bulabilirsiniz.
Ekran boyutu değişikliklerini işleme
Farklı pencere boyutları için farklı kaynaklara sahip olmak, duyarlı View
düzenleri oluşturmanın ana yollarından biridir. Nitelikli kaynaklar ekran düzeyinde düzen kararları için hâlâ bir seçenek olsa da Oluştur, düzenleri normal koşullu mantıkla tamamen kod halinde değiştirmeyi çok daha kolay hale getirir. Daha fazla bilgi edinmek için Farklı ekran boyutlarını destekleme bölümüne bakın.
Ayrıca, Compose'un uyarlanabilir kullanıcı arayüzleri oluşturmak için sunduğu teknikleri öğrenmek için Uyarlanabilir düzenler oluşturma bölümüne bakın.
Görünümler ile iç içe kaydırma
Kaydırılabilir View öğeleri ve her iki yönde iç içe yerleştirilmiş kaydırılabilir composable'lar arasında iç içe kaydırma birlikte çalışabilirlik özelliğinin nasıl etkinleştirileceği hakkında daha fazla bilgi için İç içe kaydırma birlikte çalışabilirlik bölümünü okuyun.
RecyclerView
uygulamasında oluştur
RecyclerView
ürünündeki composable'lar RecyclerView
1.3.0-alpha02 sürümünden itibaren iyi performans gösteriyor. Bu avantajları görmek için RecyclerView
uygulamasının en az 1.3.0-alpha02 sürümünü kullandığınızdan emin olun.
WindowInsets
ile View'lar birlikte çalışabilir
Ekranınızda aynı hiyerarşide hem Görünümler hem de Oluşturma kodu olduğunda varsayılan ek öğeleri geçersiz kılmanız gerekebilir. Bu durumda, verinin hangi veride kullanıldığını, hangisinin hangi veriyi göz ardı etmesi gerektiğini net olarak belirlemeniz gerekir.
Örneğin, en dıştaki düzeniniz bir Android Görünümü düzeniyse ek öğeleri Görünüm sisteminde kullanmalı ve Oluştur için yoksaymalısınız.
Alternatif olarak, en dıştaki düzeniniz composable ise Compose'da içe aktarılan öğeleri kullanıp AndroidView
composable'ları buna uygun şekilde doldurmanız gerekir.
Varsayılan olarak her ComposeView
, WindowInsetsCompat
tüketim düzeyindeki tüm ekleri tüketir. Bu varsayılan davranışı değiştirmek için ComposeView.consumeWindowInsets
değerini false
olarak ayarlayın.
Daha fazla bilgi için Oluşturma bölümünde WindowInsets
dokümanlarını okuyun.
Sizin için önerilenler
- Not: Bağlantı metni JavaScript kapalıyken görüntülenir
- Emoji gösterme
- Oluşturma işleminde Materyal Tasarım 2
- Oluşturma penceresinde pencere öğeleri