버그 잡이

안드로이드(android) 위젯(widget) 만들기 본문

안드로이드

안드로이드(android) 위젯(widget) 만들기

버그잡이 2020. 3. 10. 11:26

제가 만든 위젯입니다.

 

습관앱을 만들면서 진행 현황을 핸드폰 홈 스크린에서 확인할 수 있는 위젯을 만들었습니다.

 

주요 기능

 

1. 리스트뷰를 위젯에 표현

2. 위젯 클릭시 앱으로 이동

3. 데이터 최신화

 

 

 

구현 방법

 

1. 리스트뷰를 위젯에 표현

 

- https://onepinetwopine.tistory.com/402

 

Android 안드로이드 ] Listview Widget 위젯 만들기

Android App Widget Document : https://developer.android.com/reference/android/appwidget/package-summary 위젯 앱 위젯은 핸드폰 홈화면에서 볼 수 있는 작은 어플리케이션입니다. 보통 시계, 날씨, 데이터리..

onepinetwopine.tistory.com

기본 홈 위젯 출력과 ListView 출력은 위의 블로그를 따라했습니다.(감사합니다!)

 

간단하게 저만의 정리를 덧붙이자면

 

  1) 단순 버튼 위젯 구현

  • 일단 위젯은 액티비티 위에서 작동하는 것이 아니라
  • AppWidgetProvider 상속받아서 구현한다.
  • Appwidgetprovider 상속받으면 위젯 상태변화에 따라 호출되는 콜백함수를 작성할 있습니다.

 

  2) ListView 위젯 구현

  • 단순한 버튼 위젯이라면 AppWidgetProvider 있어도 되지만
  • ListView 출력하기 위해서는 Adapter 필요합니다.
  • adapter 역할을 RemoteViewFactory 클래스 해줍니다.
  • RemoteViewFactory를 위해서 RemoteViewService도 필요

 

 

 

 

2. 위젯 클릭시 앱으로 이동

 

- 여기서는 listView 각 항목별 다른 이벤트가 아닌 위젯 전체에 대한 클릭 이벤트를 설정했습니다.

- 위젯을 클릭하면 해당앱의 MainActivity(저의 경우 HabitListActivity.class)로 이동하는 것입니다.

 

*안드로이드 공식문서를 참고하여 작성하였습니다.

https://developer.android.com/guide/topics/appwidgets/index.html#%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%9C%A0%EC%A7%80

 

(핵심 내용을 요약하자면)

1) WidgetListView.class의 onUpdate에서 setPendingIntentTemplate 설정

   - setPendingIntentTemplate에 pendingIntent를 등록

   - listView가 클릭되면 pendingIntent를 수행하도록 함.

 

2) WidgetListViewclass의 onReceive에서 클릭시 수행될 작업 설정

   - 클릭시 구체적인 동작은 여기에 작성

 

3) MyRemoteViewsFactory의 RemoteViews getViewAt에서 setOnClickFillInIntent 설정

   - 1번의 setPendingIntentTemplate와 함께 쓰임. extraData를 추가적으로 전달할 수 있음.

   - setPendingIntentTemplate와 setOnClickFillInIntent는 꼭 같이 써야함(왜 그런지는 잘 모르겠습니다 ㅜ) 

 

 

*WidgetListView.class

 @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        // There may be multiple widgets active, so update all of them
        for (int appWidgetId : appWidgetIds) {

            // RemoteViewsService 실행 등록시키는 함수 -> Factory작동을 위해서     -> Factory는 위젯에 담기는 뷰들을 생성해줌.(listview가 아닌 간단한 텍스트뷰라면 필요없음.)
            Intent serviceIntent = new Intent(context, MyRemoteViewsService.class);
            serviceIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);   
            serviceIntent.setData(Uri.parse(serviceIntent.toUri(Intent.URI_INTENT_SCHEME)));    
            RemoteViews widget = new RemoteViews(context.getPackageName(), R.layout.widget_listview);
            widget.setRemoteAdapter(R.id.widget_listview, serviceIntent);

            //Listview 클릭 이벤트를 위한 코드. -> 원리는 pendingIntent 부여 -> 하나씩 부여하기에는 부담이 되어서 각 항목의 클릭이 아닌 위젯 자체에 대한 클릭
            Intent toastIntent = new Intent(context, WidgetListView.class);     
            toastIntent.setAction(WidgetListView.TOAST_ACTION);                 
            toastIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
            serviceIntent.setData(Uri.parse(serviceIntent.toUri(Intent.URI_INTENT_SCHEME)));        
            PendingIntent toastPendingIntent = PendingIntent.getBroadcast(context, 0, toastIntent,
                    PendingIntent.FLAG_UPDATE_CURRENT);                                                         
            widget.setPendingIntentTemplate(R.id.widget_listview, toastPendingIntent);              //listView의 collection에 하나하나 pendingIntent를 부여하는 것은 메모리 부담이 많이 됨. 그래서 콜렉션에 하나의 펜딩인텐트만 부여한다.

            //보내기
            appWidgetManager.updateAppWidget(appWidgetIds, widget);

        }

        super.onUpdate(context, appWidgetManager, appWidgetIds);
    }

    @Override
    public void onReceive(Context context, Intent intent) {

        AppWidgetManager mgr = AppWidgetManager.getInstance(context);
        if (intent.getAction().equals(TOAST_ACTION)) {

            Intent i = new Intent(context, HabitListActivity.class);
            i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.startActivity(i);
        }

        super.onReceive(context, intent);
    }

 

*MyRemoteViewsFactory

  @Override
    public RemoteViews getViewAt(int position) {
        RemoteViews listviewWidget = new RemoteViews(context.getPackageName(), R.layout.item_collection);
      

        Intent fillInIntent = new Intent();
        fillInIntent.putExtra(WidgetListView.EXTRA_ITEM, position);
        listviewWidget.setOnClickFillInIntent(R.id.listView_parent, fillInIntent);   //pendingIntent와 같이 쓰이는 녀석. 클릭시 설정된 extraData를 추가적으로 전달할 수 있다.

        return listviewWidget;
    }

 

 

 

3. 데이터 실시간 최신화

 

- 이거는 간단합니다. MainActivity에서 데이터가 바뀔때 AppWidgetManager를 import해서 "notifyAppWidgetViewDataChanged" 를 해주면 됩니다.

 

*아래 코드는 WidgetListView.class가 아닌 Activity에 작성합니다.

 

*HabitListActivity.class

    AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(this);
        int appWidgetIds[] = appWidgetManager.getAppWidgetIds(
                new ComponentName(this, WidgetListView.class));
        appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.widget_listview);

참고사이트 : https://stackoverflow.com/questions/35957181/how-to-update-app-widget-with-list-view-from-an-activity

 

 

 

반응형
Comments