توضّح هذه الصفحة كيفية التعامل مع الأحجام وتوفير تنسيقات مرنة وسلسة باستخدام Glance، وذلك باستخدام مكوّنات Glance الحالية.
استخدِم Box
وColumn
وRow
.
تتضمّن ميزة "نظرة سريعة" ثلاثة تنسيقات رئيسية قابلة للتجميع:
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, key = { person -> person.id }) { person -> Text(person.name) }
تحديد SizeMode
قد تختلف أحجام AppWidget
حسب الجهاز أو اختيار المستخدم أو مشغّل التطبيقات،
لذلك من المهم توفير تنسيقات مرنة كما هو موضّح في صفحة توفير AppWidget
تنسيقات مرنة للتطبيقات المصغّرة. تعمل ميزة "نظرة سريعة" على تبسيط ذلك من خلال تعريف 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 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 |
* القيم الدقيقة مخصّصة لأغراض العرض التجريبي فقط. |
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 { 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" )
عند تغيير الحالة، يتم تفعيل دالة 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.
لمزيد من المعلومات حول التنسيقات الأساسية، يُرجى الانتقال إلى مقالة تنسيقات التطبيقات المصغّرة الأساسية.