Android App Widget

yo_hou·2023년 1월 31일
0
post-thumbnail

Widget 개요

Widget이란?

앱의 가장 중요한 데이터와 기능을 사용자의 홈 화면에서 한 눈에 볼 수 있으며 바로 엑세스 가능하도록 만든 뷰

  • 기능도 기능이지만, 사람들이 하나하나 앱에 들어가서 알아보는게 귀찮다 보니 위젯을 통해 간단하게 확인하는 용도로도 많이 쓰이는 것으로 보인다.
  • 위젯에도 날씨 앱과 같은 정보 위젯, 기기 기능을 관리하는 관리 위젯, 이를 모두 결합한 하이브리드 위젯으로 나뉜다.

Widget의 차별점

위젯도 일반적인 앱처럼 돌아간다고 생각할 수 있겠지만, 액티비티의 모음으로 이루어진 일반적인 앱들과는 다소 차이가 있다.

  • 위젯은 다른 홈 화면과 공존해야 하기에 동작이 매우 제한되며 사용 가능한 유일한 동작은 터치와 수직 스와이프 뿐이다.
  • 또한 독자적으로 만들어지는 것이 아닌 AppWidgetProvider를 통해 앱 위젯이 게시된다.

Widget 공식 문서

AppWidgetProvider

  • 앱 위젯을 BroadcastReceiver로 처리하기 위한 편의성 클래스이다.
  • 업데이트, 삭제, 사용 설정 및 중지되는 경우와 같이 관련성이 높은 이벤트 브로드캐스트만 수신한다.

AppWidgetProvider Method

onUpdate()

  • 앱 위젯을 업데이트하기 위해 호출
  • 사용자가 앱 위젯을 추가할 때도 호출되므로 뷰의 이벤트 핸들러 정의와 같은 필수 설정을 실행하고 필요한 경우 임시 Service를 시작해야 함
  • 구성이 완료된 경우 구성 활동에 첫 번째 업데이트를 실행해야 함

onAppWidgetOptionsChanged()

  • 위젯이 처음 배치될 때와 위젯의 크기가 조절될 때 호출
  • 위젯의 크기 범위를 기반으로 콘텐츠를 표시하거나 숨길 수 있음
  • 위젯 크기 옵션

onDelete()

  • 위젯이 앱 위젯 호스트에서 삭제될 때마다 호출

onEnabled(Context)

  • 앱 위젯의 인스턴스가 처음으로 생성될 때 호출
  • 앱의 인스턴스를 두 개 추가하는 경우 처음에만 호출
  • 새 DB를 열거나 모든 앱 위젯 인스턴스에 한 번만 발생해야 하는 다른 설정을 실행해야 하는 경우 여기에 작성

onDisabled(Context)

  • 앱 위젯의 마지막 인스턴스가 앱 위젯 호스트에서 삭제될 때 호출

onReceived(Context, Intent)

  • 모든 브로드캐스트에서 위의 각 콜백 메서드 이전에 호출됨.
  • 기본 AppWidgetPrivoder 구현은 모든 앱 위젯 브로드캐스트를 필터링하고 위 메서드를 적절하게 호출하므로 일반적으로 구현 필요 없음

AppWidget 예시

class ExampleAppWidgetProvider : AppWidgetProvider() {

        override fun onUpdate(
                context: Context,
                appWidgetManager: AppWidgetManager,
                appWidgetIds: IntArray
        ) {
            // Perform this loop procedure for each App Widget that belongs to this provider
            appWidgetIds.forEach { appWidgetId ->
                // Create an Intent to launch ExampleActivity
                val pendingIntent: PendingIntent = Intent(context, ExampleActivity::class.java)
                        .let { intent ->
                            PendingIntent.getActivity(context, 0, intent, 0)
                        }

                // Get the layout for the App Widget and attach an on-click listener
                // to the button
                val views: RemoteViews = RemoteViews(
                        context.packageName,
                        R.layout.appwidget_provider_layout
                ).apply {
                    setOnClickPendingIntent(R.id.button, pendingIntent)
                }

                // Tell the AppWidgetManager to perform an update on the current app widget
                appWidgetManager.updateAppWidget(appWidgetId, views)
            }
        }
    }

Widget 적용 후기

1. 위젯 칸 공식

위젯도 크기를 막 설정하면 안이쁘게 나와서 보기 좀 그렇다.

공식 문서에서는 셀 개수 당 크기가 다음과 같다고 한다.

근데 적용해보니까 실제로 잘 안돼서 조금 당황했다..

어떤 사람은 (셀 개수 * 74) - 2으로 하니까 잘 됐다고 하는데, 난 결국 노가다의 길을 걷고야 말았다...


2. 위젯의 뷰 변화

난 위젯이 어떤 행동에 의해 다른 뷰로 바뀌기도 하는 등의 기능을 기대하고 있었다.

그런데, 생각보다 이게 어려웠다.

AppWidgetProvider에 PendingIntent를 통해서 연결해주는 액티비티가 있어서 처음에는 액티비티를 여러개 생성하였다.
이후 그때그때 위젯 화면을 달리해주고 싶을 때마다 PendingIntent를 다른 액티비티에 새로 연결해주자는 생각이었다.

근데 이게 생각처럼 되지도 않을 뿐더러, 위젯은 가볍게 만들어야 하는데 뷰가 너무 중구난방으로 바뀌는게 아닌가 하는 생각도 들었다.

사실 위젯의 목적이 간단한 정보 전달과 기능 제공인데 말이다.
그래서, 뷰 변화도 정말 간단히 바꿔주어 위젯의 제 기능에 충실하고자 하였다.


그렇다면 뷰를 어떻게 변화시켰느냐??

정말 원시적인 방법이면서도 안좋은 방법이라고 생각하긴 하지만, visibility 속성을 이용하기로 하였다.

기존 레이아웃은 VISIBLE 상태로 놓고, 후에 나타나게 하고 싶은 레이아웃을 INVISIBLE이나 GONE으로 해놨다가 이벤트가 발생하면 기존 것을 GONE으로 바꿔주고 나타나게 하고 싶은 레이아웃을 VISIBLE로 바꿔주었다.

위젯 구현 시에는 RemoteView를 통해 뷰를 끌어오기에 해당 RemoteView의 내장 함수인 setViewVisibility(int viewId, int visibility)를 사용하면 쉽게 바꿔줄 수 있다.

0개의 댓글