Bir Bakışta ile kullanıcı arayüzü oluşturma

Bu sayfada, mevcut Glance bileşenlerini kullanarak boyutların nasıl ele alınacağı ve Glance ile esnek ve duyarlı düzenlerin nasıl sağlanacağı açıklanmaktadır.

Box, Column ve Row kullanın

Glance'ta composable üç ana sayfa düzeni bulunur:

  • Box: Öğeleri başka bir öğenin üzerine yerleştirir. RelativeLayout diline çevrilir.

  • Column: Öğeleri dikey eksende birbirinden sonra yerleştirir. Dikey yönlü LinearLayout anlamına gelir.

  • Row: Öğeleri yatay eksende birbirinden sonra yerleştirir. Yatay yönlü bir LinearLayout anlamına gelir.

Glance, Scaffold nesnelerini destekler. Column, Row ve Box composable'larınızı belirli bir Scaffold nesnesine yerleştirin.

Sütun, satır ve kutu düzeninin resmi.
Şekil 1. Sütun, Satır ve Kutu içeren düzen örnekleri.

Bu composable'ların her biri, içeriklerinin dikey ve yatay hizalamalarını ve değiştiricileri kullanarak genişlik, yükseklik, ağırlık veya dolgu kısıtlamalarını tanımlamanızı sağlar. Buna ek olarak, her alt öğe üst öğe içindeki alanı ve yerleşimi değiştirmek için değiştiricisini tanımlayabilir.

Aşağıdaki örnekte, Şekil 1'de görüldüğü gibi, alt öğelerini yatay olarak eşit bir şekilde dağıtan bir Row öğesinin nasıl oluşturulacağı gösterilmektedir:

Row(modifier = GlanceModifier.fillMaxWidth().padding(16.dp)) {
    val modifier = GlanceModifier.defaultWeight()
    Text("first", modifier)
    Text("second", modifier)
    Text("third", modifier)
}

Row, kullanılabilir maksimum genişliği doldurur ve her alt öğe aynı ağırlığa sahip olduğundan kullanılabilir alanı eşit olarak paylaşır. Düzenleri ihtiyaçlarınıza göre uyarlamak için farklı ağırlıklar, boyutlar, dolgular veya hizalamalar tanımlayabilirsiniz.

Kaydırılabilir düzenler kullanma

Duyarlı içerik sağlamanın bir başka yolu da kaydırılabilir hale getirmektir. Bu, LazyColumn composable ile yapılabilir. Bu composable, uygulama widget'ındaki kaydırılabilir bir kapsayıcıda görüntülenecek bir dizi öğe tanımlamanıza olanak sağlar.

Aşağıdaki snippet'ler, LazyColumn içindeki öğeleri tanımlamanın farklı yollarını gösterir.

Öğe sayısını sağlayabilirsiniz:

// Remember to import Glance Composables
// import androidx.glance.appwidget.layout.LazyColumn

LazyColumn {
    items(10) { index: Int ->
        Text(
            text = "Item $index",
            modifier = GlanceModifier.fillMaxWidth()
        )
    }
}

Öğeleri tek tek girin:

LazyColumn {
    item {
        Text("First Item")
    }
    item {
        Text("Second Item")
    }
}

Bir öğe listesi veya dizisi sağlayın:

LazyColumn {
    items(peopleNameList) { name ->
        Text(name)
    }
}

Yukarıdaki örneklerin bir kombinasyonunu da kullanabilirsiniz:

LazyColumn {
    item {
        Text("Names:")
    }
    items(peopleNameList) { name ->
        Text(name)
    }

    // or in case you need the index:
    itemsIndexed(peopleNameList) { index, person ->
        Text("$person at index $index")
    }
}

Önceki snippet'in itemId değerini belirtmediğini unutmayın. itemId öğesinin belirtilmesi, performansı iyileştirmeye ve Android 12 ve sonraki sürümlerden itibaren appWidget güncellemelerine (örneğin, listeye öğe eklerken veya listeden öğe çıkarırken) ve listede kaydırma konumunun korunmasına yardımcı olur. Aşağıdaki örnekte itemId öğesinin nasıl belirtileceği gösterilmektedir:

