Android Widget 만들기
원티드 안드로이드앱 4.0.1 업데이트에 위젯 기능이 추가되었습니다. 오늘의 맞춤 추천 포지션을 랜덤으로 표시해 주는 심플한 위젯인데, 안드로이드 스튜디오로 손쉽게 위젯 작업을 할 수 있었습니다. 위젯 기능 개발을 하면서 경험한 몇 가지를 정리해 보았습니다.
위젯 등록 화면 | 위젯 실행 화면 |
---|---|
![]() |
![]() |
위젯 생성하기
이전에 위젯을 만들어 보신분들은 아시겠지만, 기존의 Activity 안에서 뷰가 동작하는 방식과 다릅니다. 그래서 관련 서적이나 앱 개발 가이드 페이지를 참조하여 일일히 구현해야 했는데요, 해당 부분에 대한 자세한 내용은 링크를 참조해 주세요. 혹은 안드로이드 위젯으로 구글 검색을 하면 다양한 구조 설명들이 있으니 참조하시면 좋습니다. 안드로이드 스튜디오가 업데이트 되면서 기본 위젯을 만들 수 있습니다. 프로젝트에서 [New] > [Widget] > [App Widget]을 선택 하시면 됩니다.
각 선택 항목은 아래와 같습니다.
- Class Name : 클래스 명 (AppWidgetProvider를 상속한 클래스가 생성됩니다.)
- Placement : homescreen에서 표시할지, lockscreen에서 표시할지, 혹은 둘다 할지 여부를 설정합니다. (appwidget-provider의 withCategory에 대응되는 값입니다.)
- Resizable : 위젯 사이즈 조절 가능 여부를 설정합니다. 양방향, 가로only, 세로only, 조정불가 옵션이 있습니다. (appwidget-provider의 resizeMode에 대응되는 값입니다.)
- Minimum Width : 위젯의 최소 가로 사이즈 설정값입니다. 셀 크기 단위로 설정가능합니다. (appwidget-provider의 minWidth에 대응되는 값입니다.)
- Minimum Height : 위젯의 최소 세로 사이즈 설정값입니다. 셀 크기 단위로 설정가능합니다. (appwidget-provider의 minHeight에 대응되는 값입니다.)
생성 완료 후 drawable-nodpi의 example_appwidget_preview.png파일이 생성된 것을 확인할 수 있습니다. 해당 리소스는 사용자가 위젯 등록 시 표시될 이미지 리소스입니다. (appwidget-provider 의 previewImage 에 대응되는 값입니다.) 만드려는 위젯에 맞게 리소스를 적절히 수정하여 사용하면 됩니다.
특정 시점에 위젯 갱신하기
appwidget-provider에 updatePeriodMillis 을 이용하여 자동 업데이트 주기 설정이 가능합니다. 하지만 updatePeriodMillis의 경우 최소 값이 30분이므로, 사용자의 특정 액션에 대하여 새로고침을 해줄 필요가 있습니다. AppWidgetProvider는 Broadcast에 의하여 동작되도록 되어 있으며, 위젯의 갱신, 삭제, 모두 삭제 등에 대한 Broadcast가 전달 될 때 어떤 위젯에 대한 이벤트인지 appWidgetIds를 함께 전달해줍니다. 각 생성된 위젯마다 고유의 식별자를 appWidgetId로 할당해서 전달해주고 있습니다. 아래는 안드로이드 스튜디오에서 위젯을 생성하고 난 뒤 생성된 코드입니다.
class NewAppWidget : AppWidgetProvider() {
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
// There may be multiple widgets active, so update all of them
for (appWidgetId in appWidgetIds) {
updateAppWidget(context, appWidgetManager, appWidgetId)
}
}
override fun onEnabled(context: Context) {
// Enter relevant functionality for when the first widget is created
}
override fun onDisabled(context: Context) {
// Enter relevant functionality for when the last widget is disabled
}
companion object {
internal fun updateAppWidget(context: Context, appWidgetManager: AppWidgetManager,
appWidgetId: Int) {
val widgetText = context.getString(R.string.appwidget_text)
// Construct the RemoteViews object
val views = RemoteViews(context.packageName, R.layout.new_app_widget)
views.setTextViewText(R.id.appwidget_text, widgetText)
// Instruct the widget manager to update the widget
appWidgetManager.updateAppWidget(appWidgetId, views)
}
}
}
onUpdate, onDisabled, onEnabled 등 AppWidgetProvider의 override fun은 모두 onReceived에서 AppWidgetProvider.ACTION_*에 대응되어 동작되도록 작성되어 있습니다. 해당 내용을 참조하여 새로고침을 하고싶은 모든 위젯 ID를 읽어와서 broadcast evnet를 발생시키면 해당 위젯을 새로고침 할 수 있습니다.
companion object Utils {
@JvmStatic
fun updateWidget(context: Context?) {
val appWidgetManager = AppWidgetManager.getInstance(context)
val ids = appWidgetManager.getAppWidgetIds(ComponentName(context, TodayWidget::class.java))
val intent = Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE)
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids)
context?.sendBroadcast(intent)
}
}
사용자의 특정 액션 (예를들어 로그인 / 로그아웃 후) 이 후 위젯을 새로 갱신할 필요가 있을 때에는 위 코드에서 Utils.updateWiddget 함수를 이용하여 갱신 수행해 주면 됩니다.
참조사항
위젯의 provider meta data에 resizeMode을 none으로 설정하여도 특정 런처에서는 위젯의 크기 변경이 가능합니다. 또한 위젯의 크기 (셀 사이즈)를 1크기로 지정하여도 마찬가지로 런처마다 표시되는 형식이 다릅니다.
만약 위젯의 새로고침 주기를 30분보다 더 짧게 하려면 AppWidgetManager.ACTION_APPWIDGET_UPDATE로 전달되는 intent를 이용하여 AlarmManager를 이용하여 처리하면 됩니다. 이 경우 안드로이드 위젯은 하나의 타입을 여러개 생성할 수 있으므로 intent cache 처리에 신경 써야 할 것 같습니다.
마치며
원티드는 사용자가 서비스를 실행할 수 있는 다양한 경로에 대하여 고려하고 준비하고 있습니다. 위젯의 경우 홈 화면에서 사용자가 서비스에서 필수적으로 생각하는 기능을 등록해서 사용할 수 있게끔 준비해준다면 좋은 유입 채널이 될 수도 있다고 생각합니다. 이러한 시도는 앞으로도 계속될 수 있도록 하겠습니다.