[컴][안드로이드] 앱 위젯 만들기


App Widget Provider 를 이용해서 App Widget 을 만드는 방법
이곳의 내용은 App Widget 을 만드는 방법 의 내용을 번역하면서 필자마음대로 편집한 것이다.

App Widget 을 만들기 위해서는 아래의 요소가 필요하다.
  1. AppWidgetProviderInfo : App Widget 에 대한 metadata 를 갖고 있다.(App Widget의 layout / 업데이트 주기 / AppWidgetProvider class),
  2. AppWidgetProvider : broadcast 이벤트를 기반으로 되어 있다. 여기를 통해 broadcast 를 받게 되는데 이 broadcast 는 App Widget 이 update, enabled, disabled, deleted 될 때 쓰인다.
  3. View layout : 처음 layout 을 정해준다. XML 로 되어 있다.
  4. App Widget Configuration : 이것은 선택적이다. App Widget 이 생성될 때 위젯의 세팅을 수정 할 수 있게 하려면, 만들어야 할 것이다.
대략적인 구조는 아래와 같다.
image

일단 여기서는 아래 내용에 대해서만 알아보도록 하자.
  1. AndroidManifest.xml 작성
  2. AppWidgetProviderInfo 작성
  3. initialLayout 작성

Template Download

App Widget Design Guidelines 에 가면
Download the App Widget Templates Pack for Android 4.0
template 을 download 할 수 있다.


Step. 1 : AndroidManifest.xml 를 작성하자.

아래처럼 receiver ~ /receiver 부분을 넣어주면 된다.
<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
 package="com.economist2pocket"
 android:versionCode="1"
 android:versionName="1.0" >
<application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >     
 ...
 <receiver android:name="com.pakagename.E2pAppWidgetProvider" >
   <intent-filter>
   <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
   </intent-filter>
   <meta-data 
    android:name="android.appwidget.provider" 
    android:resource="@xml/e2p_appwidget_info" />
 </receiver>

Provider class

receiver 이름에 Provider class 의 이름을 적어준다. 여기서는
E2pAppWidgetProvider
이다.

action

intent-filter 에는 APPWIDGET_UPDATE 만 일단 정의해 놓으면 된다.

meta-data

meta-data 에는 AppWidgetProviderInfo 정보가 들어가게 된다.
  1. android:name : meta-data 의 이름을 적어준다. android.appwidget.provider 를 적어주면 된다.
  2. android:resource : AppWidgetProviderInfo 리소스의 위치를 적어주면 된다.(Step. 2를 참고하자.)
는 필수로 들어가야 한다.



Step.2 : AppWidgetProviderInfo 를 만들자.

여기서는 res 밑에 xml/e2p_appwidget_info.xml 를 만들었다. 이 경로가 AndroidManifest.xml 의 meta-data 에 적었던 경로이다.

image

e2p_appwidget_info.xml
<!--
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="294dp" // 위젯 사용시 최소한 소모되는 크기(너비)
    android:minHeight="72dp" // 위젯 사용시 최소한 소모되는 크기(높이)
    android:updatePeriodMillis="86400000" // update 를 요청하는 주기    android:previewImage="@drawable/preview" // "설치할 수 있는 위젯 리스트"에서 보여지는 위젯 아이콘 
    android:initialLayout="@layout/e2p_appwidget"
    android:configure="com.example.android.ExampleAppWidgetConfigure" 
    android:resizeMode="horizontal|vertical"    // android 3.1부터 도입, widget 이 resize 가능하게 해준다.
    android:widgetCategory="home_screen|keyguard"    // android 4.2 에서 도입, 기본값은 home_screen 이다. keyguard 에서도 보여지게 하려면 여기를 참고하자.    android:initialKeyguardLayout="@layout/example_keyguard" // lock-screen 에서 initialLayout 과 같은 역할
    >
</appwidget-provider>

-->
AppWidgetProviderInfo 에서 사용할 수 있는 attribute 를 볼 수 있을 것이다. 여기서는 몇 가지 attribute만 소개한다.

minWidth, minHeight

위젯 사용시 최소한 소모되는 크기이다.
minWidth, minHeight 의 크기가 홈스크린의 cell 사이즈와 맞지 않으면, 가장 비슷한 크기로 변하게 된다.
여러 기기에 설치가 가능하게 하기 위해서는 widget 의 4x4 cell 크기를 넘지 않는 것이 좋다.

minResizeWidth , minResizeHeight

Android 3.1 에서부터 minResizeWidth , minResizeHeight 가 도입되었는데,  이 사이즈를 사용하면 user 는 widget 을 resize 할 수 있게 되고, minWidth, minHeight 보다 작은 값의 widget 으로 resize 할 수도 있다.
minResizeWidth , minResizeHeight 값은 더이상 작아지면 안되는 가장 작은 값이 되야 한다.

updatePeriodMillis

update를 요청하는 주기이다. update 를 요청할 때는 AppWidgetProvider 의 onUpdate() 를 호출하게 된다.
실질적인 update 는 항상 정확한 시간에 이루어지는 것은 아니다.
battery 의 소모때문에 update 는 자주 안하는 것이 권장사항이다.

