توضّح هذه الصفحة كيفية التعامل مع الأحجام وتوفير تنسيقات مرنة ومتجاوبة باستخدام Glance، وذلك باستخدام مكوّنات Glance الحالية.
استخدِم Box وColumn وRow.
تتضمّن Glance ثلاثة تصاميم رئيسية قابلة للإنشاء:
Box: يضع العناصر فوق بعضها البعض. ويتم تحويلها إلىRelativeLayout.
Column: تضع العناصر الواحد تلو الآخر على المحور العمودي. ويتم تحويلها إلىLinearLayoutباتجاه عمودي.
Row: تضع العناصر بجانب بعضها البعض على المحور الأفقي. ويتم تحويلها إلىLinearLayoutباتجاه أفقي.
تتوافق ميزة "نظرة سريعة" مع عناصر 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 أو أعلى، لأنّ ميزة "التمرير السريع" متاحة على الأجهزة التي تعمل بالإصدار Android 17 والإصدارات الأحدث. - ضبط إعدادات
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 // ... } }
عند استخدام هذا الوضع، تأكَّد مما يلي:
- يتم تحديد قيم البيانات الوصفية الخاصة بالحد الأدنى والأقصى للحجم بشكل صحيح استنادًا إلى حجم المحتوى.
- المحتوى مرن بما يكفي ضمن نطاق الحجم المتوقّع.
بشكل عام، يجب استخدام هذا الوضع في إحدى الحالتين التاليتين:
أ) أن يكون حجم 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 x 112 | 72 × 72 | 203 × 150 |
|---|---|---|---|---|
SizeMode.Single |
110 × 110 | 110 × 110 | 110 × 110 | 110 × 110 |
SizeMode.Exact |
105 × 110 | 203 x 112 | 72 × 72 | 203 × 150 |
SizeMode.Responsive |
80 × 100 | 80 × 100 | 80 × 100 | 150 x 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 الأقدم من 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", )
معالجة النص
يتضمّن الإصدار 1.1.0 من Glance واجهة برمجة تطبيقات لضبط أنماط النصوص. اضبط أنماط النص باستخدام السمات 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) ), )
المكوّنات الإضافية
يتضمّن الإصدار 1.1.0 من Glance إصدار مكوّنات إضافية، كما هو موضّح في الجدول التالي:
| الاسم | صورة | رابط المرجع | ملاحظات إضافية |
|---|---|---|---|
| زر التعبئة |
|
المكوّن | |
| أزرار محدَّدة الجوانب |
|
المكوّن | |
| أزرار الرموز |
|
المكوّن | أساسي / ثانوي / رمز فقط |
| شريط العنوان |
|
المكوّن | |
| سقالة | يظهر كلّ من "اللوحة الأساسية" و"شريط العنوان" في العرض التوضيحي نفسه. |
لمزيد من المعلومات حول تفاصيل التصميم، اطّلِع على تصاميم المكوّنات في مجموعة التصميم هذه على Figma.
لمزيد من المعلومات حول التنسيقات الأساسية، يُرجى الانتقال إلى تنسيقات الأدوات الأساسية.