개발일지

Android in A..Z - Widget (생성) 본문

Android (안드로이드)/Widget

Android in A..Z - Widget (생성)

강태종 2021. 12. 1. 15:12

위젯

위젯은 사용자의 홈 화면에서 바로 엑세스할 수 있으며, 정보 표현, 앱 제어 등 다양한 작업을 할 수 있습니다. 하지만 위젯은 홈 화면으로 제공되기 때문에 제한이 있습니다.

 

- 제스쳐 : 좌우 스와이프 기능이 제한됩니다. (대부분 홈화면은 ViewPager 형식의 네비게이션을 가지기 때문에 이벤트가 겹쳐서 제한됩니다.

- View : RemoteViews를 통해 제공되기 때문에 제한된 View만 사용가능합니다. (참고)


필수 설정

AppWidgetProviderInfo (참고)

위젯에 대한 갱신 주기, Layout, 크기 등 메타 데이터를 설명하며 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("CovidWidget", 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("onLatestCovidStatus", response)
                    val covidStatusResponse = response.body() ?: return
                    CoroutineScope(Dispatchers.IO).launch {
                        updateWidget(context, appWidgetIds, covidStatusResponse)
                    }
                }

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

 

Layout

위젯의 Layout을 생성합니다. 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에 추가해야 합니다.

- 기본적으로 위젯에 업데이트를 받아야 하기 때문에 APPWIDGET_UPDATE Action을 추가합니다. 

- AppWidgetProviderInfo를 연결하기 위해 meta-data를 추가합니다.

        <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/Example/tree/main/Android/Widget

 

GitHub - KangTaeJong98/Example: My Example Code

My Example Code. Contribute to KangTaeJong98/Example development by creating an account on GitHub.

github.com


 

'Android (안드로이드) > Widget' 카테고리의 다른 글

Android Widget - 위젯 이벤트  (0) 2022.03.09
Comments