Dikkat edilmesi gereken diğer noktalar

Görünümler'den Compose'a geçiş tamamen kullanıcı arayüzüyle ilgili olsa da birçok güvenli ve artımlı bir taşıma gerçekleştirmek için dikkate alınması gereken noktalar vardır. Bu taşıma işlemi sırasında göz önünde bulundurulması gereken bazı Compose'a görüntüleme tabanlı uygulama.

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ı uygulamalarda, Materyal'in üç sürümü mevcuttur:

  • Materyal Tasarım 1, AppCompat kitaplığı (ör. Theme.AppCompat.*)
  • Materyal Tasarım 2, MDC-Android kütüphane (ör. Theme.MaterialComponents.*)
  • Materyal Tasarım 3, MDC-Android kütüphane (ör. Theme.Material3.*)

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

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

Uygulamanızın tasarım sistemiyse en yeni sürümü (Material 3) kullanmanızı öneririz kişi bunu yapabilecek durumda olacaktır. Her iki Görünüm için de taşıma kılavuzları mevcut ve Oluştur:

Materyalin hangi sürümüne sahip olursa olsun Compose'da yeni ekran oluştururken Kullandığınız tasarımda, herhangi bir uygulamadan önce MaterialTheme uyguladığınızdan emin olun Compose Material kitaplıklarından kullanıcı arayüzü yayınlayan composable'lar. Materyal bileşenler (Button, Text vb.), mevcut bir MaterialTheme değerine bağlıdır ve bunlar olmadan davranışları tanımsızdır.

Tümü Jetpack Compose örnekleri MaterialTheme temel alınarak oluşturulmuş özel bir Oluştur teması kullanın.

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 Navigasyon bileşenini kullanıyorsanız Oluşturma ile Gezinme - Birlikte Çalışabilirlik ve Daha fazla bilgi için Jetpack Gezinme'yi Gezinme Oluşturma'ya taşıyın.

Karma Oluştur/Görüntülemeler kullanıcı arayüzünüzü test etme

Uygulamanızın bazı bölümlerini Compose'a taşıdıktan sonra test, uygulamanızın hiçbir şeyi düzeltmediniz.

Bir etkinlik veya parça Oluşturma özelliğini kullandığında, createAndroidComposeRule ActivityScenarioRule tercih edebilirsiniz. createAndroidComposeRule entegrasyonu ActivityScenarioRule ve ComposeTestRule ile Oluşturma ve oluşturma işlemlerini test etmenize olanak tanır. Kodu aynı anda görüntüleyebilirsiniz.

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 başlıklı makalelere bakın.

Compose'u mevcut uygulama mimarinizle entegre etme

Tek Yönlü Veri Akışı (UDF) mimarisi desenleri de Compose ile uyumlu. Uygulama, (MVP) gibi mimari kalıplarına bağlı olarak değil, Compose'dan önce veya kullanım sırasında kullanıcı arayüzünün bu bölümünü UDF'ye taşıyın.

Oluşturma'da ViewModel kullanma

Mimari Bileşenleri ViewModel kitaplığındaki Herhangi bir composable'dan ViewModel viewModel() fonksiyonuna ilişkin Oluşturma ve diğer kitaplıklar bölümünde açıklanmıştır.

Oluştur'u kullanırken aynı ViewModel türünün ViewModel öğeleri View yaşam döngüsü kapsamlarını izlediğinden farklı composable'lar kullanılabilir. Kapsam, ana makine etkinliği, parça veya Gezinme kitaplığı kullanılıyorsa gezinme grafiği olacaktır.

Örneğin, composable'lar bir etkinlik içinde barındırılıyorsa viewModel() her zaman yalnızca etkinlik tamamlandığında silinen aynı örneği döndürür. Aşağıdaki örnekte, aynı kullanıcı ("user1") iki kez selamlanmıştır çünkü aynı GreetingViewModel örneği yardımcı olur. Oluşturulan ilk ViewModel örnek, diğer composables.

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 olarak ViewModel öğelerinin kapsamı, bir gezinme grafiğindeki hedefte ViewModel öğesinin farklı bir örneği vardır. Bu durumda ViewModel, hedefin yaşam döngüsünü kapsar 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şturuldu.

@Composable
fun MyApp() {
    NavHost(rememberNavController(), startDestination = "profile/{userId}") {
        /* ... */
        composable("profile/{userId}") { backStackEntry ->
            GreetingScreen(backStackEntry.arguments?.getString("userId") ?: "")
        }
    }
}

Doğru bilgi kaynağı

