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

Bu sayfada, mevcut Glance bileşenlerini kullanarak Glance ile boyutları nasıl yöneteceğiniz ve esnek ve duyarlı düzenler nasıl sağlayacağınız açıklanmaktadır.

Box, Column ve Row özelliğini kullanın

Bir bakışta üç temel birleştirilebilir düzen vardır:

  • Box: Öğeleri birbirinin üzerine yerleştirir. Bu, RelativeLayout olarak çevrilir.

  • Column: Öğeleri dikey eksende birbirinin üzerine yerleştirir. Bu, dikey yöne sahip bir LinearLayout olarak çevrilir.

  • Row: Öğeleri yatay eksende birbirinin arkasına yerleştirir. Yatay yönde bir LinearLayout olarak çevrilir.

Bir Bakışta, Scaffold nesnelerini destekler. Column, Row ve Box bileşenlerinizi 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 bileşenlerin her biri, içeriklerinin dikey ve yatay hizalamalarını ve genişlik, yükseklik, ağırlık veya dolgu kısıtlamalarını değiştiricileri kullanarak tanımlamanıza olanak tanır. Ayrıca her alt öğe, ebeveyn öğe içindeki alanı ve yerleşimi değiştirmek için kendi değiştiricisini tanımlayabilir.

Aşağıdaki örnekte, alt öğelerini yatay olarak eşit şekilde dağıtan bir Row öğesinin nasıl oluşturulacağı gösterilmektedir (Şekil 1):

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

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

Kaydırılabilir düzenler kullanma

Duyarlı içerik sunmanın bir diğer yolu da içeriği kaydırılabilir hâle getirmektir. Bu, LazyColumn bileşeni ile mümkündür. Bu bileşen, uygulama widget'ında kaydırılabilir bir kapsayıcı içinde görüntülenecek bir öğe grubu tanımlamanıza olanak tanır.

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

Öğe sayısını belirtebilirsiniz:

// 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 sağlayın:

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

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

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

Önceki ö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'te itemId belirtilmediğini unutmayın. itemId belirtmek, Android 12 ve sonraki sürümlerde liste ve appWidget güncellemeleri sırasında performansı iyileştirmeye ve kaydırma konumunu korumaya yardımcı olur (örneğin, listede öğe eklerken veya listeden öğe çıkarırken). Aşağıdaki örnekte, itemId değerinin nasıl belirtileceği gösterilmektedir:

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

SizeMode değerini tanımlayın

AppWidget boyutları cihaza, kullanıcı seçimine 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. Bir Bakışta, SizeMode tanımı ve LocalSize değeriyle bunu basitleştirir. Aşağıdaki bölümlerde üç mod açıklanmaktadır.

SizeMode.Single

SizeMode.Single varsayılan moddur. Yalnızca bir tür içerik sağlandığını gösterir. Yani, AppWidget kullanılabilir boyutu değişse bile içerik boyutu değişmez.

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 aşağıdakilerden emin olun:

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

Genel olarak bu modu şu durumlarda kullanmalısınız:

a) AppWidget sabit boyuttadır veya b) yeniden boyutlandırıldığında içeriği değişmez.

SizeMode.Responsive

Bu mod, GlanceAppWidget'nin belirli boyutlarla sınırlı bir dizi duyarlı düzen tanımlamasına olanak tanıyan duyarlı düzenler sağlama ile aynıdır. Tanımlanmış her boyut için içerik oluşturulur ve AppWidget oluşturulduğunda veya güncellendiğinde belirli boyutla eşlenir. Ardından sistem, mevcut boyuta göre en uygun olanı seçer.

Örneğin, hedefimiz AppWidget'te üç 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. İçerikte ek düğme, üst ve alt metinler yer almıyor.
  • İkinci çağrıda boyut 250x100 olarak değerlendirilir. İçerikte ek düğme yer alıyor ancak üst ve alt metinler yer almıyor.
  • Üçüncü çağrıda boyut 250x250 olarak değerlendirilir. İçerikte ek düğme ve her iki metin de yer alır.

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

Aşağıdaki tabloda, SizeMode ve AppWidget kullanılabilir boyutuna bağlı olarak boyutun 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 sayfa düzenleri sağlamanın eşdeğeridir. Bu durumda, mevcut AppWidget boyutu her değiştiğinde (ör. kullanıcı ana ekrandaki AppWidget'i yeniden boyutlandırdığında) GlanceAppWidget içeriği istenir.

Örneğin, mevcut genişlik belirli bir değerden büyükse hedef widget'a ek 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ğlar ancak bazı dezavantajları vardır:

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

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

Kaynaklara erişim

Aşağıdaki örnekte gösterildiği gibi, herhangi bir Android kaynağına erişmek için LocalContext.current kullanın:

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

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

Birleştirilebilirler ve yöntemler, ImageProvider gibi bir "sağlayıcı" kullanarak veya GlanceModifier.background(R.color.blue) gibi bir aşırı yükleme yöntemi kullanarak 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 ayarlamak için 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 uygulamalardaki özel yazı tipleri desteklenmez:

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

Karmaşık düğmeler ekleme

Karma düğmeler Android 12'de kullanıma sunulmuştur. Glance, aşağıdaki karma düğme türleri için geriye dönük uyumluluğu destekler:

Bu birleşik düğmelerin her biri, "işaretli" durumu gösteren 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 saklayabilirsiniz:

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

Renklerini özelleştirmek için CheckBox, Switch ve RadioButton için colors özelliğini de sağlayabilirsiniz:

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 yayınlanmasını içerir:

Ad Resim Referans bağlantısı Ek notlar
Doldurulmuş düğme alt_text Bileşen
Dış Çizgili Düğmeler alt_text Bileşen
Simge düğmeleri alt_text Bileşen Birincil / İkincil / Yalnızca simge
Başlık çubuğu alt_text Bileşen
İskele İskelet 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 kitindeki bileşen tasarımlarına bakın.

Standart düzenler hakkında daha fazla bilgi için Standart widget düzenleri başlıklı makaleyi inceleyin.