items(items = peopleList, key = { person -> person.id }) { person ->
    Text(person.name)
}

SizeMode tanımlayın

AppWidget boyutları cihaza, kullanıcının tercihine veya başlatıcıya bağlı olarak farklılık gösterebilir. Bu nedenle, Esnek widget düzenleri sağlama sayfasında açıklandığı gibi esnek düzenler sağlamanız önemlidir. Glance, SizeMode tanımı ve LocalSize değeri ile bu işlemi basitleştirir. Aşağıdaki bölümlerde üç mod açıklanmaktadır.

SizeMode.Single

SizeMode.Single varsayılan moddur. Yalnızca bir içerik türünün sağlandığını, yani AppWidget kullanılabilir boyut değişse bile içerik boyutunun değişmediğini gösterir.

class MyAppWidget : GlanceAppWidget() {

    override val sizeMode = SizeMode.Single

    override suspend fun provideGlance(context: Context, id: GlanceId) {
        // ...

        provideContent {
            MyContent()
        }
    }

    @Composable
    private fun MyContent() {
        // Size will be the minimum size or resizable
        // size defined in the App Widget metadata
        val size = LocalSize.current
        // ...
    }
}

Bu modu kullanırken şunlardan emin olun:

  • Minimum ve maksimum boyut meta veri değerleri, içerik boyutuna göre doğru şekilde tanımlanır.
  • İçerik, beklenen boyut aralığında yeterince esnek olmalıdır.

Genel olarak bu modu aşağıdaki durumlardan birinde kullanmalısınız:

a) AppWidget sabit boyuttaysa veya b) yeniden boyutlandırıldığında içeriğini değiştirmiyor.

SizeMode.Responsive

Bu mod, duyarlı düzenler sağlama ile eşdeğerdir. Bu mod, GlanceAppWidget öğesinin belirli boyutlarla sınırlanmış bir dizi duyarlı düzen tanımlamasına olanak tanır. Tanımlanan her boyut için içerik oluşturulur ve AppWidget oluşturulduğunda veya güncellendiğinde belirli boyutla eşlenir. Daha sonra sistem, mevcut boyuta göre en uygun boyutu seçer.

Örneğin, AppWidget hedefimizde üç boyut ve içeriğini tanımlayabilirsiniz:

class MyAppWidget : GlanceAppWidget() {

    companion object {
        private val SMALL_SQUARE = DpSize(100.dp, 100.dp)
        private val HORIZONTAL_RECTANGLE = DpSize(250.dp, 100.dp)
        private val BIG_SQUARE = DpSize(250.dp, 250.dp)
    }

    override val sizeMode = SizeMode.Responsive(
        setOf(
            SMALL_SQUARE,
            HORIZONTAL_RECTANGLE,
            BIG_SQUARE
        )
    )

    override suspend fun provideGlance(context: Context, id: GlanceId) {
        // ...

        provideContent {
            MyContent()
        }
    }

    @Composable
    private fun MyContent() {
        // Size will be one of the sizes defined above.
        val size = LocalSize.current
        Column {
            if (size.height >= BIG_SQUARE.height) {
                Text(text = "Where to?", modifier = GlanceModifier.padding(12.dp))
            }
            Row(horizontalAlignment = Alignment.CenterHorizontally) {
                Button()
                Button()
                if (size.width >= HORIZONTAL_RECTANGLE.width) {
                    Button("School")
                }
            }
            if (size.height >= BIG_SQUARE.height) {
                Text(text = "provided by X")
            }
        }
    }
}

Önceki örnekte, provideContent yöntemi üç kez çağrılır ve tanımlanan boyutla eşlenir.

  • İlk çağrıda boyut 100x100 olarak değerlendirilir. İçerik ekstra düğme veya üst ve alt metinler içermez.
  • İkinci çağrıda, boyut 250x100 olarak değerlendirilir. İçerikte ekstra düğme bulunur, ancak üst ve alt metinler içerikte yer almaz.
  • Üçüncü çağrıda boyut 250x250 olarak değerlendirilir. İçerikte ekstra düğme ve her iki metin de bulunuyor.

