Dikkat edilmesi gereken diğer noktalar

Görünümler'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 dikkate alınması gereken birçok nokta vardır. Bu sayfada, Görünüm tabanlı uygulamanızı Compose'a taşırken dikkate alınması gereken bazı noktalar bulunmaktadır.

Uygulamanızın temasını taşıma

Android uygulamalarına tema eklemek için önerilen tasarım sistemi Materyal Tasarım'dır.

Görünüm tabanlı uygulamalarda, Materyal'in üç sürümü mevcuttur:

  • AppCompat kitaplığının kullanıldığı Materyal Tasarım 1 (ör. Theme.AppCompat.*)
  • MDC-Android kitaplığını (ör. Theme.MaterialComponents.*) kullanan Materyal Tasarım 2
  • MDC-Android kitaplığını (ör. Theme.Material3.*) kullanan Materyal Tasarım 3

Oluşturma uygulamaları için Material'ın iki sürümü vardır:

  • Compose Material kitaplığını kullanan Materyal Tasarım 2 (ör. androidx.compose.material.MaterialTheme)
  • Compose Material 3 kitaplığını kullanan Materyal Tasarım 3 (ör. androidx.compose.material3.MaterialTheme)

Uygulamanızın tasarım sistemi uygun konumdaysa en son sürümü (Materyal 3) kullanmanızı öneririz. Hem Görünümler hem de Oluşturma için taşıma kılavuzları vardır:

Compose'da yeni ekranlar oluştururken, Material Design'ın hangi sürümünü kullanıyor olursanız olun, Compose Material kitaplıklarından kullanıcı arayüzü yayınlayan tüm composable'lardan önce MaterialTheme eklediğinizden emin olun. Malzeme bileşenleri (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 örnekleri MaterialTheme temel alınarak oluşturulmuş özel bir Oluştur teması kullanır.

Daha fazla bilgi edinmek için Compose'daki tasarım sistemleri ve XML temalarını Compose'a taşıma sayfalarına göz atın.

Uygulamanızda Gezinme bileşenini kullanıyorsanız daha fazla bilgi için Compose ile Gezinme - Birlikte Çalışabilirlik ve Jetpack Gezinmeyi Gezinme Oluşturmaya 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 test yapmak, hiçbir şeyi bozmadığınızdan emin olmak için kritik öneme sahiptir.

Bir etkinlik veya parça Compose'u kullandığında ActivityScenarioRule yerine createAndroidComposeRule kullanmanız gerekir. createAndroidComposeRule, Oluşturma ve Görüntüleme kodunu aynı anda test etmenize olanak tanıyan bir ComposeTestRule ile ActivityScenarioRule entegre edilir.

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 hakkında daha fazla bilgi edinmek için Oluşturma düzeninizi test etme konusuna bakın. Kullanıcı arayüzü test çerçeveleriyle birlikte çalışabilirlik için Espresso ile birlikte çalışabilirlik ve UiAutomator ile birlikte çalışabilirlik sayfalarını inceleyin.

Compose'u mevcut uygulama mimarinizle entegre etme

Tek Yönlü Veri Akışı (UDF) mimari kalıpları Oluşturma ile sorunsuz şekilde çalışır. Uygulama, Model Görünümü 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 bu bölümünü UDF'ye taşımanızı öneririz.

Compose'da ViewModel kullanma

Mimari BileşenleriViewModel kitaplığını kullanırsanız Oluşturma ve diğer kitaplıklar bölümünde açıklandığı gibi viewModel() işlevini çağırarak herhangi bir composable'dan ViewModel öğesine erişebilirsiniz.

Oluştur'u benimserken ViewModel öğelerinin görüntüleme yaşam döngüsü kapsamlarını izlemesi nedeniyle farklı composable'larda aynı ViewModel türünün kullanılmasına dikkat edin. Gezinme kitaplığı kullanılıyorsa bu kapsam; ana makine etkinliği, parça veya gezinme grafiği olur.

Örneğin, composable'lar bir etkinlik içinde barındırılıyorsa viewModel(), yalnızca etkinlik tamamlandığında silinen aynı örneği her zaman döndürür. Aşağıdaki örnekte, ana makine etkinliği altındaki tüm composable'larda aynı GreetingViewModel örneği yeniden kullanıldığından aynı kullanıcı ("user1") iki kez karşılanır. Oluşturulan ilk ViewModel örneği, diğer composable'larda yeniden kullanılır.

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 öğelerinin kapsamını kullandığından gezinme grafiğinde hedef olan composable'lar farklı bir ViewModel örneğine sahiptir. Bu durumda, ViewModel, hedefin yaşam döngüsüne ayarlanır ve hedef, geri yığından kaldırıldığında silinir. 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") ?: "")
        }
    }
}

