출처 : http://www.coremodeling.com/android/tutorial/AppWidget/AppWidget.html

 

App Widget

  1. 작성자 : 고덕한(deokhan.koh@gmail.com)
  2. 소속 : 코아모델링(www.coremodeling.com)
  3. 작성일자 : 2011년 9월 22일

Android 홈페이지에 있는 App Widget 문서를 번역하면서 AppWidget 에 대한 개념과 개발하는 방법을 익히도록 합니다.

URL 은 http://developer.android.com/guide/topics/appwidgets/index.html 클릭하면 됩니다.

App Widget 은 Home Screen 에 보여지는 것과 같이 다른 Application 에 Embedded 될 수 있고 주기적으로 update 가 되는 미니어처 어플리케이션 View 라 할 수 있다. 이러한 View 들은 User Interface 에서 Widget 이라 일컬어진다. 그리고 App Widget Provider 를 사용하여 개발하여 설치가능하다. App Widget 을 포함한 어플리케이션 컴포넌트를 App Widget host 라 일컫는다. App Widget 은 아래와 같은 Music App Widget 같은 것을 말한다.

1. The Basics

App Widget 을 개발하기 위해서는 아래와 같이 따른다.

1.1 AppWidgetProviderInfo 객체

AppWidget 에 대한 metadata 를 표현하는 것으로써, App Widget 의 layout, update 주기, 그리고 AppWidgetProvider 클래스 등의 정보를 표시한다. 이러한 내용을 XML 파일에 정의해야만 한다.

위치는 /res/xml 디렉토리에 파일을 위치한다.

1.2 AppWidgetProvider 클래스구현

AppWidget 이 동작하기 위해서 AppWidgetProvider 를 상속받아서 구현해야 한다. AppWidget 으로써 동작하기 위해서 필요한 메소드를 Override 해야하며, 이는 Broadcast 이벤트를 수신하여 필요한 기능이 동작하기 위함이다. updated, enabled, disabled, 그리고 deleted 메소드를 Override 한다.

1.3 View layout

Layout XML 파일이며, Widget 이 초기에 보여기 위한 layout 파일이다.

추가적으로 App Widget 의 환경설정을 위한 Configuration Activity 를 추가할 수 있다. 이것은 옵션 사항으로써 AppWidget 에 처음 생성될 때 사용자가 AppWidget 에 환경을 설정하도록 지원하는 기능이다.

2. Declaring an App Widget in the Manifest

첫번째로 AppWidgetProvider 클래스를 AndroidManifest.xml 파일에 등록한다. 예제는 아래와 같다.

<receiver android:name="ExampleAppWidgetProvider" >

    <intent-filter>

        <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />

    </intent-filter>

    <meta-data android:name="android.appwidget.provider"

               android:resource="@xml/example_appwidget_info" />

</receiver>

AppWidgetProvider 는 BroadcastReceiver 를 상속받아서 작성된 클래스이다. 따라서 AndroidManifest.xml 에 등록하려면, <receiver> 태그를 사용하여 등록을 한다.

위 예제에서 보듯이 <intent-filter> 안에 <action> element를 android.appwidget.action.APPWIDGET_UPDATE 를 추가한다. 이는 이 APPWIDGET_UPDATE action 에 대한 broadcast message 가 발생하면, 이 AppWigetProvider 가 동작하게끔 처리하는 것이다. AppWidgetManager 는 다른 모든 AppWidget 에게 broadcast message 를 필요한 경우에 자동으로 전송을 한다.

<meta-data> element 를 AppWidgetProviderInfo 에 해당하는 Resource 의 xml 파일을 지정한다.

3. Adding the AppWidgetProviderInfo Metadata

AppWidgetProviderInfo 는 xml 파일로 정의하며, App Widget 을 작성하기 위해서 반드시 필요한 파일이다. 이 XML 파일에는 initial layout resource, App Widget 의 upate 주기, 그리고 옵션으로 처음 생성 시점에 실행되는 configuration Activity 정보를 설정한다. XML 파일에 <appwidget-provider> element 로 정의하며, XML 파일은 /res/xml 디렉토리에 위치한다.

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"

    android:minWidth="294dp"

    android:minHeight="72dp"

    android:updatePeriodMillis="86400000"

    android:previewImage="@drawable/preview" <!-- android 3.0 이후에 추가 -->

    android:initialLayout="@layout/example_appwidget"

    android:configure="com.example.android.ExampleAppWidgetConfigure"

    android:resizeMode="horizontal|vertical"> <!-- android 3.1 이후에 추가 -->