SizeMode.Responsive, diğer iki modun kombinasyonudur ve önceden tanımlanmış sınırlar dahilinde duyarlı içerik tanımlayabilmenizi sağlar. Genel olarak bu mod, AppWidget yeniden boyutlandırıldığında daha iyi performans gösterir ve daha yumuşak geçişler sağlar.

Aşağıdaki tabloda, SizeMode ve AppWidget kullanılabilir boyuta bağlı olarak boyut değeri gösterilmektedir:

Kullanılabilir boyut 105 x 110 203 x 112 72 x 72 203 x 150
SizeMode.Single 110 x 110 110 x 110 110 x 110 110 x 110
SizeMode.Exact 105 x 110 203 x 112 72 x 72 203 x 150
SizeMode.Responsive 80 x 100 80 x 100 80 x 100 150 x 120
* Tam değerler yalnızca demo amaçlıdır.

SizeMode.Exact

SizeMode.Exact, tam düzenler sağlama ile eşdeğerdir. Bu işlem, mevcut AppWidget boyutu her değiştiğinde (örneğin, kullanıcı ana ekranda AppWidget öğesini yeniden boyutlandırdığında) GlanceAppWidget içeriğini ister.

Örneğin, hedef widget'ta, kullanılabilen genişlik belirli bir değerden büyükse fazladan bir düğme eklenebilir.

class MyAppWidget : GlanceAppWidget() {

    override val sizeMode = SizeMode.Exact

    override suspend fun provideGlance(context: Context, id: GlanceId) {
        // ...

        provideContent {
            MyContent()
        }
    }

    @Composable
    private fun MyContent() {
        // Size will be the size of the AppWidget
        val size = LocalSize.current
        Column {
            Text(text = "Where to?", modifier = GlanceModifier.padding(12.dp))
            Row(horizontalAlignment = Alignment.CenterHorizontally) {
                Button()
                Button()
                if (size.width > 250.dp) {
                    Button("School")
                }
            }
        }
    }
}

Bu mod diğerlerinden daha fazla esneklik sağlasa da dikkat edilmesi gereken birkaç nokta vardır:

  • Boyut her değiştiğinde AppWidget tamamen yeniden oluşturulmalıdır. Bu durum, içerik karmaşık olduğunda performans sorunlarına ve kullanıcı arayüzünde atlamalara yol açabilir.
  • Kullanılabilir boyut, başlatıcının uygulamasına bağlı olarak farklılık gösterebilir. Örneğin, başlatıcı boyut listesi sağlamazsa mümkün olan minimum boyut kullanılır.
  • Boyut hesaplama mantığı, Android 12 öncesi cihazlarda her durumda çalışmayabilir.

Genel olarak bu modu, SizeMode.Responsive kullanılamıyorsa (yani küçük bir duyarlı düzen grubu uygun değilse) kullanmalısınız.

Kaynaklara erişin

Android kaynaklarına erişmek için aşağıdaki örnekte gösterildiği gibi LocalContext.current kullanın:

LocalContext.current.getString(R.string.glance_title)

Nihai RemoteViews nesnesinin boyutunu küçültmek ve dinamik renkler gibi dinamik kaynakları etkinleştirmek için doğrudan kaynak kimlikleri sağlamanızı öneririz.

Oluşturulabilirler ve yöntemler, ImageProvider gibi bir "sağlayıcı" veya GlanceModifier.background(R.color.blue) gibi bir aşırı yükleme yöntemi kullanan kaynakları kabul eder. Örnek:

Column(
    modifier = GlanceModifier.background(R.color.default_widget_background)
) { /**...*/ }

Image(
    provider = ImageProvider(R.drawable.ic_logo),
    contentDescription = "My image",
)

Herkese açık kullanıcı adı metni

Glance 1.1.0, metin stillerinizi ayarlayabileceğiniz bir API içerir. TextStyle sınıfının fontSize, fontWeight veya fontFamily özelliklerini kullanarak metin stillerini ayarlayın.