NOTE

이 update 는 폰이 sleep 상태에 있을때, 폰을 깨워서 update 해준다. 그래도 업데이트가 1시간에 1번정도라면 크게 문제되지 않는다.
하지만 update 가 더욱 자주되어야 하고, 만약에 폰이 sleep 모드일때 update 할 필요가 없다면, alarm 을 사용하라고 한다.
AlarmManager 를 이용해서 너의 AppWidgetProvider 가 받을 intent와 함께  alarm 을 set 하면 된다.
alarm type 은 ELAPSED_REALTIME 또는 RTC 로 해 놓으면 된다.
그리고 updatePeriodMillis 는 '0' 으로 하면 된다.


Step. 3 : App Widget 의 layout 을 작성하자.

RemoteView 로 만든다. 이 RemoteView 는 제한적으로 아래에 제시된 내용만 지원한다.

지원하는 layoutwidget classesetc



  1. FirmLayout
  2. LinearLayout
  3. RelativeLayout
  4. GridLayout



  1. AnalogClock
  2. Button
  3. Chronometer
  4. ImageButton
  5. ImageView
  6. ProgressBar
  7. TextView
  8. ViewFlipper
  9. ListView
  10. GridView
  11. StackView
  12. AdapterViewFlipper



  1. ViewStub

Anroid 4.0 (sdk version 14) 이상에서는 widget 간의 간격이나 icon 과의 배열이 알아서 잘 되는 편이지만, 이전 버전에서 widget 은 일반적으로
  1. 모서리 부분까지 그려지거나
  2. 다른 위젯과 수평을 이루지 않는다.
그래서 sdk version 에 따라 다른 margin 값을 줘야 한다.

방법은 간단하다. margin 을 @dimen/widget_margin 으로 하고는 values-14 에만 값을 '0dp'로 주면 된다.

dimens.xml

image


res/values/dimens.xml:
<dimen name="widget_margin">8dp</dimen>
res/values-v14/dimens.xml:
<dimen name="widget_margin">0dp</dimen>
 

Step 4 : onUpdate(), Source code

onUpdate() 가 호출되는 순간

  • updatePeriodMillis 시간마다 호출(Step. 2 참고)
  • user 가 widget 를 추가할 때도 호출된다. 하지만 configuration Activity 를 정의되어 있다면 처음 widget 을 추가할 때 호출되는 부분은 onUpdate()가 아니라, configuration Activity 가 된다.

Widget 을 눌렀을 때 Activity 실행

Widget 를 눌렀을 때 특정 Activity 를 실행하게 하고 싶다면,  onUpdate() 에서 아래처럼 onClickPendingIntent 를 설정해 주면 된다.
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget_provider_layout);
views.setOnClickPendingIntent(R.id.update, pendingIntent);

Application Not Responding(ANR)

AppWidgetProvider 는 BroadcastReceiver 를 extend 한 녀석이다. 그래서 Application Not Responding (ANR) error 에 의해 작업이 중단될 수 있다. 이것을 피하고 싶다면 onUpdate() 에서 Service 를 이용하자. 아래에 Service 를 이용하는 예제가 있다.
Wiktionary sample's AppWidgetProvider
참고 : Service 사용시 주의할 점


Example

아래 onUpdate() 예제 소스이다. 이 onUpdate() 는 간단한 textView 에 값을 보여주는 Widget 의 소스이다.

public class E2pAppWidgetProvider extends AppWidgetProvider{

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

  final int N = appWidgetIds.length;
  Log.d("WordWidget.UpdateService", "onUpdate()");

  // Perform this loop procedure for each App Widget that belongs to this provider
  for (int i=0; i<N; i++) {
   int appWidgetId = appWidgetIds[i];
   
   Intent intent = new Intent(context, MainActivity.class);
   PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);

   RemoteViews views = new RemoteViews(context.getPackageName(),
        R.layout.e2p_appwidget);
   views.setOnClickPendingIntent(R.id.update, pendingIntent);


   // To prevent any ANR timeouts, we perform the update in a service
   CheckEconomistAsyncTask ce
    = new CheckEconomistAsyncTask(context, views);
   ce.execute();
  }
 }

  
}

setOnClickPendingIntent() 로 widget 을 클릭 했을 때의 listener 를 설정해 놓고,

AsyncTask 로 network 에서 데이터를 retrieving 한다.(여기서도 NetworkOnMainThreadException 이 있어서 바로 connect 가 안된다.)

그리고 아래 AyncTask.onPostExecute() 에서 updateAppWidget 만 잘 호출해 주면 된다.

@Override
protected void onPostExecute(String res) {
 
 // Push update for this widget to the home screen
 ComponentName thisWidget
  = new ComponentName(mContext, E2pAppWidgetProvider.class);
 AppWidgetManager manager = AppWidgetManager.getInstance(mContext);
 mView.setTextViewText(R.id.update, "new");
 manager.updateAppWidget(thisWidget, mView);
 
}


See Also

  1. Home widgets Research Notes
  2. How are RemoteViews used for Android Widgets

댓글 없음:

댓글 쓰기