Изменение размера виджета в Android 4.1 Jelly Bean

в 16:13, , рубрики: widget, костыль, Разработка под android, метки: ,

Как стало известно — в новой версии Android Jelly Bean (API v16) появилась возможность изменения размера виджета, да не просто изменения, а автоматического, который теперь подстраивается под свободное место на экране.
Функция удобная и полезная, но в официальной документации про это почти ничего нету, что довольно странно.
Изменение размера виджета в Android 4.1 Jelly Bean
Так как я разрабатываю несколько виджетов, то решил добавить данный функционал.

Первое что я заметил, это то, что виджет вообще не меняет размер, это решилось просто добавлением 3 строк в widget_provider.xml (res/xml):

android:resizeMode="horizontal|vertical"
android:minResizeHeight="72dip"
android:minResizeWidth="72dip"

android:resizeMode отвечает за направление изменения размера виджета и имеет 3 возможных значения:

  • horizontal — позволяет растягивать виджет по горизонтали
  • vertical — позволяет растягивать виджет по вертикали
  • none — запрещает растягивать виджет (по умолчанию)

android:minResizeHeight и android:minResizeWidth отвечают за минимальный размер виджета, который можно выставить.
В коде выше виджет можно растягивать в любом направлении и его минимальный размер 1*1 ячейка.

Вроде все стало отлично, и все работает, однако какого размера стал виджет не ясно.
В документации нашел упоминание метода onAppWidgetExtrasChanged(Context context, AppWidgetManager appWidgetManager,int appWidgetId, Bundle newExtras), который должен вызываться при изменении размера, но у меня он так ни разу и не вызвался.
Далее обнаружилось, что при изменении размера виджету отправляется новый броадкаст "android.appwidget.action.APPWIDGET_UPDATE_OPTIONS". Следуя логике можно было предположить, что с ним должен отправиться и новый размер. Так и оказалось.
Но отправляется он не в очень явном виде. В итоге получился такой код:

        final String action = intent.getAction();
        if (action.equalsIgnoreCase("android.appwidget.action.APPWIDGET_UPDATE_OPTIONS")) {

            Bundle b = intent.getBundleExtra("appWidgetOptions");
            int appWidgetMinWidth = (Integer) b.get("appWidgetMinWidth");
         // int appWidgetMaxWidth = (Integer)  b.get("appWidgetMaxWidth");
         // int appWidgetMinHeight = (Integer)  b.get("appWidgetMinHeight");
            int appWidgetMaxHeight = (Integer)  b.get("appWidgetMaxHeight");
            int w = 1;
            int h = 1;
            for (int i=1;i<=10;i++) {
                if  (appWidgetMinWidth==i*80) {w=i; break;}
            }
            for (int i=1;i<=10;i++) {
                if  (appWidgetMaxHeight==(i*100)) {h=i; break;}
            }
		Log.i("Widget","Width="+w+" Height="+h);

Отправляются 4 размера — минимальная и максимальная ширина и высота. В ходе эксперимента выяснилось, что эти размеры постоянны и удобнее ориентироваться на минимальную ширину и максимальную высоту.
При изменении размера виджета в лог выведется его размер в количестве ячеек. Далее этот размер можно сохранить для данного виджета (его ID: int id = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);) и при обновлении считывать ти данные и подстраивать параметры.

В более ранних версия Android такой функции естественно небыло, и многие разработчики, да и я в том числе, делали несколько размеров виджета, дабы предоставить людям выбор, теперь этот выбор не нужен, поэтому по хорошему, оставить пользователю с Jelly Bean 1 виджет и функцию масштабирования, а пользователям более ранних версий несколько виджетов с разными размерами, но все это что бы было в 1 пакете.
реализовать это оказалось очень просто:
Создаем в папке res/values файл bool.xml с содержимым

<resources>
    <bool name="v16">true</bool>
</resources>

И в папке res/values-v16 такой же файл, но нескольки иным содержимым:

<resources>
    <bool name="v16">false</bool>
</resources>

После чего в AndroidManifest.xml у ресиверов виджетов, которые необходимо скрыть в новой версии дописываем

android:enabled="@bool/v16"

И все, начиная с API v16 данные виджеты будут отключены, и в списке не отображаться.

P.S. данный метод проверялся на MDPI и HDPI экранах с размером сетки лаунчера 4*4, просьба пользователей планшетов с Android Jelly Bean и размером сетки большим чем 4*4 проверить метод, возможно размеры будут изменяться по несколько другой последовательности.

Автор: BOOMik

* - обязательные к заполнению поля