توضّح هذه الصفحة كيفية التعامل مع الأحجام وتوفير تصميمات مرنة وسريعة الاستجابة من خلال ميزة "نظرة سريعة" باستخدام مكوّنات ميزة "نظرة سريعة" الحالية.
استخدام 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
بدءًا من الإصدار 12 من نظام التشغيل Android والإصدارات الأحدث (على سبيل المثال، عند إضافة عناصر أو إزالتها من القائمة). يوضّح المثال التالي
كيفية تحديد itemId
:
items(items = peopleList, key = { person -> person.id }) { person -> Text(person.name) }
تحديد SizeMode
قد تختلف أحجام 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 × 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 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"
)
إضافة أزرار مركبة
تم تقديم الأزرار المركّبة في الإصدار 12 من نظام التشغيل Android. تدعم ميزة "نظرة سريعة" التوافق مع الإصدارات العكسية للأنواع التالية من الأزرار المركبة:
يعرض كل من هذه الأزرار المركّبة عرضًا قابلاً للنقر يمثل الحالة "محدد".
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.