Kullanıcı arayüzünün bir bölümünde Oluştur'u kullandığınızda Oluştur ve Görüntüleme sistem kodunun veri paylaşması gerekebilir. Mümkünse bu paylaşılan durumu UDF en iyi uygulamalarını izleyen başka bir sınıfa ekleyin. her iki platform tarafından kullanılan örneğin, ViewModel Paylaşılan verilerin veri güncellemeleri çıkarmasını sağlar.

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 sistem kabul etmesi ve bu sistemin, veri güncellemelerini diğer sistemle paylaşması gerekir. Genel kural olarak, doğruluk kaynağının sahibi kullanıcı arayüzü hiyerarşisinin köküne daha yakın olan öğe olmalıdır.

Doğru bilgi kaynağı olarak yazın

Şunu kullanın: SideEffect composable'ı kullanarak Compose durumunu Compose dışı koda yayınlayabilirsiniz. Bu durumda, veri 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 değerini güncellemek üzere SideEffect kullanın.

@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 Oluşturma'daki yan etkiler başlıklı makaleyi inceleyin.

Bilgi kaynağı olarak sistemi göster

Görüntüleme sistemi eyaletin sahibiyse ve Oluştur ile paylaşıyorsa şunu öneririz: durumu, iş parçacığı açısından güvenli hâle getirmek üzere mutableStateOf nesne içine sarmalarsınız. Oluştur'u tıklayın. Bu yaklaşımı kullanırsanız composable işlevler basitleştirilmiştir çünkü bu artık veri kaynağına sahip değiller, ancak Görünüm sisteminin durumu kullanan Görünümler de dahildir.

Aşağıdaki örnekte, CustomViewGroup bir TextView ve bir İçinde TextField composable bulunan ComposeView. TextView özelliğinin gösterilmesi gerekiyor Kullanıcının TextField içinde yazdığı içeriğin içeriği.

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 paylaşılan kullanıcı arayüzünü kullanmanız gerekebilir öğelerini görüntüleme ve yönetme işlemlerini yapabilirsiniz. Örneğin, uygulamanızda özel CallToActionButton bileşenini, Oluşturduğunuz ve görüntülemeye dayalı ekranlar.

Oluşturma'da, paylaşılan kullanıcı arayüzü öğeleri, XML kullanılarak stilize edilmiş veya özel görünümlü olup olmadığına bakılmaksızın uygulama genelinde yeniden kullanılabilen bileşenler haline gelir. Örneğin, Örneğin, şuna özel çağrınız için bir CallToActionButton composable: işlem Button bileşeni.

Görünüme dayalı ekranlarda composable'ı kullanmak için, şu özelliklere sahip özel bir görünüm sarmalayıcı oluşturun: AbstractComposeView ile başlayıp uzanıyor. Geçersiz kılınmış Content composable'da, oluşturduğunuz composable'ı aşağıdaki örneğe bakın:

@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. Bu da özel CallToActionViewButton görünümünü şişirebilir ve kullanılabilir hale getirir, geleneksel bir görünüm gibi. Görünüm Bağlama ile bunun bir örneğini görebilirsiniz aşağıda bulabilirsiniz:

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şende değişken durum varsa Doğruluk durumu kaynağı bölümüne bakın.

Sunudan bölme durumuna öncelik ver

Geleneksel olarak View, durum bilgilidir. View, şu alanları yönetir: Ne gösterileceğini ve nasıl gösterileceğini açıklayın. Google Takvim widget'ını View öğesini Compose'a dönüştürüyorsanız oluşturulan verileri durum kaldırma bölümünde daha ayrıntılı olarak açıklandığı gibi tek yönlü bir veri akışı elde etmek.

Örneğin, bir View,visibility görünmez veya kaybolabilir. Bu, View öğesinin doğal bir özelliğidir. Bu sırada diğer kod parçaları View öğesinin görünürlüğünü değiştirebilir, yalnızca View ne olduğunu gerçekten biliyor. Bunu sağlamanın mantığı, bir View görünür olduğunda hataya açık olabilir ve genellikle View ile bağlantılıdır kendisi.

Buna karşın Compose, tamamen farklı composable'ları görüntülemeyi kolaylaştırır (kotlin'de koşullu mantığı kullanarak):

@Composable
fun MyComposable(showCautionIcon: Boolean) {
    if (showCautionIcon) {
        CautionIcon(/* ... */)
    }
}

CautionIcon web sitesinin tasarımı gereği, neden gösterildiğini bilmesine veya bununla ilgilenmesine gerek yoktur. visibility kavramı da yoktur. O ya bestededir, ya da değildir.

Durum yönetimi ile sunum mantığını net bir şekilde birbirinden ayırarak, durumu kullanıcı arayüzüne dönüştürme olarak görüntüleme şeklinizi özgürce değiştirebilirsiniz. Olmak composable'ı yeniden kullanılabilir hâle getirdiği için, eyalet sahipliği daha esnektir.

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