</appwidget-provider>

다음은 각 attribute 에 대한 설명이다.

3.1 minWidth 와 minHeight

AppWidget layout 의 최소 폭과 높이의 값을 설정한다.

Home Screen 에 보여질 Widget 에 대한 높이와 폭을 지정하는 것이다. Home Screen 은 아이콘 크기의 Cell 형태로 이루어지져 있다. 따라서 Widget 의 폭과 높이를 지정할 때, HomeScreen 에 놓여질 Cell 의 크기를 기준으로 계산을 해야한다. 기본적인 Cell 의 크기는 74 dp 이며, 그 값을 계산하는 방식은 아래와 같다.

(number of cell * 74)  - 2

주의 : Home Screen 에서 Widget 이 가질 수 있는 최대의 Cell 의 갯수는 4 x 4 이다, 이보다 크게 Widget 의 크기를 디자인해서는 안된다.

3.2 updatePeriodMillis

AppWidget Framework 가 AppWidgeProvider.onUpdate() 메소드를 얼마나 자주 호출해야 하는지를 정의한다. 하지만 실제로 onUpdate() 가 정확한 시간에 호출되는지는 보증할 수 없다. 가능한한 제 시간에 호출되는 것으로 가정해야 하며, 어떨 때는 Battery 문제로 인하여  한시간에 한번도 호출되지 않을 수도 있다. 주로 사용자가 15분 주기 또는 1시간 주기등으로 설정한다.

주의 : updatePeriodMillis 를 설정해 놓으면 Device 가 sleep mode(주로 화면이 꺼진 상태) 로 되어있을 지라도 update 를 수행하기 위해서 Device 를 깨워서 동작을 한다. 이러한 작업이 1시간 주기로 동작하면, Battery 문제가 없지만, 자주 호출이 되면 Battery 를 많이 소모하게 된다. 따라서 Device 가 꺠어있는 상태에서만 동작하도록 하려면 updatePeriodMillis 의 값은 “0” 으로 설정하고, AlarmManager 를 사용하여 주기적으로 동작하도록 설정해야 한다. 이때 Alarm 의 Type 은 ELASPED_REALTIME 또는 RTC  로 설정하면 된다.

3.3 initialLayout

AppWidget layout 을 정의하기 위한 layout resource 를 지정한다.

3.4 configure

사용자가 App Widget 을 추가할 때 실행할 Activity 를 지정한다. 이 Activity 는 App Widget 의 환경설정을 하는데 주로 사용한다.

3.5 previewImage

App Widget 을 추가하기 위해서 Menu 를 선택하고 App Widget 목록에서 해당 Widget 에 대한 미리보기 이미지를 지정한다. 이 항목이 지정되지 않았을 경우에는 디폴트 Icon 으로 나타난다. 이 속성은 Android 3.0 부터 지원한다.

3.6 autoAdvanceViewId

해당 Widget host 에 의해서 auto-advanced 되어야 하는 App Widget subview 의 view ID 를 기술한다. 이 속성은 Android 3.0 부터 지원한다.

3.7 resizeMode

Home Screen 에서 Widget 의 폭과 높이에 대해서 Resize 할 것인지를 정의한다. 값은 “vertical”, “horizontal”, “none” 으로 설정한다. 이 속성은 Android 3.1 부터 지원한다.

4. Creating the App Widget Layout

Widget 이 처음 실행되었을 때, Home Screen 보여질 Layout 을 설정해야 한다. 우선 /res/layout 디렉토리에 Layout 을 생성해야 한다.

Layout 을 생성하는 것은 기존의 Layout 을 작성하는 것과 별반 차이가 없으나, Widget 에서는 사용할 수 있는 View 가 한정되어 있다.

