이 페이지에서는 기존 Glance 구성요소를 사용하여 크기를 처리하고 Glance로 유연하고 반응이 빠른 레이아웃을 제공하는 방법을 설명합니다.
Box
, Column
, Row
사용
Glance에는 세 가지 기본 컴포저블 레이아웃이 있습니다.
Box
: 요소를 다른 요소 위에 배치합니다.RelativeLayout
로 변환됩니다.Column
: 세로축에 요소를 차례로 배치합니다. 세로 방향의LinearLayout
로 변환됩니다.Row
: 요소를 가로축에 나란히 배치합니다. 가로 방향의LinearLayout
로 변환됩니다.
Glance는 Scaffold
객체를 지원합니다. 지정된 Scaffold
객체 내에 Column
, Row
, Box
컴포저블을 배치합니다.
이러한 각 컴포저블을 사용하면 수정자를 사용하여 콘텐츠의 세로 및 가로 정렬과 너비, 높이, 가중치 또는 패딩 제약 조건을 정의할 수 있습니다. 또한 각 하위 요소는 수정자를 정의하여 상위 요소 내의 공간과 배치를 변경할 수 있습니다.
다음 예에서는 그림 1과 같이 하위 요소를 수평으로 균등하게 분산하는 Row
를 만드는 방법을 보여줍니다.
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
를 지정하면 Android 12 이상에서 목록 및 appWidget
업데이트를 통해 성능을 개선하고 스크롤 위치를 유지하는 데 도움이 됩니다 (예: 목록에서 항목을 추가하거나 삭제할 때). 다음 예는 itemId
를 지정하는 방법을 보여줍니다.
items(items = peopleList, key = { person -> person.id }) { person -> Text(person.name) }
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 // ... } }
이 모드를 사용할 때는 다음 사항을 확인하세요.
- 최소 및 최대 크기 메타데이터 값이 콘텐츠 크기를 기반으로 올바르게 정의됩니다.
- 콘텐츠가 예상 크기 범위 내에서 충분히 유연합니다.
일반적으로 다음과 같은 경우에 이 모드를 사용해야 합니다.
a) AppWidget
의 크기가 고정되어 있거나 b) 크기를 조절해도 콘텐츠가 변경되지 않습니다.
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
는 정확한 레이아웃을 제공하는 것과 동일하며, 사용 가능한 AppWidget
크기가 변경될 때마다 (예: 사용자가 홈 화면에서 AppWidget
크기를 조절할 때) GlanceAppWidget
콘텐츠를 요청합니다.
예를 들어 대상 위젯에서 사용 가능한 너비가 특정 값보다 큰 경우 추가 버튼을 추가할 수 있습니다.
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
를 완전히 다시 만들어야 합니다. 이로 인해 콘텐츠가 복잡한 경우 성능 문제가 발생하고 UI가 갑자기 튀어오를 수 있습니다. - 사용 가능한 크기는 런처의 구현에 따라 다를 수 있습니다. 예를 들어 런처가 크기 목록을 제공하지 않으면 가능한 최소 크기가 사용됩니다.
- Android 12 이전 기기에서는 크기 계산 로직이 일부 상황에서 작동하지 않을 수 있습니다.
일반적으로 SizeMode.Responsive
를 사용할 수 없는 경우(즉, 반응형 레이아웃의 작은 세트를 실행할 수 없는 경우) 이 모드를 사용해야 합니다.
리소스 액세스
다음 예와 같이 LocalContext.current
를 사용하여 Android 리소스에 액세스합니다.
LocalContext.current.getString(R.string.glance_title)
최종 RemoteViews
객체의 크기를 줄이고 동적 색상과 같은 동적 리소스를 사용 설정하려면 리소스 ID를 직접 제공하는 것이 좋습니다.
컴포저블과 메서드는 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에는 텍스트 스타일을 설정하는 API가 포함되어 있습니다. TextStyle 클래스의 fontSize
, fontWeight
또는 fontFamily
속성을 사용하여 텍스트 스타일을 설정합니다.
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" )
상태가 변경되면 제공된 람다가 트리거됩니다. 다음 예와 같이 확인 상태를 저장할 수 있습니다.
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) } ) } }
CheckBox
, Switch
, RadioButton
에 colors
속성을 제공하여 색상을 맞춤설정할 수도 있습니다.
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에는 다음 표에 설명된 대로 추가 구성요소가 포함되어 있습니다.
이름 | 이미지 | 참조 링크 | 추가 참고사항 |
---|---|---|---|
채워진 버튼 | 구성요소 | ||
윤곽선 버튼 | 구성요소 | ||
아이콘 버튼 | 구성요소 | 기본 / 보조 / 아이콘만 | |
제목 표시줄 | 구성요소 | ||
Scaffold | 스캐폴드와 제목 표시줄이 동일한 데모에 있습니다. |
디자인 세부정보에 관한 자세한 내용은 Figma의 이 디자인 키트에 있는 구성요소 디자인을 참고하세요.
표준 레이아웃에 관한 자세한 내용은 표준 위젯 레이아웃을 참고하세요.