달력

5

« 2024/5 »

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
728x90
반응형


자, 앱 위젯을 만들어 봅시다.


먼저 앱 위젯이 뭐냐고 묻는 분들이 있겠죠?



앱 위젯이란?


오른쪽 그림에서와 같이, 바탕화면 ( HomeScreen ) 위에 떠 있으면서,
User 와 Interaction 하는 녀석들을 말합니다. ( 이 HomeScreen 을 Widget host 라 부름 )

Developer 에 따르면, 다른 application 에 embedded 될 수 있는 application view 의
미니어쳐라고 기술해 놨네요. 이녀석은 주기적으로 update 신호를 받습니다.

오른쪽 그림은, 물론 IPhone 에서의 위젯인 것으로 추정되지만,
기본적으로 App Widget 이라 하면 저런 류의 것들을 말한다고 볼 수 있겠죠.

저런것. 우리도 쉽게 만들 수 있습니다. ㅋ

( 그림 출처: http://s4.appbrain.com/screen?id=-2668879440831307019&i=2 )




자 그럼 준비물들을 알아보죠.


- AppWidgetProvider class 를 상속하는 provider class

- Widget 의 View Layout 를 기술한 xml 파일

- Widget 의 속성 ( Meta data ) 을 기술한 xml 파일 ( AppWidgetProviderInfo 와 매칭 )

- 부가적인 것 : Widget 의 configuration 변경을 제공할 activity



자 위의 것들만 있으면 아주 기본적인 위젯을 만들 수 있습니다.


물론, Widget 에서 이것저것 일을 해주려면 추가적인 Class 나 Resource 가 필요 하겠지요.



자 그럼 하나하나 들여다 볼까요?



- AppWidgetProvider class 를 상속하는 provider class


AppWidgetProvider 를 상속한 이 TestAppWidget 은 broadcast 형태로 다른 component 들과 통신을 합니다.
( AppWidgetProvider 가 BroadcastReceiver 를 상속한 녀석이기 때문이죠. 오호? )

App Widget 이 update, enabled, disabled, delete 되었을 때 broadcast 형태로 callback 이 불립니다.

그럼 어떤것들이 implement 되어야 하는지 살펴봅시다!

onUpdate( Context context, AppwidgetManager appWidgetManager, int[] appWidgetIds )
onDeleted( Context, context, int[] appWidgetIds )
onEnabled( Context context )
onDisabled( Context context )
onReceived( Context context, Intent intent )

자 그럼 이제 하나하나 다시 파봅시다. ㅋㅋ ( 복잡시려우니 밖으로 빼내겠습니다. )




onUpdate( Context, AppwidgetManager, int[] )

요 녀석은 App Widget 의 속성 ( Meta data ) 에서 지정해준 updatePeriodMillis 값에 따라 주기적으로 호출됩니다.

또한 처음 widget 이 화면에 붙을 때, init() 작업을 해주기 위해서도 call 됩니다.
( Configuration activity 를 따로 두었다면, 처음 붙을 때 불리지는 않습니다. 대신 Conf. Activity 가 init 을 하죠.)

요 녀석이 가장 중요한 녀석입니다. 보통 Handler 를 넣어주어서 작업을 수행하죠.



onDelete( Context )


요 녀석은 App Widget 이 Widget host 로부터 삭제될 때 불립니다.



onEnabled( Context )

요 녀석은 App Widget 이 "처음" 생성될 때 불립니다.
여기서 "처음" 에 double quotation mark 를 붙인 이유는..
같은 위젯을 여러개 띄우면, 첫번째 위젯을 띄울 때만 호출되기 때문입니다.
즉, 첫번째 위젯을 붙일 때는 호출되지만, 동일한 위젯을 하나 더 추가해서 붙일 때는 호출되지 않습니다. (오호?)



onDisabled( Context )

요 녀석은 App Widget 이 "삭제" 될 때 불립니다.
여기서도 "삭제" 에 double quotation mark 를 붙인 이유는.. onEnabled 와 비슷합니다.
즉 이녀석도 가장 마지막에 남아있는 App Widget 이 detach 되었을 때. 그 때만 호출되기 때문입니다. (오호?)


onReceive( Context, Intent )

요 녀석은 일반적인 브로드캐스팅 receiver 입니다. 이녀석은 위에 나열한 callback 들보다 먼저 불리게 됩니다.
이녀석은 implement 할 "필요가 없습니다." 이미 다 구현이 되어 있기 때문이죠.
왜 이 녀석도 double quotation mark 를 넣었을까요?

기본적으로 이 onReceive function은 AppWidgetProvider 에 구현이 되어있습니다.
뭐가 구현 되어있냐구요? Widget 이 붙었을 때 이 콜백함수를 불러라, Widget 이 삭제되었을 때 이 콜백함수를 불러라.
뭐 이런내용이 미리 구현이 되어있죠. 하지만 "전문적인 개발자" 라면 이러한 단순한 callback 만 부르는
onReceive 의 내용이 맘에 들지 않을 수 잇죠. 그렇다면!! 추가적인 implement 를 해주시면 되겠습니다.





- Widget 의 View Layout 를 기술한 xml 파일


이녀석은 그냥 평범한 activity 의 layout 과 차이점이 없습니다.
원하는 View 를 꾸며주시면 되겠습니다.





- Widget 의 속성 ( Meta data ) 을 기술한 xml 파일


이녀석에는 App Widget 에 관련된 meta data 들을 기술해주면 됩니다. 저장 위치는 /res/xml/
Tag 는 이렇게 시작합니다. <appwidget-provider>

여기에 지정해주는 attribute 들은 다음과 같습니다.

minWidth / minHeight / updatePeriodMillis / initialLayout / configure

attribute 이름만 봐도 뭐를 써줘야 하는지 다 아시겠죠?

부가적인 내용만 추가하자면..

minWidth 와 minHeight 는 Widget Host 가 dimension 을 계산할 때 쓰입니다.
HomeScreen 에 붙을 때는 HomeScreen이 정의한 cell size 라는 것이 있는데 이 단위에 맞게 round up 되죠.
( developer 에 따르면 Orientation 등의 최악의 상황을 대비하여 72 * 72 를 min 으로 잡으라네요. ) -> one cell

updatePeriodMillis 는 얼마나 자주 onUpdate() 가 불릴지를 결정하는 것이죠.
( developer 에 따르면 정확한 시간에 작동하지 않을 뿐더러, 배터리를 아끼기 위해서 가능한 적게 설정하길 권장합니다.
추가적으로 User 가 update 주기를 설정할 수 있도록 하는 것이 가장 좋은 방법이라고 제시하고 있습죠. )

configure 에는 앞서 말했던 부가적인, configurationActivity 가 있을 경우에 link 해주시면 되겠습니다.




- 부가적인 것 : Widget 의 configuration 변경을 제공할 activity


요놈은 일반 Activity 와 같습니다.
단지, 속성 ( Meta data ) 를 통해 기술하며, widget 을 통해 처음 붙을 때 start 된다는 것만 빼면요.




- 추가적인 것 : Manifest 등록


앞서 AppWidgetProvider 는 Broadcast 를 상속한 녀석이라고 했죠?
Broadcasting 을 받으려면 어떻게 해야 하죠? 당연히 Receiver 를 Manifest 에 등록해주어야 하죠?
그 과정이 빠져서는 안 됩니다.







자 이제 예제를 통해서 어떤 식으로 만들어지는지 자세히 훑어볼까요?



Mission


Button 하나를 가지는, 가장 작은 Widget 을 만든다.
그 Button 을 클릭했을 때 빈 Activity 하나를 띄워본다.
( 이것만 하면 뭐 하냐고?? 나머지는 당신들이 구현해야지!! ㅎㅎ )





시작하기 전에 먼저 준비물들을 다시 한번 되짚어보죠.


- AppWidgetProvider class 를 상속하는 provider class

- Widget 의 View Layout 를 기술한 xml 파일

- Widget 의 속성 ( Meta data ) 을 기술한 xml 파일 ( AppWidgetProviderInfo 와 매칭 )

- 부가적인 것 : Widget 의 configuration 변경을 제공할 activity




자 그럼 하나씩 만들어봅시다.



먼저 Widget 의 layout 부터 만들어볼까요? 아주 간단히 버튼 하나를 가진 Widget 을 만들어봅시다요.


/res/layout/widget_layout.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

>

<Button

android:id="@+id/btn"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="Click Here!"

/>

</LinearLayout>





자 여기서 약간 주의가 필요합니다.


나중에 AppWidgetProvider 를 상속한 class 를 구현할 때 RemoteViews 라는 class 를 쓰게 됩니다.


이것에 대해서는 나중에 설명하겠습니다.

우선은.. 이 RemoteViews 가 지원하는 view 는 한정되어 있다는 거죠. ( 모든 layout, view 를 지원하지 않습니다. )


FrameLayout

LinearLayout

RelativeLayout


AnalogClock

Button

Chronometer

ImageButton

ImageView

ProgressBar

TextView


요렇게 한정됩니다. 요걸 기억하면서 layout 을 짜주셔야 합니다.


나중가서 "오오. 왜 안 돼지? 열받어!! " 해봐야 소용 없다는 것!





자 그럼 위젯의 속성을 지정하는 ( Meta data 를 포함하는 ) xml 입니다.


/res/xml/widget_configuration.xml

<?xml version="1.0" encoding="utf-8"?>

<appwidget-provider

android:minWidth = "72dp"

android:minHeight = "72dp"

android:initialLayout = "@layout/widget_layout"/>



자 여기서 minWidth 와 minHeight 를 72 dp 로 둔 이유를 모르시는 분들은.. [ 앱 위젯 만들기 #1 ] 요기서 한번 복습하시길..


나머지 속성들도 있지만, 여기서는 딱히 필요가 없기 때문에 기술하지 않았습죠. 후후.


( configure 를 지정해서 activity 를 띄울 수도 있지만, widget 이 붙는 순간 바로 호출되기 때문에 의도와는 맞지 않죠. )





자 이제 Highlight 로 왔습죠. AppWidgetProvider 를 상속하는 class 입니다.

( 기초만을 정확히 짚어내기 위해 developers 를 95% 참조했습니다. ㅎ )


/src/package/SimpleAppWidget.java

public class SimpleAppWidget extends AppWidgetProvider{

@Override

public void onUpdate(Context context, AppWidgetManager appWidgetManager,

int[] appWidgetIds) {

super.onUpdate(context, appWidgetManager, appWidgetIds);

for ( int i = 0; i < appWidgetIds.length; i++ ){

int widgetId = appWidgetIds[i];

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

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

RemoteViews remoteView = new RemoteViews( context.getPackageName(), R.layout.widget_layout );

remoteView.setOnClickPendingIntent( R.id.btn, pendingIntent );

appWidgetManager.updateAppWidget( widgetId, remoteView );

}

}

}



자 요것도 한눈에 아시겠죠?? ( 응?? )


한눈에 알 수 없다면 아마도 RemoteVIews 와 들어보지 못한 updateAppWidget() 때문이겠죠?


자, 설명 들어갑니다.




닥디로 우선 설명해보면. ( RemoteViews )


- 다른 process 에서 보여지는 View hierarchy 를 기술하는 class 입니다.

- Layout resource file 을 바탕으로 inflate 하고, 이 inflate 한 것을 바탕으로 기본적인 operation 을 줄 수 있습니다.



자 아시겠습니까?


잘 아시는 분은 아시겠지만, 원래 다른 thread ( process 는 물론 ) 에서는 UI thread 의 view 들을 조작할 수가 없습니다.

( 안드로이드 정책이지요. )


따라서 보통 thread 를 돌릴 때 handler 등을 사용하여 UI handling 을 하는데,


이 RemoteView 도 일종의 그런 역할을 하는 놈이라고 볼 수 있겠습니다.



Receiver 에서 처리하는 내용이 App Widget 의 View 에 적용되는 것이지요.





자 대~~충은 감이 오실거라 생각하고 코드의 흐름을 설명하면..


update 명령을 받았을 때, 띄워줄 activity 를 intent 로 만들고, 그것을 pending intent 로 감싸줍니다.


RemoteViews 를 통해서 app widget 에서 사용하는 layout 을 inflate 해오고요,


setOnClickPendingIntent( int, PendingIntent ) 를 통해서 그 inflate 해온 놈중에,


하나의 view 에 click 시 pending intent 를 처리하도록 명령을 주는 것입니다.



마지막으로 이 RemoteViews 를 통해 연결했다는 이 내용이

( 여기까지는 직접설정이 아니라, 이렇게 하겠다는 내용입니다. )


AppWidget 을 관리하는 AppWidgetManager 의 updateAppWidget ( int, RemoteViews ) 를 통해 설정이 되는 형식입니다.




아. 설명 잘 한다. ㅋ





자 그럼 이제는 Manifest 기술과, 떨거지 activity 처리만 남았습니다.


src/package/ConfigurationAct.java

public class ConfigurationAct extends Activity{


}



목표는 activity 를 띄우는 것이니 여기까지만으로~ ㅎ






지금까지 다 구현했어도, Manifest 에 기술이 안 되면 모두 말짱 꽝입니다.!


AndroidMafniest.xml

<receiver android:name=".SimpleAppWidget">

<intent-filter>

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

</intent-filter>

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

</receiver>


<activity android:name=".ConfigurationAct"/>



요기서~ 딴 broadcasting 은 알아서 들어오지만.


저기 기술된 APPWIDGET_UPDATE 만은 명시적으로 filter 를 걸어주어야만 합니다.


추가로!!! 지금까지 meta data 를 가지고 있다 meta data 를 가지고 있다 라고 했던..


widget_configuration.xml 을 meta-data tag 를 통해서 기술해줘야 합니다.

(name = "androidappwidget.provider" 는 이 resource 의 형태를 기술해주는 것이죠. )




이로써 아주 간단한 App Widget 을 만들어 봤습니다.


모두들 매우 쉽게 만들 수 있을꺼라 믿으며, 만든 것의 스샷을 살짝 올려드립죠. ㅎㅎ



728x90
반응형
:
Posted by mapagilove