Home Screen 에 보여지는 View 는 RemoteViews 에 기반하고 있으며, RemoteViews 하위에 디자인한 View 가 속하게 된다.

기존의 모든 layout class 들을 모두 지원하지는 않고, 아래의 클래스들을 지원한다.

FrameLayout, LinearLayout, Relativelayout

AnalogClock, Button, Chronometer, ImageButton, ImageView, ProgressBar, TextView, ViewFlipper

5. Using the AppWidgetProvider Class

AppWidgetProvider 클래스는 BroadcastReceiver 클래스를 상속받는다. 이로 인해서 App Widget broadcast message 손쉽게 처리할 수 있다. AppWidgetProvier 클래스는 AppWidget 에 관련된 broadcast message 만 수신한다. 이 메시지는 updated, deleted, enabled, 그리고 disabled 이다. 이 메시지를 받을 때마다 이에 관련된 다음 메소드들이 호출된다.

5.1 onUpdate()

이 메소드는 AppWidgetProviderInfo 로 지정된 XML 파일에서 updateMillis 속성에 정의된 주기로 호출된다. 또한 이메소드는 사용자가 Home Screen 에서 App Widget 을 추가했을 때 호출된다. 이때 View 에 이벤트 핸들러를 추가하거나 임시 Service 를 호출하는 등의 필요한 초기 설정 작업을 수행해야 한다.

하지만 configuration Activity 가 정의된 경우에는 사용자가 Home Screen 에 App Widget 을 추가할 때, 이 메소드는 호출되지 않는다. 단지 주기적으로 호출될 때 실행된다. 따라서 Activity 를 지정하였을 경우에는, Configuration 을 완성하기 위해서 Activity 에서 onUpdate() 메소드를 처음에 호출하도록 구현해야 한다.

5.2 onDeleted(Context, int[])

App Widget host 에서 삭제될 때 호출된다.

5.3 onEnabled(Context)

App Widget 이 처음으로 instance 가 생성될 때 호출된다. 예를 들어 사용자가 두 개의 App Widget instance 를 추가한다 하더라도, 처음에 한번만 호출이 된다. 이 메소드에 초기에 필요한 작업들을 구현한다. 주로 데이터베이스 설정같은 작업을 구현한다.

5.4 onDisabled(Context)

App Widget host 에서 마지막 App Widget instnace 가 삭제될 때 호출된다. 이 메소드에서는 onEnabled() 에서 초기화된 작업을 모두 제거하는 기능을 주로 구현한다.

onDeleted() 는 여러개의 Instance 중에서 각각을 삭제할 때 호출되지만, onDisabled() 는 마지막 Instance 가 삭제될 때 호출된다.

5.5 onReceive(Context, Intent)

위의 메소드가 호출되기 이전에 먼저 호출된다. 일반적으로 이 메소드를 구현할 필요는 없으며, 공통적으로 처리할 내용이 있을 경우에 구현한다.

AppWidgetProvider 에서 가장 중요한 메소드가 onUpdate() 이다. 왜냐하면 이 메소드는 사용자가각각의  App Widget 을 host 에 추가할 때 호출되기 때문이다. 만약 App Widget 이 사용자의 Event 를 수용한다면, 이 메소드에서 Event handler 를 등록해야 한다. 아래 코드는 onUpdate() 메소드에서 Click 이벤트를 등록하는 코드이다.

public class ExampleAppWidgetProvider extends AppWidgetProvider {

    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {

        final int N = appWidgetIds.length;

        // 이 Provider 에 속하는 각각의 App Widget 에 대해서 순차적으로 Loop 을 수행

        for (int i=0; i<N; i++) {

            int appWidgetId = appWidgetIds[i];

            // ExampleActivity 를 실행할 Intent 를 생성

            Intent intent = new Intent(context, ExampleActivity.class);

            PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);

            // 버튼에 onClick listener 를 설정하기 위해서 App Widget 에 대한 layout 의 참조를 얻는다.

            RemoteViews views = new RemoteViews(context.getPackageName(), 

                                                                                  R.layout.appwidget_provider_layout);

            views.setOnClickPendingIntent(R.id.button, pendingIntent);

            // AppWidgetManager 이 현재 App Widget 에 update 를 수행하도록 처리한다.

            appWidgetManager.updateAppWidget(appWidgetId, views);

        }

    }

}

