티스토리 뷰
Collection
ListView와 GridView같이 다양한 요소를 표현하는 View는 보통 Adapter와 연결되어 표현합니다. 위젯에도 컬렉션을 표현할 수 있으며 Adapter 연결을 RemoteViewsService와 RemoteViewsFactory로 진행합니다.
즉 개발자는 RemoteViewsFactory를 통해 Adapter의 기능을 구현하고, RemoteViewsService를 통해 RemoteViewsFactory를 생성하여 Collection에 연결할 수 있습니다.
RemoteViewsFactory
RemoteViewsFactory를 상속받아 Adapter처럼 구현하면 됩니다. getLoadingView는 Collection을 로딩할 때 보여줄 View를 반환하며 로딩할 때 보여줄 View가 없다면 null을 반환합니다.
class MemoWidgetRemoteViewsFactory(
private val context: Context,
private val memoRepository: MemoRepository
) : RemoteViewsService.RemoteViewsFactory {
private var list = emptyList<Memo>()
override fun onCreate() {
}
override fun onDataSetChanged() {
runBlocking(Dispatchers.IO) {
list = memoRepository.findAll()
}
}
override fun onDestroy() {
}
override fun getCount(): Int {
return list.size
}
override fun getViewAt(position: Int): RemoteViews {
return RemoteViews(context.packageName, R.layout.widget_memo_remotes_view).apply {
setTextViewText(R.id.memo_text_view, list[position].memo)
}
}
override fun getLoadingView(): RemoteViews? {
return null
}
override fun getViewTypeCount(): Int {
return 1
}
override fun getItemId(position: Int): Long {
return list[position].id.toLong()
}
override fun hasStableIds(): Boolean {
return true
}
}
RemoteViewsService
RemoteViewsService는 RemoteViews에게 Adapter를 제공하는 기능을 합니다. RemoteVeiwsService를 상속받고 onGetViewFactory를 구현하면 됩니다.
@AndroidEntryPoint
class MemoWidgetRemoteViewsService : RemoteViewsService() {
@Inject
lateinit var memoRepository: MemoRepository
override fun onGetViewFactory(intent: Intent): RemoteViewsFactory {
return MemoWidgetRemoteViewsFactory(this, memoRepository)
}
}
RemoteViews
val intent = Intent(context, MemoWidgetRemoteViewsService::class.java)
val remoteViews = RemoteViews(context.packageName, R.layout.widget_memo).apply {
setRemoteAdapter(R.id.list_view, intent)
}
내부구조
RemoteViewsService
RemoteViewsService는 Service를 상속받고 있으며 onGetViewFactory를 통해 RemoteViewsFactory를 생성하고 onBind를 통해 RemoteViewsFactoryAdapter를 반환하여 RemoteViews에 Adapter를 제공합니다.
onGetViewFactory를 통해 RemoteViewsFactory를 생성하는 내부 코드
@Override
public IBinder onBind(Intent intent) {
synchronized (sLock) {
Intent.FilterComparison fc = new Intent.FilterComparison(intent);
RemoteViewsFactory factory = null;
boolean isCreated = false;
if (!sRemoteViewFactories.containsKey(fc)) {
factory = onGetViewFactory(intent);
sRemoteViewFactories.put(fc, factory);
factory.onCreate();
isCreated = false;
} else {
factory = sRemoteViewFactories.get(fc);
isCreated = true;
}
return new RemoteViewsFactoryAdapter(factory, isCreated);
}
}
RemoteViewsFactoryAdapter
RemoteViewsFactoryAdapter는 생성자로 RemoteViewsFactory를 받는데 getViewAt, getCount 등 Adapter의 주요 기능을 RemoteViewsFactory를 통해 구현합니다.
RemoteViewsFactoryAdapter가 RemoteViewsFactory에 의존하여 Adapter의 기능을 구현하는 내부 코드
private static class RemoteViewsFactoryAdapter extends IRemoteViewsFactory.Stub {
public RemoteViewsFactoryAdapter(RemoteViewsFactory factory, boolean isCreated) {
mFactory = factory;
mIsCreated = isCreated;
}
public synchronized boolean isCreated() {
return mIsCreated;
}
public synchronized void onDataSetChanged() {
try {
mFactory.onDataSetChanged();
} catch (Exception ex) {
Thread t = Thread.currentThread();
Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, ex);
}
}
public synchronized void onDataSetChangedAsync() {
onDataSetChanged();
}
public synchronized int getCount() {
int count = 0;
try {
count = mFactory.getCount();
} catch (Exception ex) {
Thread t = Thread.currentThread();
Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, ex);
}
return count;
}
public synchronized RemoteViews getViewAt(int position) {
RemoteViews rv = null;
try {
rv = mFactory.getViewAt(position);
if (rv != null) {
rv.addFlags(RemoteViews.FLAG_WIDGET_IS_COLLECTION_CHILD);
}
} catch (Exception ex) {
Thread t = Thread.currentThread();
Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, ex);
}
return rv;
}
public synchronized RemoteViews getLoadingView() {
RemoteViews rv = null;
try {
rv = mFactory.getLoadingView();
} catch (Exception ex) {
Thread t = Thread.currentThread();
Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, ex);
}
return rv;
}
public synchronized int getViewTypeCount() {
int count = 0;
try {
count = mFactory.getViewTypeCount();
} catch (Exception ex) {
Thread t = Thread.currentThread();
Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, ex);
}
return count;
}
public synchronized long getItemId(int position) {
long id = 0;
try {
id = mFactory.getItemId(position);
} catch (Exception ex) {
Thread t = Thread.currentThread();
Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, ex);
}
return id;
}
public synchronized boolean hasStableIds() {
boolean hasStableIds = false;
try {
hasStableIds = mFactory.hasStableIds();
} catch (Exception ex) {
Thread t = Thread.currentThread();
Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, ex);
}
return hasStableIds;
}
public void onDestroy(Intent intent) {
synchronized (sLock) {
Intent.FilterComparison fc = new Intent.FilterComparison(intent);
if (RemoteViewsService.sRemoteViewFactories.containsKey(fc)) {
RemoteViewsFactory factory = RemoteViewsService.sRemoteViewFactories.get(fc);
try {
factory.onDestroy();
} catch (Exception ex) {
Thread t = Thread.currentThread();
Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, ex);
}
RemoteViewsService.sRemoteViewFactories.remove(fc);
}
}
}
private RemoteViewsFactory mFactory;
private boolean mIsCreated;
}
1. RemoteViews에서 RemoteViewsService를 통해 Adapter를 등록합니다.
2. AppWidgetManager에서 updateAppWidget을 호출하면 RemoteViewsFactory를 생성하여 Adapter와 연결합니다.
3. notifyAppWidgetViewDataChanged를 통해 RemoteViews 일부분을 업데이트 하며 RemoteViewsFactory는 onDataSetChanged 부터 진행하여 업데이트합니다.
Git (예제코드)
https://github.com/KangTaeJong98/TaeTae98/tree/main/Android/Widget
GitHub - KangTaeJong98/TaeTae98: Study Repository
Study Repository. Contribute to KangTaeJong98/TaeTae98 development by creating an account on GitHub.
github.com
'Android > Widget' 카테고리의 다른 글
Android Widget - 위젯 설정 (0) | 2021.12.02 |
---|---|
Android Widget - 위젯 이벤트 (0) | 2021.12.02 |
Android Widget - 위젯 생성 (0) | 2021.12.02 |
- Total
- Today
- Yesterday
- Exception
- 연산자
- Widget
- DART
- Android
- 보이스카우트 규칙
- ConcatAdapter.Config
- Coroutine
- observable
- viewmodel
- ViewModelStoreOwner
- clean code
- git
- null
- rxjava
- ViewModelProvider
- 클린코드
- isActive
- DSL
- 함수
- commit
- ConcatAdapter
- 클린 코드
- Flowable
- gradle
- TDD
- CancellationException
- Kotlin
- Flutter
- 코루틴
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |