본문 바로가기

유저 인터페이스/홈스크린 위젯

홈스크린 위젯을 만들어보자! - 간단한 위젯 작성하기

여러 종류의 홈스크린 위젯의 모습


안드로이드는 애플리케이션의 목록이 표시되는 애플리케이션 런처(Application Launcher)와 별도로, 사용자가 임의로 꾸밀 수 있는 화면인 홈 스크린(Home Screen)을 제공합니다. 

홈 스크린은 단말기를 사용할 때 가장 처음 접하게 되는 화면이며, 사용자의 취향에 따라 다양한 요소를 배치하여 자신에 맞는 홈 스크린을 만들 수 있습니다. 홈 스크린에 배치할 수 있는 요소는 다음과 같습니다.

  • 애플리케이션 아이콘
  • 폴더/라이브 폴더
  • (특정 작업의)바로가기
  • 홈스크린 위젯

이번 강좌에서는 위 4가지 요소 중 사용자에게 가장 풍부한 기능을 제공할 수 있는 홈 스크린 위젯(이하 '위젯')을 작성하는 방법에 대해 알아보겠습니다.


위젯의 기초

위젯은 홈스크린의 일정 공간을 차지합니다. 홈스크린에서 위젯이 차지할 수 있는 공간은 홈스크린을 일정한 비율로 나눈 영역인 셀(Cell) 단위로 관리되며, 위젯의 크기는 셀을 몇 개 사용하느냐에 따라 결정됩니다.

일반적으로 안드로이드 단말에서 위젯이 차지할 수 있는 홈스크린 영역은 다음과 같이 가로 4개, 세로 4개 총 16개로 나누어집니다.

홈스크린에서 위젯이 차지할 수 있는 영역 구성


각 위젯은 최소 1개의 셀부터 시작하여 최대 16개의 셀을 차지할 수 있으며, 사각형 형태로만 셀을 차지할 수 있습니다. (4x1, 2x2 등) 위젯이 차지하는 셀의 크기는 매니페스트의 위젯 노드에서 설정하며, 이에 대한 자세한 사항은 잠시 뒤에 살펴보겠습니다.

하나의 위젯을 만들려면 몇 가지 절차를 거쳐야 하는데, 그 절차는 다음과 같습니다.

  • 위젯 레이아웃 작성하기
  • 위젯 프로바이더 작성하기
  • 매니페스트에 위젯 등록하기
  • 위젯 클래스 작성하기

위젯 레이아웃 작성하기

위젯을 만들려면, 먼저 위젯의 레이아웃을 작성해야 합니다. 위젯은 홈스크린에 바로 표시되기 때문에, 위젯의 배경을 지정해야 해당 위젯을 홈스크린에서 쉽게 식별할 수 있습니다. 이 때문에, 일반적으로 위젯 레이아웃을 구성하는 최상위 뷰의 배경에 위젯 배경 이미지를 지정합니다.

