Android/Widget

Android Widget - 위젯 생성

강태종 2021. 12. 2. 09:35

위젯

홈 화면에서 쉽게 정보를 제공하거나 어플리케이션을 제어할 수 있는 기능이다. 사용자가 쉽게 접근할 수 있지만 어플리케이션이 아닌 홈 화면에서 제공받기 때문에 제약이 따른다.

Widget Example


기본사항

AppWidgetProviderInfo

레이아웃, 업데이트 빈도, 크기 등 위젯에 대한 메타 데이터를 XML로 정의한다.

더보기

메터 데이터 설명 (링크)

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:initialLayout="@layout/widget_covid"
    android:configure="com.taetae98.widget.ui.activity.CovidWidgetConfigureActivity"
    android:resizeMode="horizontal|vertical"
    android:updatePeriodMillis="360000"
    android:minWidth="220dp"
    android:minHeight="146dp" />

 

AppWidgetProvider

BroadcastReceiver를 상속 받았으며 이벤트 형식으로 위젯을 관리할 수 있다.

더보기
@AndroidEntryPoint
class CovidWidgetProvider : AppWidgetProvider() {
    @Inject
    lateinit var covidStatusManager: CovidStatusManager

    @Inject
    lateinit var covidWidgetRepository: CovidWidgetRepository

    companion object {
        fun getLargeRemoteViews(context: Context, covidWidget: CovidWidget, covidStatusResponse: CovidStatusResponse): RemoteViews {
            val format = NumberFormat.getInstance()
            return RemoteViews(context.packageName, R.layout.widget_covid).apply {
                setInt(R.id.layout, "setBackgroundColor", covidWidget.backgroundColor)
                setTextColor(R.id.city_text_view, covidWidget.textColor)
                setTextColor(R.id.total_title_text_view, covidWidget.textColor)
                setTextColor(R.id.positive_title_text_view, covidWidget.textColor)
                setTextColor(R.id.death_title_text_view, covidWidget.textColor)
                setTextColor(R.id.total_text_view, covidWidget.textColor)
                setTextColor(R.id.positive_text_view, covidWidget.textColor)
                setTextColor(R.id.death_text_view, covidWidget.textColor)
                setTextViewText(R.id.city_text_view, covidWidget.city)
                setTextViewText(R.id.total_text_view, "${format.format(covidStatusResponse.body[covidWidget.city]?.totalPositive)}명")
                setTextViewText(R.id.positive_text_view, "${format.format(covidStatusResponse.body[covidWidget.city]?.positive)}명")
                setTextViewText(R.id.death_text_view, "${format.format(covidStatusResponse.body[covidWidget.city]?.death)}명")
            }
        }

        private fun updateWidget(context: Context, covidWidget: CovidWidget, covidStatusResponse: CovidStatusResponse) {
            AppWidgetManager.getInstance(context).updateAppWidget(
                covidWidget.id, getLargeRemoteViews(context, covidWidget, covidStatusResponse)
            )
        }

        suspend fun updateWidget(context: Context, appWidgetIds: IntArray, covidStatusResponse: CovidStatusResponse) {
            appWidgetIds.forEach { id ->
                AppDatabase.getInstance(context).covidWidgetDao().findById(id)?.let { widget ->
                    updateWidget(context, widget, covidStatusResponse)
                }
            }
        }
    }

    override fun onReceive(context: Context, intent: Intent) {
        super.onReceive(context, intent)
        Logger.intent("Widget onReceive", intent)
    }

    override fun onDeleted(context: Context, appWidgetIds: IntArray) {
        super.onDeleted(context, appWidgetIds)
        CoroutineScope(Dispatchers.IO).launch {
            covidWidgetRepository.deleteByIds(appWidgetIds)
        }
    }

