![[Android, Kotlin] Jetpack Compose와 Glance로 간단한 위젯 구현(영상 & 코드 포함) [Android, Kotlin] Jetpack Compose와 Glance로 간단한 위젯 구현(영상 & 코드 포함)](https://blog.kakaocdn.net/dn/nCGQz/btsMMCj8ArT/68WjXwj7SXERMNSs2PO9k0/img.png)
이 포스팅은 Jetpack Compose와 Glance를 이용해 간단한 앱 위젯을 만드는 방법을 다룹니다 📱✨
저는 백엔드 개발자로, 안드로이드 개발에 대해서는 이제 막 기고 있는 병아리 수준입니다 🐣💻
많이 참조하며 포스팅을 작성하고 있지만, 틀린 점이 있을 수 있음을 양해 부탁드립니다 🙏😊
이 포스팅은 제가 잊지 않기 위해 기록으로 남기는 것입니다.
동영상
소스
libs.versions.toml
[versions]
agp = "8.8.0"
kotlin = "2.0.0"
coreKtx = "1.15.0"
junit = "4.13.2"
junitVersion = "1.2.1"
espressoCore = "3.6.1"
lifecycleRuntimeKtx = "2.8.7"
activityCompose = "1.10.1"
composeBom = "2024.04.01"
#위젯을 위한 의존성 버전
glance = "1.0.0"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
androidx-ui = { group = "androidx.compose.ui", name = "ui" }
androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
#위젯을 위한 의존성
androidx-glance-appwidget = { module = "androidx.glance:glance-appwidget", version.ref = "glance" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
build.gradle.kts(app 수준)
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
}
android {
namespace = "com.lsy.widget_review"
compileSdk = 35
defaultConfig {
applicationId = "com.lsy.widget_review"
minSdk = 26
targetSdk = 35
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = "11"
}
buildFeatures {
compose = true
}
}
dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.activity.compose)
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.ui)
implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.ui.tooling.preview)
implementation(libs.androidx.material3)
// 위젯 의존성 추가
implementation(libs.androidx.glance.appwidget)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
androidTestImplementation(platform(libs.androidx.compose.bom))
androidTestImplementation(libs.androidx.ui.test.junit4)
debugImplementation(libs.androidx.ui.tooling)
debugImplementation(libs.androidx.ui.test.manifest)
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Widget_review"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.Widget_review">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!--리시버 등록-->
<receiver
android:name=".MyReceiver"
android:enabled="true"
android:exported="false">
<!-- 위젯 권한등록 -->
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<!-- 위젯 설정(width,height 등) -->
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/widget_info" />
</receiver>
</application>
</manifest>
widget_info.xml
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="200dp"
android:minHeight="100dp"
android:updatePeriodMillis="0"
android:resizeMode="horizontal|vertical"
android:widgetCategory="home_screen">
</appwidget-provider>
MyReceiver.kts
package com.lsy.widget_review
import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.GlanceAppWidgetReceiver
class MyReceiver: GlanceAppWidgetReceiver() {
// 여기서는 그냥 불변으로 초기화하겠다.
// MyAppWidget이 위젯
override val glanceAppWidget: GlanceAppWidget = MyAppWidget()
}
MyAppWidget.kts
package com.lsy.widget_review
import android.content.Context
import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.dp
import androidx.glance.Button
import androidx.glance.GlanceId
import androidx.glance.GlanceModifier
import androidx.glance.action.actionStartActivity
import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.provideContent
import androidx.glance.layout.Alignment
import androidx.glance.layout.Column
import androidx.glance.layout.Row
import androidx.glance.layout.fillMaxSize
import androidx.glance.layout.padding
import androidx.glance.text.Text
class MyAppWidget : GlanceAppWidget() {
// GlanceAppWidget을 상속받아 앱 위젯을 정의하는 클래스
// 이 클래스는 위젯의 UI와 동작을 정의하는 역할을 한다.
override suspend fun provideGlance(context: Context, id: GlanceId) {
// provideGlance()는 위젯의 UI를 제공하는 함수이다.
// 위젯의 콘텐츠를 제공하기 위해 provideContent()를 호출한다.
provideContent {
MyContent()
}
}
@Composable
private fun MyContent() {
// MyContent 함수는 실제 위젯에 표시될 UI를 구성한다.
// Column을 사용해 세로 방향으로 요소를 배치하며,
// Text와 Button을 포함한 레이아웃을 정의한다.
Column(
modifier = GlanceModifier.fillMaxSize(),
verticalAlignment = Alignment.Top,
horizontalAlignment = Alignment.CenterHorizontally
) {
// Text는 위젯에 표시될 텍스트를 나타낸다.
Text(text = "위젯 테스트입니다.", modifier = GlanceModifier.padding(12.dp))
// Row는 수평으로 버튼을 배치하는데 사용된다.
Row(horizontalAlignment = Alignment.CenterHorizontally) {
Button(
text = "안녕",
// 버튼 클릭 시 메인엑티비티 실행
onClick = actionStartActivity<MainActivity>()
)
Button(
text = "하세요.",
onClick = actionStartActivity<MainActivity>()
)
}
}
}
}
이렇게 정말 간단한 위젯을 만들고 애뮬레이터에서 등록해봤다.
개인 스터디 기록을 메모하는 공간이라 틀린점이 있을 수 있습니다.
틀린 점 있을 경우 댓글 부탁드립니다.
'IT > Live Coding' 카테고리의 다른 글
Spring Boot + Jasypt를 이용한 암호화 테스트 🔐 (테스트 영상 & 소스코드 포함) (0) | 2025.03.22 |
---|---|
Spring Boot + JWT로 인증 시스템 구현 (테스트 영상 & 소스 코드 포함) (0) | 2025.03.22 |
Spring Boot + Redis로 요청 제한 (테스트 영상 & 소스 코드 포함) (1) | 2025.03.09 |
Spring Boot + Redis로 실시간 랭킹 구현 (테스트 영상 & 소스 코드 포함) (1) | 2025.03.08 |
Spring Boot + Redis 캐싱 구현하기 (테스트 영상 & 소스 코드 포함) (0) | 2025.03.05 |
댓글