위 소스의 내용을 설명하자면, Activity 를 실행하기 위해서 PendingIntent 를 사용한다. 또한 버튼에 클릭이벤트를 OnClickPendingIntent 로 지정한다. 그리고 하나의 App Widget 에 여러개의 Instance 를 생성했을 경우( Home Screen 에 똑같은 Widget 을 두개 이상 추가했을 경우) 에 모든 Widget 에 동일한 효과를 주기 위해서 Loop 을 사용하여 설정하였다.

주의 : AppWidgetProvider 는 BroadcastReceiver 클래스를 상속받았기에, 시간이 많이 소요되는 작업은 그 처리 결과를 보증할 수 없다. 따라서 시간이 많이 걸리는 작업은 별도의 Service 를 만들어서 실행해야 한다.

5. 7 Receiving App Widget broadcast Intents

AppWidgetProvider 클래스는 BroadcastReceiver 클래스를 상속받아서 편리하게 사용하기위해 작성된 클래스이다. 따라서 BroadcasetRecevier 로 구현하듯이 작성한 후에, 아래의 Intent 를 사용하면 된다.

  1. ACTION_APPWIDGET_UPDATE
  2. ACTION_APPWIDGET_DELETED
  3. ACTION_APPWIDGET_ENABLED
  4. ACTION_APPWIDGET_DISABLED

6. Creating an App Widget Configuration Activity

사용자가 App Widget 을 처음 만들고 나서, App Widget 에 대한 환경설정을 하기 위해서는 App Widget Configuration Activity 를 생성할 수 있다. 이 Activity 는 App Widget host 에 의해서 자동으로 실행되며, 사용자가 App Widget 을 처음 설치할 때 설정을 할 수 있도록 하는 것이다. 이 Activity 로 App Widget 에 대한 색, 크기, 업데이트 주기 또는 다른 설정들을 할 수 있다.

Configuration Activity 는 일반적인 다른 Activity 처럼 AndroidManifest.xml 에 등록이 되어야 하며, “ACTION_APPWIDGET_CONFIGURE” action 으로 실행되어진다. 아래 예제와 같이 작성하면 된다.

<activity android:name=".ExampleAppWidgetConfigure">

    <intent-filter>

        <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>

    </intent-filter>

</activity>

또한 이 Activity 는 AppWidgetProviderInfo XML 파일에 android:configure 항목에 정의가 되어야 한다. 아래와 같이 작성한다.

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"

    ...

    android:configure="com.example.android.ExampleAppWidgetConfigure"

    ... >

</appwidget-provider>

주의 : Activity 를 정의할 때, fully-qualified namespace 를 사용하여 정의해야 한다.

지금까지 Activity 를 설정하는 것을 살펴보았으며, 설정은 이것으로 간단하게 처리할 수 있다. 하지만 Activity 내에서는 두가지 중요한 기능을 구현해야 한다.

  1. App Widget host 에서 Configuration Activity 를 실행할 때, Activity 는 result 를 리턴 받는다. result 는 Intent 로 받으며, 이 Intent 에 App Widget ID 값이 저장되어서 리턴받는다. Intent 에 EXTRA_APPWIDGET_ID 값으로 저장되어 있다.
  2. App Widget 이 생성될 때, onUpdate() 메소드가 호출되지 않는다. Configuration Activity 가 실행될 때 시스템에서 ACTION_APPWIDGET_UPDATE broadcast message 를 전송하지 않는다. 이것은 App Widget 이 처음으로 생성될 때, Configuration Activity가 AppWidgetManager 에서 update 를 요청해야하는 책임이 있다는 것이다. 즉, onUpdate() 메솓느는 처음에는 호출되지 않고, 지속적인 update 에서 호출된다.

6.1 Updating the App Widget from the configuration Activity

App Widget 이 configuration activity 를 사용할 때, 설정이 완료되어질 때, Activity 에서는 App Widget 이 update 되는 책임이 있다. AppWidgetManager 에서 직접 update 를 요청할 수 있다.

다음은 적절히 App Widget 을 update 하는 절차와 Activity 완료하는 절차를 요약했다.