위젯 배경 이미지는 자신이 만들 위젯의 크기에 맞게 작성하면 됩니다. 크기별 위젯 바탕 이미지 예제는 개발자 사이트의 위젯 가이드라인 페이지 (http://developer.android.com/guide/practices/ui_guidelines/widget_design.html)에서 구할 수 있으며, 이 페이지에 위젯을 디자인할 때 고려해야 할 내용들도 기술되어 있으므로 위젯 디자인시 참고하는 것이 좋습니다.

[어플리케이션 정보]

액티비티
  • 없음

레이아웃
  • simple_widget_layout.xml (위젯 레이아웃)

XML
  • simplewidget.xml (위젯 프로바이더)


API Level

  • 8 (Android 2.2)


위젯 가이드라인 페이지의 4x1 크기 위젯 배경 이미지 예제를 사용하여 예제 위젯을 만들어보도록 하겠습니다. 위젯의 레이아웃을 작성한 후, PSD 형식으로 되어 있는 예제 이미지를 다운로드하여 PNG 형식으로 변환한 하고 다음과 같이 위젯의 배경으로 지정합니다. 여기에서는 위젯 배경 이미지의 이름을 widget_background_4_1.png로 지정하였습니다.

[simple_widget_layout.xml]
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
	xmlns:android="http://schemas.android.com/apk/res/android"
	android:background="@drawable/widget_background_4_1"
	android:gravity="center"
	android:layout_height="wrap_content"
	android:layout_width="fill_parent"
>
	<TextView
		android:layout_width="wrap_content"
		android:layout_height="wrap_content"
		android:text="Hello, Widget!"
		android:textColor="#000000"
		android:textSize="20dp"
		android:id="@+id/simple_widget_layout_text"
	/>
</LinearLayout>
[코드 1]


홈스크린 위젯의 특성상 위젯 레이아웃에 사용할 수 있는 레이아웃 및 뷰에 한계가 있습니다. 홈스크린 위젯에서 사용할 수 있는 레이아웃 및 뷰는 다음과 같습니다.

레이아웃 :
FrameLayout
RelativeLayout
LinearLayout

뷰 :
AnalogClock
Button
Chronometer
ImageButton
ImageView
ProgressBar
TextView



코드1에서 작성한 위젯 레이아웃은 다음과 같이 화면에 표시됩니다.

예제로 작성할 위젯의 모습



일반적으로 액티비티 레이아웃을 작성할 때와 달리, 위젯 레이아웃을 지정할 때는 위젯을 구성하는 컴포넌트의 배치만 관리할 뿐, 위젯의 전체 크기에는 관여하지 않습니다. 즉, 코드1에서 위젯을 감싸고 있는 LinearLayout의 layout_width, layout_height 속성을 변경해도 위젯의 크기에는 영향을 미치지 않습니다.


위젯 프로바이더 작성하기

위젯 레이아웃 작성이 끝났다면, 위젯 속성을 담고 있는 위젯 프로바이더를 작성합니다. XML 추가 마법사를 연 후, XML 종류에 AppWidget Provider를 선택하여 위젯 프로바이더를 생성합니다.

위젯 프로바이더를 생성합니다.



위젯 프로바이더를 생성하면 다음과 같이 위젯의 속성을 지정할 수 있습니다.


위젯 프로바이더에서는 총 5개의 속성을 지정할 수 있습니다. 각 속성의 의미는 다음과 같습니다.


Min width, Min height

화면에 표시되는 위젯의 크기는 위젯 프로바이더의 Min width, Min height 속성에 따라 결정됩니다. 위젯은 셀(Cell) 단위로 홈스크린의 공간을 차지합니다. 홈 스크린의 화면 전환을 감안하면 한 셀의 한 변이 가질 수 있는 최소 크기는 74dp 입니다. 여기에서 안드로이드 단말의 다양한 해상도로 인해 위젯이 화면에 표시될 때 오차가 발생하는 것을 감안하여 양 쪽에 1dp씩을 빼주면, 실질적으로 한 셀의 한 변이 가질 수 있는 최소 크기는 72dp가 됩니다. 

셀 크기에 따른 최소 크기를 계산하는 공식은 다음과 같습니다.

(셀 개수 * 74) - 2 (단위 : dp)

위의 공식을 사용하여 계산한 셀 개수에 따른 최소 크기는 다음과 같습니다.


위에서 계산한 수치를 통해 일반적으로 많이 쓰이는 위젯의 크기를 정리해보면 다음과 같습니다.


여기에서는 4x1 크기의 위젯을 작성하므로, Min width에 294dp, Min height에 72dp를 지정하였습니다. 


Update period millis

위젯을 업데이트할 주기이며, 밀리초 단위로 지정합니다. 단, 여기에 지정한 주기대로 정확히 업데이트 되는 것은 보장할 수 없으며, 이 주기에 따라 업데이트를 실시할 경우 매 업데이트시마다 휴대폰이 활성 상태로 전환됩니다.

하루에 3~4번 정도 주기로 업데이트 되는 것은 큰 영향을 미치지 않겠지만, 만약 이 주기가 짧을 경우 활성 상태 전환으로 인해 배터리 소모가 증가할 수 있습니다. 이를 해결하려면 위젯을 자동으로 업데이트 하도록 하지 않고 AlarmManager 등을 사용하여 수동으로 업데이트 하도록 하면 됩니다. 수동으로 위젯을 업데이트하려면 Update period millis를 0으로 설정하면 됩니다.


Initial layout

위젯의 초기 레이아웃을 지정합니다. 인터넷에서 정보를 수신하여 표시해주는 위젯의 경우 초기 레이아웃에 정보 수신을 알려주는 문구를 지정하여 사용자에게 현재 상태를 알려줄 수 있습니다. 현재 예제에서는 위젯을 바로 업데이트할 수 있으므로, 위에서 만든 레이아웃을 Initial layout으로 지정하여 바로 위젯이 표시되도록 합니다.


Configure

위젯의 크기, 표시 내용 등을 설정할 수 있는 액티비티를 지정합니다. 패키지 이름을 포함한 액티비티의 클래스 이름을 적어주며, 설정 액티비티는 위젯이 홈스크린에 추가되지 전에 표시됩니다. 별도로 설정할 항목이 없다면 이를 비워두면 됩니다.


매니페스트에 위젯 등록하기

이제 매니페스트에 위젯을 등록할 차례입니다. 홈스크린 위젯은 AppWidgetProvider를 상속받으며, AppWidgetProvider는 브로드캐스트 리시버를 상속받으므로 매니페스트에 등록할 때는 Receiver 노드를 사용합니다. 

매니페스트에 Receiver 노드를 추가한 후, 위젯 클래스를 추가하기 전에 위젯에 필요한 속성들을 지정합니다. 매니페스트에서 위젯이 가져야 할 속성은 다음과 같습니다.

  • android.appwidget.action.APPWIDGET_UPDATE 액션을 가지는 브로드캐스트 메시지 수신
  • 위젯 속성을 가지고 있는 Meta data

android.appwidget.action.APPWIDGET_UPDATE 브로드캐스트 메시지 수신하기

인텐트 필터를 추가하고 액션을 추가한 후, Name에 android.appwidget.action.APPWIDGET_UPDATE를 입력합니다.


Meta data 추가하기

위젯의 속성을 가지고 있는 위젯 프로바이더를 연결해주기 위해, Meta data를 추가한 후 다음과 같이 Name에 android.appwidget.provider를, Resource에는 위젯 프로바이더를 지정합니다.



위젯 클래스 작성하기

이제 매니페스트에 지정할 속성은 모두 지정했습니다. 다음과 같이 Receiver 노드를 선택한 상태에서 오른쪽 속성창의 Name*을 눌러 위젯 클래스를 추가합니다.


다음과 같이 새 클래스를 추가하는 대화상자가 표시됩니다. 위젯 클래스 이름을 지정하고, Superclass에 android.appwidget,AppWidgetProvider를 입력합니다.


위젯 클래스 파일이 생성되면, 위젯을 업데이트시 위젯을 표시하기 위해 다음과 같이 onUpdate() 메서드를 추가합니다.

[MySimpleWidget.java]
public class MySimpleWidget extends AppWidgetProvider {

	@Override
	public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds){
		
	}
}
[코드2]

AppWidgetProvider에서는 상태에 따라 총 4개의 콜백 메서드가 호출되며, 각 메서드에서 수행해야 할 작업은 다음과 같습니다.


AppWidgetProvider는 브로드캐스트 리시버를 상속받은 클래스이므로 onReceive() 메서드가 구현되어 있습니다. 위의 콜백 메서드는 AppWidgetProvider의 onReceive() 메서드에서 수신한 브로드캐스트 메시지의 액션에 따라 호출되도록 구현되어 있습니다. AppWidgetProvider의 onReceive() 구현부는 다음과 같습니다.

    public void onReceive(Context context, Intent intent) {
        // Protect against rogue update broadcasts (not really a security issue,
        // just filter bad broacasts out so subclasses are less likely to crash).
        String action = intent.getAction();
        if (AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
            Bundle extras = intent.getExtras();
            if (extras != null) {
                int[] appWidgetIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS);
                if (appWidgetIds != null && appWidgetIds.length > 0) {
                    this.onUpdate(context, AppWidgetManager.getInstance(context), appWidgetIds);
                }
            }
        }
        else if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)) {
            Bundle extras = intent.getExtras();
            if (extras != null && extras.containsKey(AppWidgetManager.EXTRA_APPWIDGET_ID)) {
                final int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID);
                this.onDeleted(context, new int[] { appWidgetId });
            }
        }
        else if (AppWidgetManager.ACTION_APPWIDGET_ENABLED.equals(action)) {
            this.onEnabled(context);
        }
        else if (AppWidgetManager.ACTION_APPWIDGET_DISABLED.equals(action)) {
            this.onDisabled(context);
        }
    }

[코드3]

여기에서는 간단하게 위젯을 화면에 표시하는 것만 구현할 것이므로, 위의 콜백 메서드 중 onUpdate() 메서드만 구현하겠습니다. onUpdate() 메서드를 다음과 같이 구현합니다. 

[MySimpleWidget.java]
	@Override
	public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds){
        final int N = appWidgetIds.length;

        for (int i=0; i<N; i++) {
            int appWidgetId = appWidgetIds[i];
            RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.simple_widget_layout);
            appWidgetManager.updateAppWidget(appWidgetId, views);
        }
	}
[코드4]

onUpdate() 메서드는 세번째 인자로 appWidgetIds 배열을 받습니다. 이 배열에는 이 위젯 인스턴스들의 ID가 포함되어 있으며, 위젯을 업데이트시 우리가 만든 위젯을 사용하는 모든 위젯을 업데이트하기 위해 코드4와 같이 for문을 사용하여 현재 등록된 위젯들을 모두 업데이트합니다.

for문 내부를 보면 RemoteViews를 사용하는 것을 볼 수 있습니다. RemoteViews는 위젯의 레이아웃 및 컴포넌트의 동작 처리에 사용하는 클래스로, 생성자에서 우리가 만든 위젯 레이아웃을 지정하는 것을 확인할 수 있습니다. 여기에서는 별도로 컴포넌트의 동작을 지정하지 않으므로 바로 updateAppWidget() 메서드를 사용하여 위젯을 화면에 표시합니다.

이것으로 예제가 완성되었습니다. 실행을 누르면 위젯이 단말기에 설치됩니다. 위젯을 설치하였다면, 홈스크린에 우리가 만든 위젯을 추가해보도록 하겠습니다. 홈스크린을 길게 눌러 위젯을 선택하면, 목록에 우리가 만든 위젯이 표시되는 것을 확인할 수 있습니다.

위젯 목록에 우리가 만든 위젯인 SimpleWidget이 표시됩니다.


SimpleWidget을 선택하면 다음과 같이 위젯이 홈스크린에 등록됩니다.

SimpleWidget이 홈스크린에 등록된 모습


이것으로 간단한 위젯을 만드는 것에 대해 알아보았습니다. 액티비티 위주의 애플리케이션과는 다른 점들이 많아 처음에는 혼동이 많겠지만, 몇 번 연습해보면 쉽게 익숙해질 것입니다.

다음 강좌에서는 위젯에 버튼을 추가하고, 위젯을 업데이트하는 것에 대해 알아보겠습니다.