이 페이지에서는 기존 Glance 구성요소를 사용하여 Glance로 크기를 처리하고 유연한 반응형 레이아웃을 제공하는 방법을 설명합니다.
Box
, Column
, Row
사용
Glance에는 세 가지 기본 컴포저블 레이아웃이 있습니다.
Box
: 요소를 다른 요소 위에 배치합니다. 이는RelativeLayout
로 변환됩니다.Column
: 세로축에서 요소를 나란히 배치합니다. 세로 방향의LinearLayout
로 변환됩니다.Row
: 가로축에서 요소를 서로 뒤에 배치합니다. 가로 방향의LinearLayout
로 변환됩니다.
Glance는 Scaffold
객체를 지원합니다. 지정된 Scaffold
객체 내에 Column
, Row
, Box
컴포저블을 배치합니다.
![열, 행, 상자 레이아웃 이미지](https://developer.android.google.cn/static/develop/ui/compose/images/column_row_box.png?authuser=1&hl=ko)
이러한 각 컴포저블을 사용하면 수정자를 사용하여 콘텐츠의 세로 및 가로 정렬과 너비, 높이, 두께 또는 패딩 제약 조건을 정의할 수 있습니다. 또한 각 하위 요소는 수정자를 정의하여 상위 요소 내의 공간과 배치를 변경할 수 있습니다.
다음 예에서는 그림 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) } ) } }
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에는 다음 표에 설명된 대로 추가 구성요소의 출시가 포함되어 있습니다.
이름 | 이미지 | 참조 링크 | 추가 참고사항 |
---|---|---|---|
채워진 버튼 |
![]() |
구성요소 | |
윤곽선 버튼 |
![]() |
구성요소 | |
아이콘 버튼 |
![]() |
구성요소 | 기본 / 보조 / 아이콘만 |
제목 표시줄 |
![]() |
구성요소 | |
Scaffold | Scaffold와 제목 표시줄이 같은 데모에 있습니다. |
디자인 세부사항에 관한 자세한 내용은 Figma의 이 디자인 키트에 있는 구성요소 디자인을 참고하세요.