    override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
        super.onUpdate(context, appWidgetManager, appWidgetIds)
        covidStatusManager.onLatestCovidStatus().enqueue(
            object : Callback<CovidStatusResponse> {
                override fun onResponse(call: Call<CovidStatusResponse>, response: Response<CovidStatusResponse>) {
                    Logger.response("Widget onReceive", response)
                    val covidStatusResponse = response.body() ?: return
                    CoroutineScope(Dispatchers.IO).launch {
                        updateWidget(context, appWidgetIds, covidStatusResponse)
                    }
                }

                override fun onFailure(call: Call<CovidStatusResponse>, t: Throwable) {
                    Logger.e("Widget onReceive", t)
                }
            }
        )
    }
}

 

 

Layout

위젯에 대한 레이아웃을 설정한다. 위젯은 홈 화면에서 제공하기 때문에 RemoteViews로 관리되는데 RemoteViews에 사용할 수 있는 View가 제한적이다.

더보기
<?xml version="1.0" encoding="utf-8"?>
<GridLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/layout"
    android:rowCount="3"
    android:columnCount="3"
    android:background="#80000000"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/city_text_view"
        android:text="도시"
        android:textColor="@color/white"
        android:textSize="18sp"
        android:textStyle="bold"
        android:gravity="center"
        android:layout_rowWeight="1"
        android:layout_columnWeight="1"
        android:layout_columnSpan="3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/total_title_text_view"
        android:text="총\n확진자"
        android:textColor="@color/white"
        android:textSize="16sp"
        android:textStyle="bold"
        android:gravity="center"
        android:layout_rowWeight="1"
        android:layout_columnWeight="1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        tools:textColor="@color/white"/>
    <TextView
        android:id="@+id/positive_title_text_view"
        android:text="일일\n확진자"
        android:textColor="@color/white"
        android:textSize="16sp"
        android:textStyle="bold"
        android:gravity="center"
        android:layout_rowWeight="1"
        android:layout_columnWeight="1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        tools:textColor="@color/white"/>
    <TextView
        android:id="@+id/death_title_text_view"
        android:text="사망자"
        android:textColor="@color/white"
        android:textSize="16sp"
        android:textStyle="bold"
        android:gravity="center"
        android:layout_rowWeight="1"
        android:layout_columnWeight="1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        tools:textColor="@color/white"/>

    <TextView
        android:id="@+id/total_text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_rowWeight="1"
        android:layout_columnWeight="1"
        android:gravity="center"
        android:text="0"
        android:textColor="@color/white"
        android:textSize="16sp"
        android:textStyle="bold" />

    <TextView
        android:id="@+id/positive_text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_rowWeight="1"
        android:layout_columnWeight="1"
        android:gravity="center"
        android:text="0"
        android:textColor="@color/white"
        android:textSize="16sp"
        android:textStyle="bold" />

    <TextView
        android:id="@+id/death_text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_rowWeight="1"
        android:layout_columnWeight="1"
        android:gravity="center"
        android:text="0"
        android:textColor="@color/white"
        android:textSize="16sp"
        android:textStyle="bold" />
</GridLayout>

 

 

Manifest

AppWidgetProvider가 BroadcastReceiver 기반이기 때문에 Manifest에 등록해야 하며, AppWidgetProvider와 AppWidgetProviderInfo를 Manifest에서 연결합니다.

더보기

exported : 위젯이 홈 화면을 통해 제공되므로 true로 설정

APPWIDGET_UPDATE : 위젯 업데이트 Action을 수신하기 위해 설정

meta-data : AppWidgetProviderInfo와 연결

        <receiver
            android:name=".widget.CovidWidgetProvider"
            android:exported="true"
            android:label="@string/covid_widget" >
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>

            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/provider_covid_widget" />
        </receiver>

Git (예제 코드)

https://github.com/KangTaeJong98/TaeTae98/tree/main/Android/Widget

 

GitHub - KangTaeJong98/TaeTae98: Study Repository

Study Repository. Contribute to KangTaeJong98/TaeTae98 development by creating an account on GitHub.

github.com