Виджеты (Widgets) – это такие мини-приложения. которые могут быть встроены в главный экран устройства и выводить полезную информацию из самого приложения или даже взаимодействовать с ним, выполняя какие-либо операции. Поэтому виджеты являются очень важной частью приложения, к тому же они удобны в использовании: например, у многих музыкальных плееров есть свой виджет, который позволяет менять треки или ставить их на паузу без запуска приложения.
В ранних версиях Android виджеты могли отображать только такие простые элементы, как TextView, ImageView и так далее. Однако сейчас их возможности стали намного больше, теперь можно использовать и более сложные ListView, GridView и StackView, что позволяет показывать в виджетах больше самой разной информации.
В этой статье мы рассмотрим, как добавить список ListView в виджет и обработать нажатия на его элементы. Делать это будем на примере виджета для приложения Менеджер паролей от Wi-Fi сетей.
Начало работы
Для начала нужно создать XML-файлы разметки и метаданных.
Файл разметки будет определять внешний вид нашего виджета и расположение элементов на нём. В данном случае виджет будет состоят из заголовка с названием приложения и ListView, который будет содержать список активных сетей. Добавим в папку res/layout файл widget_network.xml со следующим кодом:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/gray" android:orientation="vertical" > <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/primary" > <ImageView android:id="@+id/widget_logo" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="16dp" android:src="@drawable/logo_widget" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" > <ListView android:id="@+id/widgetList" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/gray" tools:listitem="@layout/widget_list_item" /> </LinearLayout> </LinearLayout>
Поскольку элемент в списке содержит разные данные, для него тоже сделана отдельная разметка в файле widget_list_item.xml, также расположенном в res/layout, которая представляет собой слегка изменённую разметку элемента списка RecyclerView из приложения.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/widget_container" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/gray" android:orientation="vertical" > <LinearLayout android:id="@+id/widget_main" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginEnd="16dp" android:layout_marginLeft="16dp" android:layout_marginRight="16dp" android:layout_marginStart="16dp" android:layout_marginTop="14dp" android:orientation="horizontal" android:weightSum="1" > <TextView android:id="@+id/widget_SSID" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_weight="0.55" android:fontFamily="sans-serif" android:text="Network" android:textColor="@android:color/black" android:textSize="16sp" android:textStyle="bold" /> <TextView android:id="@+id/widget_tv_dot" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_weight="0.05" android:gravity="center" android:text="@string/dot" android:visibility="invisible" /> <TextView android:id="@+id/widget_tv_hide" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginLeft="8dp" android:layout_marginStart="8dp" android:layout_weight="0.3" android:text="@string/card_hidden" android:textSize="16sp" android:visibility="invisible" /> <ImageView android:id="@+id/widget_arrow" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_weight="0.1" app:srcCompat="@drawable/ic_keyboard_arrow_down_black_24dp" /> </LinearLayout> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="14dp" android:layout_marginEnd="16dp" android:layout_marginLeft="16dp" android:layout_marginRight="16dp" android:layout_marginStart="16dp" android:layout_marginTop="2dp" android:orientation="horizontal" > <TextView android:id="@+id/widget_password" android:layout_width="wrap_content" android:layout_height="wrap_content" android:drawablePadding="8dp" android:fontFamily="sans-serif" android:text="password" android:textIsSelectable="true" android:textSize="14sp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="8dp" android:layout_marginStart="8dp" android:text="@string/dot" /> <TextView android:id="@+id/widget_date" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="8dp" android:layout_marginStart="8dp" android:bufferType="spannable" android:fontFamily="sans-serif" android:text="date" android:textIsSelectable="true" android:textSize="14sp" /> </LinearLayout> <LinearLayout android:id="@+id/widget_more" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="14dp" android:layout_marginEnd="16dp" android:layout_marginLeft="16dp" android:layout_marginRight="16dp" android:layout_marginStart="16dp" android:layout_marginTop="2dp" android:orientation="horizontal" android:visibility="gone" android:weightSum="1" > <Button android:id="@+id/btn_widget_connect" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_weight="0.7" android:background="@color/green_settings" android:text="@string/connect" android:textSize="14sp" style="@style/Base.TextAppearance.AppCompat.Widget.Button.Colored" /> <ImageView android:id="@+id/widget_copy" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginLeft="30dp" android:layout_marginStart="30dp" android:layout_weight="0.15" android:tint="@color/secondary_text" app:srcCompat="@drawable/ic_content_copy_black_24px" /> <ImageView android:id="@+id/widget_share" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_weight="0.15" android:tint="@color/secondary_text" app:srcCompat="@drawable/ic_share_black_24px" /> </LinearLayout> </LinearLayout>
Основными данными для показа здесь являются SSID сети, пароль к ней, дата добавления, а также отметка о том, скрыта сеть или нет. При раскрытии элемента будут отображаться дополнительные кнопки, позволяющие подключиться к сети, скопировать пароль или поделиться данными о ней с другими людьми.
Теперь нужно создать файл с метаданными. Для этого в папке res/xml создадим файл network_widget.xml.
<?xml version="1.0" encoding="utf-8"?> <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" android:initialLayout="@layout/widget_network" android:minHeight="250dp" android:minWidth="250dp" android:previewImage="@drawable/logo" android:resizeMode="horizontal|vertical" android:updatePeriodMillis="864000" android:widgetCategory="home_screen"> </appwidget-provider>
В этом файле мы указываем разметку виджета, которую мы добавили ранее, а также размеры виджета, картинку, которая будет отображаться в списке виджетов, время обновления и возможность менять размер виджета.
Теперь, закончив с внешним видом виджета, можно приступить к реализации работы со списком. Для этого нам понадобится создать 3 класса: WidgetRemoteViewsFactory, WidgetRemoteViewsService, WidgetProvider. Логику их взаимодействия можно описать следующими словами: WidgetProvider при обновлении виджета будет посылать интент в WidgetRemoteViewsService, который будет возвращать обратно экземпляр WidgetRemoteViewsFactory. О том, как это устроено, будет рассмотрено ниже.
Добавление WidgetProvider
Класс WidgetProvider наследует от AppWidgetProvider, его задачей является реализация жизненного цикла виджета. Создадим класс WidgetProvider, содержащий следующий код:
public class WidgetProvider extends AppWidgetProvider { private static final String TAG = "PROVIDER"; @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { super.onUpdate(context, appWidgetManager, appWidgetIds); for (int widgetId : appWidgetIds) { updateWidget(context, appWidgetManager, widgetId); } } private void updateWidget(Context context, AppWidgetManager appWidgetManager, int widgetId) { RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_network); appWidgetManager.updateAppWidget(widgetId, views); } public static void sendRefreshBroadcast(Context context) { Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); intent.setComponent(new ComponentName(context, WidgetProvider.class)); context.sendBroadcast(intent); } @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); if (BuildConfig.DEBUG) Log.d(TAG, action); if (!TextUtils.isEmpty(action)) { if (action.equals(AppWidgetManager.ACTION_APPWIDGET_UPDATE)) { AppWidgetManager manager = AppWidgetManager.getInstance(context); ComponentName cn = new ComponentName(context, WidgetProvider.class); manager.notifyAppWidgetViewDataChanged(manager.getAppWidgetIds(cn), R.id.widgetList); } } super.onReceive(context, intent); } }
Метод onUpdate() вызывается, когда происходит обновление виджетов, при этом в параметры передаются контекст приложения, объект AppWidgetManager и ID всех виджетов, которые нужно обновить.
Если посмотреть исходный код класса AppWidgetProvider, то можно увидеть, что он наследует от класса BroadcastReceiver, поэтому он может принимать широковещательные сообщения от приложения. Метод onReceive() принимает эти сообщения и обрабатывает их в зависимости от того, что нужно разработчику.
Чтобы провайдер принимал сообщения, нужно зарегистрировать его в манифесте. Для этого в файл AndroidManifect.xml в <application> добавим следующий код:
<receiver android:name=".widget.WidgetProvider" android:label="@string/app_name"> <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/> </intent-filter> <meta-data android:name="android.appwidget.provider" android:resource="@xml/network_widget"/> </receiver>
В <intent-filter> указываем, что хотим получать сообщения об обновлении виджета, а в <meta-data> указываем XML-файл с метаданными, который мы создали ранее.
Добавление WidgetRemoteViewsService
Класс WidgetRemoteViewsService выступает посредником между WidgetProvider и WidgetRemoteViewsFactory. Этот сервис должен принимать интент от WidgetProvider и возвращать ему объект WidgetRemoteViewsFactory, который заполняет элемент списка в виджете данными. Создадим класс WidgetRemoteViewsService, наследующий от RemoteViewsService.
public class WidgetRemoteViewsService extends RemoteViewsService { @Override public RemoteViewsFactory onGetViewFactory(Intent intent) { return new WidgetRemoteViewsFactory(this.getApplicationContext(), intent); } }
Как и любой другой сервис, его нужно зарегистрировать в манифестве приложения. Для этого в файл AndroidManifect.xml внутри <application> добавим следующий код:
<service android:name=".widget.WidgetRemoteViewsService" android:permission="android.permission.BIND_REMOTEVIEWS"/>
Разрешение android.permission.BIND_REMOTEVIEWS позволяет системе привязать сервис с целью добавления представления виджета для каждого элемента и не позволяет другим приложения получать доступ к данным виджета.
Добавление WidgetRemoteViewsFactory
Задачей класса WidgetRemoteViewsFactory является заполнение списка в виджете данными. Иными словами, здесь он выступает как адаптер ListView. Для того, чтобы работать со списком, класс должен реализовывать интерфейс RemoteViewsService.RemoteViewsFactory. Создадим класс WidgetRemoteViewsFactory со следующим кодом:
public class WidgetRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory { private Context mContext; private List<WifiInfoWidget> list; private DateFormat dateFormat; private int mWidgetId; public WidgetRemoteViewsFactory(Context context, Intent intent) { mContext = context; mWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); } @Override public void onCreate() { list = new ArrayList<>(); String pattern = ((SimpleDateFormat) DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.getDefault())).toPattern(); dateFormat = new SimpleDateFormat(pattern, Locale.getDefault()); } @Override public void onDataSetChanged() { list.clear(); Set<WifiInfoWidget> networkList = new HashSet<>(); String json = SP.getString(mContext, WIDGET_LIST, null); if (json != null) { networkList = SP.getWidgetList(json); } if (networkList != null) { list.addAll(networkList); } } @Override public void onDestroy() { } @Override public int getCount() { return list.size(); } @Override public RemoteViews getViewAt(int i) { WifiInfoWidget wi = list.get(i); RemoteViews rv = new RemoteViews(mContext.getPackageName(), R.layout.widget_list_item); rv.setTextViewText(R.id.widget_SSID, wi.SSID); if (wi.hidden) { rv.setViewVisibility(R.id.widget_tv_dot, View.VISIBLE); rv.setViewVisibility(R.id.widget_tv_hide, View.VISIBLE); } else { rv.setViewVisibility(R.id.widget_tv_dot, View.INVISIBLE); rv.setViewVisibility(R.id.widget_tv_hide, View.INVISIBLE); } rv.setTextViewText(R.id.widget_password, wi.password); rv.setTextViewText(R.id.widget_date, dateFormat.format(wi.date)); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { rv.setImageViewResource(R.id.widget_copy, R.drawable.ic_content_copy_black_24px); rv.setImageViewResource(R.id.widget_share, R.drawable.ic_share_black_24px); if (wi.widgetExpand) { rv.setImageViewResource(R.id.widget_arrow, R.drawable.ic_keyboard_arrow_up_black_24dp); } else { rv.setImageViewResource(R.id.widget_arrow, R.drawable.ic_keyboard_arrow_down_black_24dp); } } else { Drawable drawableCopy = VectorDrawableCompat.create(mContext.getResources(), R.drawable.ic_content_copy_black_24px, mContext.getTheme()); if (drawableCopy != null) { setDrawable(rv, R.id.widget_copy, drawableCopy); } Drawable drawableShare = VectorDrawableCompat.create(mContext.getResources(), R.drawable.ic_share_black_24px, mContext.getTheme()); if (drawableShare != null) { setDrawable(rv, R.id.widget_share, drawableShare); } Drawable drawableArrow; if (wi.widgetExpand) { drawableArrow = VectorDrawableCompat.create(mContext.getResources(), R.drawable.ic_keyboard_arrow_up_black_24dp, mContext.getTheme()); } else { drawableArrow = VectorDrawableCompat.create(mContext.getResources(), R.drawable.ic_keyboard_arrow_down_black_24dp, mContext.getTheme()); } if (drawableArrow != null) { setDrawable(rv, R.id.widget_arrow, drawableArrow); } } if (wi.widgetExpand) { rv.setViewVisibility(R.id.widget_more, View.VISIBLE); } else { rv.setViewVisibility(R.id.widget_more, View.GONE); } return rv; } @Override public RemoteViews getLoadingView() { return null; } @Override public int getViewTypeCount() { return 1; } @Override public long getItemId(int i) { return i; } @Override public boolean hasStableIds() { return true; } private void setDrawable(RemoteViews rv, int id, Drawable drawable) { Bitmap b = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); Canvas c = new Canvas(b); drawable.setBounds(0, 0, c.getWidth(), c.getHeight()); drawable.draw(c); rv.setImageViewBitmap(id, b); } }
Метод onCreate() вызывается при создании адаптера, здесь мы инициализируем объект List<WifiInfoWidget> и формат даты для показа.
Метод onDataSetChanged() вызывается, когда адаптер обновил виджет. В этом методе забираем сети из SharedPreferences и заполняем им ранее инициализированный список.
Метод onDestroy() вызывается при удалении списка, здесь, если требуется, нужно реализовывать логику очистки.
Метод getCount() возвращает количество элементов в списке.
Метод getViewAt() здесь является самым важным, он выполняет заполнение элемента списка данными, затем возвращает в адаптер посредством сервиса готовый объект RemoteViews.
Метод getLoadingView() возвращает специальный объект View, если элементы списка ещё не успели создаться.
Метод getViewTypeCount() возвращает количество типов представлений в ListView. Поскольку представления в списке одинаковые, возвращаем 1.
Метод getItemId() возвращает ID элемента в выбранной позиции.
Метод hasStableIds() возвращает true, если один и тот же ID всегда относится к одному и тому же объекту.
Старт сервиса в WidgetProvider
Теперь нам нужно подключить к нашему провайдеру адаптер. Для этого добавим в метод updateWidget() класса WidgetProvider следующий код:
private void updateWidget(Context context, AppWidgetManager appWidgetManager, int widgetId) { RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_network); setList(views, context, widgetId); appWidgetManager.updateAppWidget(widgetId, views); } private void setList(RemoteViews views, Context context, int widgetId) { Intent intent = new Intent(context, WidgetRemoteViewsService.class); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId); views.setRemoteAdapter(R.id.widgetList, intent); }
Здесь с помощью метода setRemoteAdapter() мы устанавливаем адаптер списка, который подключается к сервису WidgetRemoteViewsService через специальный интент.
Обработка нажатий на элементы списка
Теперь нам нужно сделать так, чтобы, при нажатии на кнопки в списке виджета, выполнялись определённые операции. Сложность здесь состоит в том, что при использовании коллекций не разрешается устанавливать PendingIntent на отдельные элементы. Поэтому воспользуемся методом setPendingIntentTemplate() для установки шаблона PendingIntent в коллекции, а отдельные элементы будут вызываться посредством метода setOnClickFillInIntent().
Для начала в методе updateWidget() класса WidgetProvider создадим шаблон для коллекции, который будет отправлять в onReceive() событие о нажатии.
public static final String ACTION_ON_ITEM_CLICK = "ON_MORE_CLICK"; public static final String COMMAND = "COMMAND"; public static final String MORE = "MORE"; public static final String CONNECT = "CONNECT"; public static final String COPY = "COPY"; public static final String SHARE = "SHARE"; public static final String ITEM = "ITEM"; ... private void updateWidget(Context context, AppWidgetManager appWidgetManager, int widgetId) { ... final Intent onItemClick = new Intent(context, WidgetProvider.class); onItemClick.setAction(ACTION_ON_ITEM_CLICK); onItemClick.setData(Uri.parse(onItemClick.toUri(Intent.URI_INTENT_SCHEME))); final PendingIntent onClickPendingIntent = PendingIntent.getBroadcast(context, 0, onItemClick, PendingIntent.FLAG_UPDATE_CURRENT); views.setPendingIntentTemplate(R.id.widgetList, onClickPendingIntent); appWidgetManager.updateAppWidget(widgetId, views); }
Затем в классе WidgetRemoteViewsFactory в методе getViewAt() добавим интенты при нажатии на кнопки.
@Override public RemoteViews getViewAt(int i) { ... if (wi.widgetExpand) { rv.setViewVisibility(R.id.widget_more, View.VISIBLE); rv.setOnClickFillInIntent(R.id.btn_widget_connect, createIntent(WidgetProvider.CONNECT, wi)); rv.setOnClickFillInIntent(R.id.widget_copy, createIntent(WidgetProvider.COPY, wi)); rv.setOnClickFillInIntent(R.id.widget_share, createIntent(WidgetProvider.SHARE, wi)); } else { rv.setViewVisibility(R.id.widget_more, View.GONE); } rv.setOnClickFillInIntent(R.id.widget_arrow, createIntent(WidgetProvider.MORE, wi)); return rv; } private Intent createIntent(String cmd, WifiInfoWidget wi) { Intent intent = new Intent(); intent.setAction(WidgetProvider.ACTION_ON_ITEM_CLICK); Bundle bundle = new Bundle(); bundle.putString(WidgetProvider.COMMAND, cmd); bundle.putSerializable(WidgetProvider.ITEM, wi); intent.putExtras(bundle); return intent; }
В объект Bundle кроме данных о сети мы также добавляем команду, по которой провайдер будет различать, нажатие на какую кнопку было совершено и какие действия нужно выполнить.
Нажатия на кнопки реализованы, теперь нужно их обработать. Вернёмся в класс WidgetProvider, в метод onReceive() добавим следующий код:
@Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); if (BuildConfig.DEBUG) Log.d(TAG, action); if (!TextUtils.isEmpty(action)) { if (action.equals(AppWidgetManager.ACTION_APPWIDGET_UPDATE)) { AppWidgetManager manager = AppWidgetManager.getInstance(context); ComponentName cn = new ComponentName(context, WidgetProvider.class); manager.notifyAppWidgetViewDataChanged(manager.getAppWidgetIds(cn), R.id.widgetList); } if (action.equals(ACTION_ON_ITEM_CLICK)) { parseItemClick(context, intent.getExtras()); } } super.onReceive(context, intent); } private void parseItemClick(Context context, Bundle bundle) { if (bundle != null) { String command = bundle.getString(COMMAND); if (!TextUtils.isEmpty(command)) { if (BuildConfig.DEBUG) Log.d(TAG, command); switch (command) { case MORE: { WifiInfoWidget wi = (WifiInfoWidget) bundle.getSerializable(ITEM); if (wi != null) { wi.widgetExpand = !wi.widgetExpand; Set<WifiInfoWidget> networkList = new HashSet<>(); String json = SP.getString(context, WIDGET_LIST, null); if (json != null) { networkList = SP.getWidgetList(json); } if (networkList != null) { for (WifiInfoWidget wifiInfo : networkList) { if (wifiInfo.equals(wi)) { wifiInfo.widgetExpand = wi.widgetExpand; SP.saveWidgetList(context, networkList); sendRefreshBroadcast(context); break; } } } } break; } case CONNECT: { WifiInfoWidget wi = (WifiInfoWidget) bundle.getSerializable(ITEM); if (wi != null) { App.selectContent("widget", "подключить сеть"); WifiManager wifiManager = (WifiManager) context.getApplicationContext() .getSystemService(Context.WIFI_SERVICE); if (wifiManager == null) { return; } Toast.makeText(context, R.string.wifi_changing_network, Toast.LENGTH_SHORT).show(); Set<WifiInfo> networkList = new HashSet<>(); String json = SP.getString(context, MAIN_LIST, null); if (json != null) { networkList = SP.getList(json); } if (networkList != null) { for (WifiInfo wifiInfo : networkList) { if (wifiInfo.SSID.equals(wi.SSID) && wifiInfo.password.equals(wi.password) && wifiInfo.hidden == wi.hidden) { WifiConfigManager wcf = new WifiConfigManager(wifiManager); if (wcf.getStatus().toString().equals("PENDING")) wcf.execute(wifiInfo); break; } } } } break; } case COPY: { WifiInfoWidget wi = (WifiInfoWidget) bundle.getSerializable(ITEM); if (wi != null) { App.selectContent("widget", "копировать"); Tools.CopyToClipboard(context, wi.password); Toast.makeText(context, R.string.Copy_value, Toast.LENGTH_LONG).show(); } break; } case SHARE: { WifiInfoWidget wi = (WifiInfoWidget) bundle.getSerializable(ITEM); if (wi != null) { App.selectContent("widget", "строка логин-пароль"); String s = "SSID : " + wi.SSID + "\nPassword : " + wi.password; Intent i = new Intent(); i.setAction(Intent.ACTION_SEND); i.putExtra(Intent.EXTRA_TEXT, s); i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); i.setType("test/plain"); context.startActivity(i); } break; } } } } }
Таким образом мы можем легко обрабатывать нажатия на различные элементы списка.
Обновление виджета из приложения
Осталось малое: посылать в виджет широковещательное сообщение, которое будет запускать обновление при добавлении/удалении элемента из списка в приложении. Для этого в классе WidgetProvider добавим метод sendRefreshBroadcast(), который будет отправлять в onReceive() сообщение об обновлении виджета, что затем вызовет всю цепочку WidgetProvider – WidgetRemoteViewsService – WidgetRemoteViewsFactory – WidgetProvider.
public static void sendRefreshBroadcast(Context context) { Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); intent.setComponent(new ComponentName(context, WidgetProvider.class)); context.sendBroadcast(intent); }
Метод этот будем вызывать в классе главной активности в местах, где происходит изменение списка.
WidgetProvider.sendRefreshBroadcast(mainView.getContext());
На этом всё. Результат того, как работает наш виджет, вы можете увидеть ниже.