fontFamily, aşağıdaki örnekte gösterildiği gibi tüm sistem yazı tiplerini destekler, ancak uygulamalarda özel yazı tipleri desteklenmez:

Text(
    style = TextStyle(
        fontWeight = FontWeight.Bold,
        fontSize = 18.sp,
        fontFamily = FontFamily.Monospace
    ),
    text = "Example Text"
)

Bileşik düğme ekle

Bileşik düğmeler Android 12'de kullanıma sunuldu. Bir Bakışta, aşağıdaki bileşik düğme türleri için geriye dönük uyumluluğu destekler:

Bu birleşik düğmelerin her biri, "işaretli" durumunu temsil eden tıklanabilir bir görünüm gösterir.

var isApplesChecked by remember { mutableStateOf(false) }
var isEnabledSwitched by remember { mutableStateOf(false) }
var isRadioChecked by remember { mutableStateOf(0) }

CheckBox(
    checked = isApplesChecked,
    onCheckedChange = { isApplesChecked = !isApplesChecked },
    text = "Apples"
)

Switch(
    checked = isEnabledSwitched,
    onCheckedChange = { isEnabledSwitched = !isEnabledSwitched },
    text = "Enabled"
)

RadioButton(
    checked = isRadioChecked == 1,
    onClick = { isRadioChecked = 1 },
    text = "Checked"
)

Durum değiştiğinde, sağlanan lambda tetiklenir. Kontrol durumunu aşağıdaki örnekte gösterildiği gibi depolayabilirsiniz:

class MyAppWidget : GlanceAppWidget() {

    override suspend fun provideGlance(context: Context, id: GlanceId) {
        val myRepository = MyRepository.getInstance()

        provideContent {
            val scope = rememberCoroutineScope()

            val saveApple: (Boolean) -> Unit =
                { scope.launch { myRepository.saveApple(it) } }
            MyContent(saveApple)
        }
    }

    @Composable
    private fun MyContent(saveApple: (Boolean) -> Unit) {

        var isAppleChecked by remember { mutableStateOf(false) }

        Button(
            text = "Save",
            onClick = { saveApple(isAppleChecked) }
        )
    }
}

CheckBox, Switch ve RadioButton renklerini özelleştirmek için colors özelliğini de kullanabilirsiniz:

CheckBox(
    // ...
    colors = CheckboxDefaults.colors(
        checkedColor = ColorProvider(day = colorAccentDay, night = colorAccentNight),
        uncheckedColor = ColorProvider(day = Color.DarkGray, night = Color.LightGray)
    ),
    checked = isChecked,
    onCheckedChange = { isChecked = !isChecked }
)

Switch(
    // ...
    colors = SwitchDefaults.colors(
        checkedThumbColor = ColorProvider(day = Color.Red, night = Color.Cyan),
        uncheckedThumbColor = ColorProvider(day = Color.Green, night = Color.Magenta),
        checkedTrackColor = ColorProvider(day = Color.Blue, night = Color.Yellow),
        uncheckedTrackColor = ColorProvider(day = Color.Magenta, night = Color.Green)
    ),
    checked = isChecked,
    onCheckedChange = { isChecked = !isChecked },
    text = "Enabled"
)

RadioButton(
    // ...
    colors = RadioButtonDefaults.colors(
        checkedColor = ColorProvider(day = Color.Cyan, night = Color.Yellow),
        uncheckedColor = ColorProvider(day = Color.Red, night = Color.Blue)
    ),

)

Ek bileşenler

Glance 1.1.0, aşağıdaki tabloda açıklandığı gibi ek bileşenlerin kullanıma sunulmasını içerir:

Ad Resim Referans bağlantısı Ek notlar
Doldurulmuş Düğme alternatif_metin Bileşen
Dış Çizgi Düğmeler alternatif_metin Bileşen
Simge Düğmeleri alternatif_metin Bileşen Birincil / İkincil / Yalnızca simge
Başlık Çubuğu alternatif_metin Bileşen
İskele İskele ve Başlık çubuğu aynı demodadır.

Tasarım ayrıntıları hakkında daha fazla bilgi için Figma'daki bu tasarım kitinde bileşen tasarımlarına bakın.