App Widget Provider 를 이용해서 App Widget 을 만드는 방법
이곳의 내용은 App Widget 을 만드는 방법 의 내용을 번역하면서 필자마음대로 편집한 것이다.
App Widget 을 만들기 위해서는 아래의 요소가 필요하다.
- AppWidgetProviderInfo : App Widget 에 대한 metadata 를 갖고 있다.(App Widget의 layout / 업데이트 주기 / AppWidgetProvider class),
- AppWidgetProvider : broadcast 이벤트를 기반으로 되어 있다. 여기를 통해 broadcast 를 받게 되는데 이 broadcast 는 App Widget 이 update, enabled, disabled, deleted 될 때 쓰인다.
- View layout : 처음 layout 을 정해준다. XML 로 되어 있다.
- App Widget Configuration : 이것은 선택적이다. App Widget 이 생성될 때 위젯의 세팅을 수정 할 수 있게 하려면, 만들어야 할 것이다.
일단 여기서는 아래 내용에 대해서만 알아보도록 하자.
- AndroidManifest.xml 작성
- AppWidgetProviderInfo 작성
- initialLayout 작성
Template Download
App Widget Design Guidelines 에 가면Download the App Widget Templates Pack for Android 4.0template 을 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 정보가 들어가게 된다.- android:name : meta-data 의 이름을 적어준다. android.appwidget.provider 를 적어주면 된다.
- android:resource : AppWidgetProviderInfo 리소스의 위치를 적어주면 된다.(Step. 2를 참고하자.)
Step.2 : AppWidgetProviderInfo 를 만들자.
여기서는 res 밑에 xml/e2p_appwidget_info.xml 를 만들었다. 이 경로가 AndroidManifest.xml 의 meta-data 에 적었던 경로이다.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> -->
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 는 제한적으로 아래에 제시된 내용만 지원한다.지원하는 layout | widget classes | etc |
|
|
|
Anroid 4.0 (sdk version 14) 이상에서는 widget 간의 간격이나 icon 과의 배열이 알아서 잘 되는 편이지만, 이전 버전에서 widget 은 일반적으로
- 모서리 부분까지 그려지거나
- 다른 위젯과 수평을 이루지 않는다.
방법은 간단하다. margin 을 @dimen/widget_margin 으로 하고는 values-14 에만 값을 '0dp'로 주면 된다.
dimens.xml
res/values/dimens.xml:
res/values-v14/dimens.xml:<dimen name="widget_margin">8dp</dimen>
<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); }
댓글 없음:
댓글 쓰기