Doğru bilgi kaynağı

Oluştur'u kullanıcı arayüzünün bir bölümünde kullandığınızda, 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ı kullanan başka bir sınıfta (ör. veri güncellemeleri yaymak için paylaşılan veri akışını sunan bir ViewModel içinde) kapsüllemenizi öneririz.

Ancak paylaşılacak veriler değişkense veya sıkı bir şekilde bir kullanıcı arayüzü öğesine bağlıysa bu her zaman mümkün değildir. Bu durumda, bir sistemin veri kaynağı olması ve o sistemin, veri güncellemelerini diğer sistemle paylaşması gerekir. Genel bir kural olarak, doğru bilgi kaynağı, kullanıcı arayüzü hiyerarşisinin köküne daha yakın olan öğeye ait olmalıdır.

Doğru bilgi kaynağı olarak yazın

Oluşturma durumunu Compose dışı bir koda yayınlamak için SideEffect composable'ı kullanın. Bu durumda, bilgi kaynağı durum güncellemeleri gönderen bir composable'da tutulur.

Örneğin analiz kitaplığınız, sonraki tüm analiz etkinliklerine özel meta veriler (bu örnekte kullanıcı özellikleri) ekleyerek kullanıcı kitlenizi 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 için Compose'da yan etkiler bölümüne bakın.

Bilgi kaynağı olarak sistemi göster

Görünüm sistemi eyalete aitse ve E-posta Oluşturma ile paylaşıyorsa durumu, Compose için iş parçacığı açısından güvenli hale getirmek amacıyla mutableStateOf nesne için sarmalamanızı öneririz. Bu yaklaşımı kullanırsanız composable işlevler basitleştirilmiştir çünkü artık doğru kaynağa sahip değildirler, ancak View sisteminin, değişken durumu ve bu durumu kullanan görünümleri güncellemesi gerekir.

Aşağıdaki örnekte bir CustomViewGroup, içinde TextField composable bulunan bir TextView ve bir ComposeView içerir. TextView, kullanıcının TextField ürününde yazdığı içeriği 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ü taşınıyor

Kademeli olarak Compose'a geçiş yapıyorsanız hem Compose hem de View sisteminde paylaşılan kullanıcı arayüzü öğeleri kullanmanız gerekebilir. Örneğin, uygulamanızın özel bir CallToActionButton bileşeni varsa bunu hem Oluşturma hem de Görünüm tabanlı ekranlarda kullanmanız gerekebilir.

Compose'da paylaşılan kullanıcı arayüzü öğeleri, stilinin XML kullanılarak oluşturulması veya özel bir görünüm olması fark etmeksizin uygulama genelinde yeniden kullanılabilen composable'lar haline gelir. Örneğin, özel harekete geçirici mesaj Button bileşeni için bir CallToActionButton composable'ı oluşturursunuz.

Görünüme dayalı ekranlarda composable'ı kullanmak için AbstractComposeView tarihinden itibaren genişleyen özel bir görünüm sarmalayıcı oluşturun. Oluşturduğunuz composable'ı, geçersiz kılınan Content composable'a aşağıdaki örnekte gösterildiği gibi Compose temanıza sarmalanmış olarak yerleştirin:

@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)
        }
    }
}

Oluşturulabilir parametrelerin, özel görünüm içinde değişebilir değişkenler haline geldiğine 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ğıda 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 Eyalet doğruluk kaynağı bölümüne bakın.

Sunudan bölme durumuna öncelik ver

Geleneksel olarak View, durum bilgilidir. View, ne gösterileceğini ve nasıl gösterileceğini açıklayan alanları yönetir. Bir View öğesini Compose'a dönüştürdüğünüzde, durum kaldırma bölümünde daha ayrıntılı açıklandığı şekilde tek yönlü bir veri akışı gerçekleştirmek için oluşturulan verileri ayırmaya bakın.

Örneğin, bir View öğenin görünür olup olmadığını veya görünmediğini belirten bir 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ştirebilse de mevcut görünürlüğünün ne olduğunu yalnızca View gerçekten bilir. 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şılık, Compose, 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(/* ... */)
    }
}

CautionIcon ürününün tasarımı gereği, neden gösterildiğini bilmesine veya önemsemesine gerek yoktur. visibility kavramı yoktur. Bestede yer alır ya da değildir.