View öğelerinin genellikle nerede bulunduklarına dair bir fikri vardır: Bir Activity içinde, Dialog, Fragment veya başka bir View hiyerarşisinin içinde başka bir yerde. Çünkü genellikle statik düzen dosyalarından şişirilirler. Bu dosya, bir View genelde çok katıdır. Bu, daha sıkı bağlantı sağlar ve bir View değerinin değiştirilmesi veya yeniden kullanılması daha zordur.

Örneğin, özel bir View belirli bir özelliğin alt görünümüne sahip olduğunu belirli bir kimlikle yazmanızı ve özelliklerini doğrudan eyleme dökülebilir. Bu, söz konusu View öğelerini birbirine sıkıca bağlar: Özel View, alt öğeyi bulamadığı takdirde kilitlenebilir veya bozulabilir. Alt öğe de özel View üst öğesi olmadan büyük olasılıkla yeniden kullanılamaz.

Bu, yeniden kullanılabilir composable'larla Compose'da daha az sorun yaşanır. Ebeveynler durumu ve geri çağırma işlevlerini kolayca belirtebilir. Böylece, kullanılacakları yeri tam olarak bilmek zorunda kalmadan yeniden kullanılabilir bileşenler 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 uygulamasının yalnızca geçerli isEnabled öğesinin ne olduğunu bilmesi gerekiyor belirtin. ControlPanelWithToggle varlığının bilmesi gerekmez veya nasıl kontrol edilebildiğini bile fark edebilirsiniz.

  • ControlPanelWithToggle, ImageWithEnabledOverlay adlı cihazın mevcut olduğunu bilmiyor. isEnabled, sıfır, bir veya daha fazla şekilde gösterilebilir ve ControlPanelWithToggle adlı kullanıcının değişmesi gerekmez.

  • Üst öğe için, ImageWithEnabledOverlay öğesinin ne kadar derinlemesine iç içe yerleştirilmiş olduğu önemli değildir veya ControlPanelWithToggle. Bu çocuklar, değişiklikler için animasyon başka çocuklara içerik aktarmaktan bahsedeceğiz.

Bu kalıp kontrolün ters çevirmesi olarak bilinir. Daha fazla bilgi edinmek için hakkındaki CompositionLocal belgeleri inceleyebilirsiniz.

Ekran boyutu değişikliklerini işleme

Farklı pencere boyutları için farklı kaynaklara sahip olmak, projenizin duyarlı View düzenleri oluşturabilirsiniz. Nitelikli kaynaklar bir seçenek olarak ekran düzeyinde düzen kararları için Compose'u kullanmak çok daha kolay normal koşullu mantıkla tamamen koddan bir araya getirmenizi sağlar. Daha fazla bilgi edinmek için Pencere boyutu sınıfları başlıklı makaleyi inceleyin.

Ayrıca, Farklı ekran boyutlarını destekleme başlıklı makaleyi inceleyin. Compose'un uyarlanabilir kullanıcı arayüzleri oluşturmak için sunduğu teknikleri öğrenin.

Görünümler ile iç içe geçmiş kaydırma

Sayfalar arasında iç içe kaydırmayı etkinleştirme hakkında daha fazla bilgi için kaydırılabilir görünüm öğeleri ve kaydırılabilir composable'lar, her iki yönde iç içe yerleştirilmiş. sesli oku İç içe kaydırma birlikte çalışabilirliği.

RecyclerView uygulamasında oluştur

RecyclerView içindeki derlenebilirler, RecyclerView sürümünden bu yana iyi performans gösteriyor 1.3.0-alpha02. Bu avantajlardan yararlanmak için RecyclerView'ı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 hem Görünümler hem de görünümler olduğunda varsayılan değerleri geçersiz kılmanız gerekebilir. Kodu aynı hiyerarşide oluşturun. Bu durumda, konu hakkında ek öğeleri hangisinin tüketmesi gerektiği ve hangisinin yoksayılması gerektiği.

Örneğin, en dıştaki düzeniniz bir Android View düzeniyse insetleri Görüntüle sisteminde tüketme ve Oluşturma için bunları yoksayma. Alternatif olarak, en dıştaki düzeniniz bir composable ise inset'leri ekleyin ve AndroidView composable'ı buna uygun şekilde yerleştirin.

Varsayılan olarak her ComposeView, WindowInsetsCompat tüketim seviyesi. Bu varsayılan davranışı değiştirmek için ComposeView.consumeWindowInsets Hedef: false.

Daha fazla bilgi için E-posta Yazma'da WindowInsets dokümanlarını okuyun.