تصف هذه الصفحة كيفية التعامل مع الأحجام وتوفير تنسيقات مرنة ومتجاوبة باستخدام Glance، وذلك باستخدام مكوّنات Glance الحالية.
استخدام Box وColumn وRow
يتضمّن Glance ثلاثة تنسيقات رئيسية قابلة للإنشاء:
Box: يضع العناصر فوق بعضها البعض. ويتم تحويله إلىRelativeLayout.Column: يضع العناصر بعد بعضها البعض على المحور العمودي. ويتم تحويله إلىLinearLayoutمع اتجاه عمودي.Row: يضع العناصر بعد بعضها البعض على المحور الأفقي. ويتم تحويله إلىLinearLayoutمع اتجاه أفقي.
يتيح Glance استخدام كائنات Scaffold. ضَع العناصر القابلة للإنشاء Column وRow وBox ضمن كائن Scaffold معيّن.
تتيح لك كلّ من هذه العناصر القابلة للإنشاء تحديد المحاذاة العمودية والأفقية لمحتواها وقيود العرض أو الارتفاع أو الوزن أو المساحة المتروكة باستخدام المعدِّلات. بالإضافة إلى ذلك، يمكن لكل عنصر ثانوي تحديد المعدِّل الخاص به لتغيير المساحة والموضع داخل العنصر الرئيسي.
يوضّح لك المثال التالي كيفية إنشاء Row يوزّع العناصر الثانوية بالتساوي أفقيًا، كما هو موضّح في الشكل 1:
Row(modifier = GlanceModifier.fillMaxWidth().padding(16.dp)) { val modifier = GlanceModifier.defaultWeight() Text("first", modifier) Text("second", modifier) Text("third", modifier) }
يملأ Row الحد الأقصى للعرض المتاح، وبما أنّ كل عنصر ثانوي له الوزن نفسه، فإنّه يشارك المساحة المتاحة بالتساوي. يمكنك تحديد أوزان أو أحجام أو مساحات متروكة أو محاذاة مختلفة لتكييف التنسيقات مع احتياجاتك.
استخدام تنسيقات قابلة للتمرير
هناك طريقة أخرى لتوفير محتوى متجاوب وهي جعله قابلاً للتمرير. ويكون ذلك ممكنًا باستخدام الدالة المركّبة LazyColumn. يتيح لك هذا العنصر القابل للإنشاء تحديد مجموعة من العناصر التي سيتم عرضها داخل حاوية قابلة للتمرير في أداة التطبيق.
توضّح مقتطفات الرموز التالية طرقًا مختلفة لتحديد العناصر داخل LazyColumn.
يمكنك تقديم عدد العناصر:
// Remember to import Glance Composables // import androidx.glance.appwidget.layout.LazyColumn LazyColumn { items(10) { index: Int -> Text( text = "Item $index", modifier = GlanceModifier.fillMaxWidth() ) } }
تقديم عناصر فردية:
LazyColumn { item { Text("First Item") } item { Text("Second Item") } }
تقديم قائمة أو مصفوفة من العناصر:
LazyColumn { items(peopleNameList) { name -> Text(name) } }
يمكنك أيضًا استخدام مجموعة من الأمثلة السابقة:
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") } }
يُرجى العِلم أنّ المقتطف السابق لا يحدّد itemId. يساعد تحديد itemId في تحسين الأداء والحفاظ على موضع التمرير من خلال تحديثات القائمة وappWidget بدءًا من Android 12 (على سبيل المثال، عند إضافة عناصر إلى القائمة أو إزالتها). يوضّح المثال التالي كيفية تحديد itemId:
items(items = peopleList, itemId = { person -> person.id.hashCode().toLong() }) { person -> Text(person.name) }
التمرير السريع
التمرير السريع هو رسم متحرك يسمح للمحتوى القابل للتمرير بالمحاذاة إلى أعلى حاوية أداة التطبيق.
لتطبيق ميزة التمرير السريع، تأكَّد من استيفاء الشروط التالية:
- يجب تحديث تبعية Glance إلى الإصدار 1.3.0-alpha02 أو إصدار أحدث.
- يجب ضبط
compileSdkعلى 37 أو إصدار أحدث، لأنّ ميزة "التمرير السريع" متاحة على الأجهزة التي تعمل بالإصدار 17 من Android والإصدارات الأحدث. - يجب ضبط
LazyColumnباستخدامVerticalScrollMode. إذا كان الجهاز يتيح ميزة "التمرير السريع"، استخدِمSnapScrollMatchHeight. وإلا، استخدِمNormal.
@Composable fun SnapScrollLayout() { val height = LocalSize.current.height val items = listOf( ColorItem(Color.Red, "Red"), ColorItem(Color.Yellow, "Yellow"), ColorItem(Color.Blue, "Blue") ) val scrollMode = if (Build.VERSION.SDK_INT >= 37) { VerticalScrollMode.SnapScrollMatchHeight(height) } else { VerticalScrollMode.Normal } LazyColumn( verticalScrollMode = scrollMode ) { items(items) { item -> ColorCard(item, height) } } } @Composable private fun ColorCard(item: ColorItem, height: Dp) { Box( modifier = GlanceModifier .background(item.color) .fillMaxWidth() .height(height), contentAlignment = Alignment.Center ) { Text( text = item.name, modifier = GlanceModifier.background(Color.White) ) } }
تحديد SizeMode
قد تختلف أحجام AppWidget حسب الجهاز أو اختيار المستخدم أو مشغّل التطبيقات،
لذا من المهم توفير تنسيقات مرنة كما هو موضّح في صفحة توفير
تنسيقات مرنة لأدوات التطبيقات. يبسّط Glance ذلك من خلال تعريف SizeMode وقيمة LocalSize. تصف الأقسام التالية الأوضاع الثلاثة.
SizeMode.Single
SizeMode.Single هو الوضع التلقائي. يشير إلى أنّه يتم توفير نوع واحد فقط من المحتوى، أي أنّه حتى إذا تغيّر حجم AppWidget المتاح، لا يتغيّر حجم المحتوى.
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 // ... } }
عند استخدام هذا الوضع، تأكَّد مما يلي:
- يجب تحديد قيم البيانات الوصفية للحد الأدنى والحد الأقصى للحجم metadata values بشكلٍ صحيح استنادًا إلى حجم المحتوى.
- يجب أن يكون المحتوى مرنًا بما يكفي ضمن نطاق الحجم المتوقّع.
بشكلٍ عام، يجب استخدام هذا الوضع في الحالات التالية:
أ) إذا كان AppWidget له حجم ثابت، أو ب) إذا لم يغيّر محتواه عند تغيير حجمه.
SizeMode.Responsive
هذا الوضع يعادل توفير تنسيقات متجاوبة، ما يسمح لـ
GlanceAppWidget بتحديد مجموعة من التنسيقات المتجاوبة التي تحدّها أحجام معيّنة. لكل حجم محدّد، يتم إنشاء المحتوى وربطه بالحجم المحدّد عند إنشاء AppWidget أو تعديله. ثم يختار النظام التنسيق الأنسب استنادًا إلى الحجم المتاح.
على سبيل المثال، في AppWidget الوجهة، يمكنك تحديد ثلاثة أحجام ومحتواها:
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") } } } }
في المثال السابق، يتم استدعاء طريقة provideContent ثلاث مرات ويتم ربطها بالحجم المحدّد.
- في الاستدعاء الأول، يتم تقييم الحجم على أنّه
100x100. لا يتضمّن المحتوى الزر الإضافي ولا النصوص العلوية والسفلية. - في الاستدعاء الثاني، يتم تقييم الحجم على أنّه
250x100. يتضمّن المحتوى الزر الإضافي، ولكن ليس النصوص العلوية والسفلية. - في الاستدعاء الثالث، يتم تقييم الحجم على أنّه
250x250. يتضمّن المحتوى الزر الإضافي وكلا النصَين.
SizeMode.Responsive هو مزيج من الوضعَين الآخرَين، ويسمح لك بتحديد محتوى متجاوب ضمن حدود محدّدة مسبقًا. بشكلٍ عام، يكون أداء هذا الوضع أفضل ويسمح بانتقالات أكثر سلاسة عند تغيير حجم AppWidget.
يوضّح الجدول التالي قيمة الحجم، استنادًا إلى SizeMode وحجم AppWidget المتاح:
| الحجم المتاح | 105 × 110 | 203 × 112 | 72 × 72 | 203 × 150 |
|---|---|---|---|---|
SizeMode.Single |
110 × 110 | 110 × 110 | 110 × 110 | 110 × 110 |
SizeMode.Exact |
105 × 110 | 203 × 112 | 72 × 72 | 203 × 150 |
SizeMode.Responsive |
80 × 100 | 80 × 100 | 80 × 100 | 150 × 120 |
| * القيم الدقيقة هي لأغراض العرض التوضيحي فقط. |
SizeMode.Exact
SizeMode.Exact يعادل توفير تنسيقات دقيقة، ما
يطلب محتوى GlanceAppWidget في كل مرة يتغيّر فيها حجم AppWidget المتاح (على سبيل المثال، عندما يغيّر المستخدم حجم AppWidget على الشاشة الرئيسية).
على سبيل المثال، في أداة الوجهة، يمكن إضافة زر إضافي إذا كان العرض المتاح أكبر من قيمة معيّنة.
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") } } } } }
يوفّر هذا الوضع مرونة أكبر من الأوضاع الأخرى، ولكنّه يتضمّن بعض المحاذير:
- يجب إعادة إنشاء
AppWidgetبالكامل في كل مرة يتغيّر فيها الحجم. قد يؤدي ذلك إلى حدوث مشاكل في الأداء وتغييرات مفاجئة في واجهة المستخدم عندما يكون المحتوى معقدًا. - قد يختلف الحجم المتاح حسب تنفيذ مشغّل التطبيقات. على سبيل المثال، إذا لم يقدّم مشغّل التطبيقات قائمة الأحجام، يتم استخدام الحد الأدنى للحجم الممكن.
- في الأجهزة التي تعمل بإصدارات Android قبل Android 12، قد لا تعمل منطق حساب الحجم في جميع الحالات.
بشكلٍ عام، يجب استخدام هذا الوضع إذا تعذّر استخدام SizeMode.Responsive (أي إذا لم يكن من الممكن استخدام مجموعة صغيرة من التنسيقات المتجاوبة).
الوصول إلى الموارد
استخدِم LocalContext.current للوصول إلى أي مورد من موارد Android، كما هو موضّح في المثال التالي:
LocalContext.current.getString(R.string.glance_title)
ننصحك بتقديم أرقام تعريف الموارد مباشرةً لتقليل حجم الكائن النهائي
RemoteViews ولتفعيل الموارد الديناميكية، مثل الألوان
الديناميكية.
تقبل العناصر القابلة للإنشاء والطرق الموارد باستخدام "مزوّد"، مثل ImageProvider، أو باستخدام طريقة التحميل الزائد مثل GlanceModifier.background(R.color.blue). على سبيل المثال:
Column( modifier = GlanceModifier.background(R.color.default_widget_background) ) { /**...*/ } Image( provider = ImageProvider(R.drawable.ic_logo), contentDescription = "My image", )
التعامل مع النص
يتضمّن Glance 1.1.0 واجهة برمجة تطبيقات لضبط أنماط النص. يمكنك ضبط أنماط النص باستخدام سمات fontSize أو fontWeight أو fontFamily لفئة TextStyle.
تتيح fontFamily استخدام جميع خطوط النظام، كما هو موضّح في المثال التالي، ولكن لا تتيح استخدام الخطوط المخصّصة في التطبيقات:
Text(
style = TextStyle(
fontWeight = FontWeight.Bold,
fontSize = 18.sp,
fontFamily = FontFamily.Monospace
),
text = "Example Text"
)
إضافة أزرار مركّبة
تم تقديم الأزرار المركّبة في Android 12. يتيح Glance التوافق مع الإصدارات السابقة لأنواع الأزرار المركّبة التالية:
تعرض كلّ من هذه الأزرار المركّبة طريقة عرض قابلة للنقر تمثّل الحالة "تم وضع علامة".
var isApplesChecked by remember { mutableStateOf(false) } var isEnabledSwitched by remember { mutableStateOf(false) } var isRadioChecked by remember { mutableIntStateOf(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" )
عندما تتغيّر الحالة، يتم تفعيل تعبير lambda المقدَّم. يمكنك تخزين الحالة "تم وضع علامة"، كما هو موضّح في المثال التالي:
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) } ) } }
يمكنك أيضًا تقديم سمة colors إلى CheckBox وSwitch وRadioButton لتخصيص ألوانها:
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) ), )
مكوّنات إضافية
يتضمّن Glance 1.1.0 إصدار مكوّنات إضافية، كما هو موضّح في الجدول التالي:
| الاسم | صورة | رابط مرجعي | ملاحظات إضافية |
|---|---|---|---|
| زر التعبئة |
|
المكوّن | |
| أزرار Outline |
|
المكوّن | |
| أزرار الرموز |
|
المكوّن | أساسي / ثانوي / رمز فقط |
| شريط العنوان |
|
المكوّن | |
| Scaffold | يظهر كلّ من Scaffold وشريط العنوان في العرض التوضيحي نفسه. |
لمزيد من المعلومات حول تفاصيل التصميم، اطّلِع على تصميمات المكوّنات في مجموعة التصميم هذه على Figma.
لمزيد من المعلومات حول التنسيقات الأساسية، يُرجى الانتقال إلى صفحة التنسيقات الأساسية لأدوات التطبيقات.