Durum yönetimi ve sunum mantığını net bir şekilde ayırarak, içeriği durumun kullanıcı arayüzüne dönüşümü olarak görüntüleme şeklinizi daha özgür bir şekilde değiştirebilirsiniz. Eyalet sahipliği daha esnek olduğundan, gerektiğinde kaldıraç duruma getirebilme olanağı, composable'ları yeniden kullanılabilir hale de getirir.

Kapsüllenmiş ve yeniden kullanılabilir bileşenleri tanıtın

View öğelerinin genellikle nerede yaşadıklarına dair bir fikri vardır: Activity, Dialog, Fragment veya başka bir View hiyerarşisinin içindeki bir yerde. Genellikle statik düzen dosyalarından şişirildiklerinden, View'in genel yapısı çok katı olma eğilimindedir. Bu, daha sıkı bağlantı sağlar ve View öğesinin değiştirilmesini veya yeniden kullanılmasını zorlaştırır.

Örneğin, özel bir View, belirli bir kimliğe sahip belirli bir türde alt görünüme sahip olduğunu varsayabilir ve bir işleme yanıt olarak özelliklerini doğrudan değiştirebilir. Böylece, söz konusu View öğeleri sıkı bir şekilde bağlanır: Özel View, alt öğeyi bulamıyorsa kilitlenebilir veya bozulabilir ve alt öğe, büyük olasılıkla özel View üst öğesi olmadan yeniden kullanılamaz.

Bu, yeniden kullanılabilir composable'larla Compose'da daha az sorun yaşanır. Ebeveynler, durumu ve geri çağırmayı kolayca belirtebilir. Böylece, kullanılacak yeri tam olarak 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, üç parça da daha kapsüllenmiş ve daha az ilişkilidir:

  • ImageWithEnabledOverlay öğesinin yalnızca mevcut isEnabled durumunun ne olduğunu bilmesi gerekir. ControlPanelWithToggle öğesinin var olduğunu ve hatta nasıl kontrol edilebilir olduğunu bilmesine gerek yoktur.

  • ControlPanelWithToggle, ImageWithEnabledOverlay adlı cihazın mevcut olduğunu bilmiyor. isEnabled, görüntülenmek üzere sıfır, bir veya daha fazla şekilde olabilir ve ControlPanelWithToggle ürününün değişmesi gerekmez.

  • Üst ağ için, ImageWithEnabledOverlay veya ControlPanelWithToggle öğesinin ne kadar derin bir şekilde iç içe yerleştirilmiş olduğu önemli değildir. Bu çocuklar, değişikliklere animasyon katıyor, içeriği değiştiriyor veya içeriği başka çocuklara veriyor olabilir.

Bu kalıp, kontrolün ters çevrilmesi olarak bilinir. Bununla ilgili 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. Ekran düzeyinde düzen kararları için uygun kaynaklar kullanılmaya devam etse de Compose, normal koşullu mantıkla düzenleri tamamen kodda değiştirmeyi çok daha kolay hale getirir. Daha fazla bilgi edinmek için Pencere boyutu sınıfları başlıklı makaleyi inceleyin.

Ayrıca, Compose'un uyarlanabilir kullanıcı arayüzleri oluşturmak amacıyla sunduğu teknikleri öğrenmek için Farklı ekran boyutlarını destekleme bölümüne bakın.

Görünümler ile iç içe geçmiş 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ışmasını etkinleştirme hakkında daha fazla bilgi için İç içe kaydırma birlikte çalışabilirliği makalesini okuyun.

RecyclerView uygulamasında oluştur

RecyclerView ürünündeki derlenebilirler, RecyclerView sürümünden 1.3.0-alfa02'den beri yüksek performans göstermektedir. Bu avantajları görebilmek için RecyclerView uygulamasının en az 1.3.0-alpha02 sürümünü kullandığınızdan emin olun.

WindowInsets, Görünümler ile birlikte çalışabilir

Ekranınızda aynı hiyerarşide hem Görünümler hem de Oluştur kodu varsa varsayılan ekleri geçersiz kılmanız gerekebilir. Bu durumda, hangi tarafın ekleri kullanması gerektiğini ve hangisinin bunları göz ardı etmesi gerektiğini açıkça belirtmeniz gerekir.

Örneğin, en dıştaki düzeniniz Android View düzeniyse Görünüm sistemindeki ek öğeleri kullanmalı ve Oluşturma için bunları yoksaymalısınız. Alternatif olarak, en dıştaki düzeniniz bir composable ise Compose'da ek öğeleri kullanmalı ve AndroidView composable'ı buna uygun şekilde doldurmalısınız.

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 E-posta Yazma'da WindowInsets dokümanlarını okuyun.