6.1.1 첫번쨰, 실행되어진 Activity 에서 Intent 로부터 App Widget ID 를 얻는다.

Intent intent = getIntent();

Bundle extras = intent.getExtras();

if (extras != null) {

    mAppWidgetId = extras.getInt(

            AppWidgetManager.EXTRA_APPWIDGET_ID,

            AppWidgetManager.INVALID_APPWIDGET_ID);

}

6.1.2 App Widget 환경설정을 수행한다.
6.1.3 환경설정이 완료되었을 때, getInstance(Context) 메소드를 호출하여 AppWidgetManager 의 객체를 얻는다.

AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);

6.1.4 updateAppWidget(int, RemoteViews) 메소드를 호출하여 RemoteViews layout 을 사용하여 App Widget 화면을 업데이트 한다.

RemoteViews views = new RemoteViews(context.getPackageName(),

R.layout.example_appwidget);

appWidgetManager.updateAppWidget(mAppWidgetId, views);

6.1.5 마지막으로 리턴할 Intent 를 생성하고, Activity result 에 설정하고, Activity 를 종료한다.

Intent resultValue = new Intent();

resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);

setResult(RESULT_OK, resultValue);

finish();

팁 : configuration Activity 가 처음 실행될 때, Activity result 를 RESULT_CANCELED 로 설정한다. 이는 사용자가 최종적으로 완료되기 전에 취소할 경우 App Widget host 에서 환경설정이 최소되었다는 것을 알아야 하고, App Widget 이 추가되지 않는다.

7. Setting a Preview Image

Android 3.0 에서 previewImage 속성을 AppWidgetProviderInfo XMl 에 추가하였다. 이 항목은 Home Screen 에서 Widget 을 추가할 때, Widget 을 선택할 수 있는 목록에서 보여질 Preview 내용을 나타내는 것이다. 다음과 같이 XML 에 설정을 한다.

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"

  ...

  android:previewImage="@drawable/preview">

</appwidget-provider>

8. Using App Widgets with Collections

Android 3.0 에서는 Collection 을 포함한 App Widget 을 새로 추가했다. 이 AppWidget 은 백그라운드에 있는 Collection으로 된 Remote Data 를보여주기 위해서 RemoteViewsService 를 사용한다. 이 collection data 는 아래의 collection view 에 의해서 보여진다.

  1. ListView : Vertically scrolling list 를 보여주는 View
  2. GridView : two-dimensional scrolling grid 형태로 item 을 보여주는 View
  3. StackView : 마치 카드처럼 아래/위로 밀어서 내용을 보여주는 View
  4. AdapterViewFlipper : Adapter 기반으로 되어 있으며, 여러개의 View 를 애니매이션처리하는 ViewAnimator

위에서 언급했듯이, Remote data 를 Collection view 에 보여지기 위해서는 Adapter 를 사용해서 UI 에 bind 시켜야 한다. Adapter 각각의 Item 들을 개별적인 View 에 bind 시켜야 한다. Android Framework 에서는 app widget 에 이 데이터들을 bind 시켜야 하며, Adapter 가 이러한 역할을 수행해야 할 것을 RemoteViewFactory 가 대신 하고 있다.

Collection 에서 특정 item 이 요청될 때, 이 RemoteViewFactory 를 생성하고 해당 Item 에 대해서 RemoteViews 객체를 리턴한다. app widget 에 collection view 를 포함하기 위해서는 반드시 RemoteViewsService 와 RemoteViewsFactory 를 구현해야 한다.

RemoteViewsService 는 Service로써 RemoteViews 객체를 요청하는 원격 adapter 역할을 수행한다. RemoteViewsFactory 는 collection view 와 view 에 보여질 데이터 사이의 adpater 를 위한 interface 이다.

아래 예제코드를 참고한다.

public class StackWidgetService extends RemoteViewsService {

    @Override

    public RemoteViewsFactory onGetViewFactory(Intent intent) {

        return new StackRemoteViewsFactory(this.getApplicationContext(), intent);

    }

}

class StackRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {

//... include adapter-like methods here. See the StackView Widget sample.

}

+ Recent posts