Блог о Gentoo и около-линуксовым штукам

10 июля 2017 г.

Пишем виджет для Android (с конфигуратором и профусктками)

5:49 Опубликовал Дмитрий Исаенко , Нет комментариев
Структурированно рассказать о том, как работает виджет сложновато, т. к. все составляющие очень тесно взаимосвязаны и всё это вызывает друг-друга туда-сюда как только хочет. Но давайте попробуем разобраться на живом примере. Полный код того, что получилось в конце страницы и на GitHub.

Виджеты бывают с «окном» конфигуратора и без него. Это «окно» является Activity, вызываемым при первом добавлении виджета на экран. Виджеты с ним и без него несколько отличаются в поведении. Такой конфигуратор нужен в основном для того, чтобы предопределить параметры для каждого нового добавляемого виджета. Это не значит, что добавляемые виджеты не могут быть одинаковыми, т.е. они могут, но без конфигуратора они будут одинаковыми гарантированно.
Итак конфигуратор, это обычная Activity которая получает параметры добавляемого виджета используя Intent, строго говоря она получает EXTRA_APPWIDGET_ID — ID добавляемого виджета и описывает какие параметры будут отображаться на виджете. По завершении, конфигуратор возвращает это значение назад и посылает RESULT_OK используя результирующий Intent. Как сказано ранее, конфигуратор должен не только вернуть эти данные, но и обновить объект RemoteViews, используемый для настройки отображаемых в виджете вещей, т. к. при добавлении виджета с помощью конфигуратора метод onUpdate() вызван не будет. Другими словами конфигуратор должен выставить необходимый текст в  TextView виджета, добавить слушателей на кнопки виджета и т.п. Определяется Activity конфигуратора в файле настройки XML виджета (см.далее).
В случае, если конфигуратора нет, при добавлении виджета будет вызван метод onUpdate() в котором как правило и описывает большая часть того, что виджет должен делать. Подробнее о методах виджета поговорим чуть позже.

Задачу поставим так: сделать виджет с конфигуратором, который будет представлять собой текст и кнопку. По нажатии на кнопку будет вызываться конфигуратор. В конфигураторе будет EditText и кнопка для применения изменений. По нажатии на неё текст в виджете будет меняться на установленный в EditText конфигуратора.

Шаг 1. Для начала создадим проект с пустой MainActivity (File → New Project → …).  Создадим внутри нашего проекта (пусть это будет в пакете java/widgets/) два java класса:
MyWidget    — сам будущий виджет
MyWidgetConfig — окно конфигурации для виджета
Вернёмся к ним позже.

Шаг 2
. Создаём XML файл в res/xml/example_info.xml ('это наш AppWidget Provider) Это то, что будет описывать нюансы самого виджета и опишет связку будущего виджета с activity конфигуратора (то окно, которое будет запускаться при добавлении виджета на экран):
<appwidget-provider 
    android:configure="widgets.MyWidgetConfig" 
    android:minheight="40dp" android:minwidth="110dp" 
    android:resizemode="horizontal|vertical" 
    android:updateperiodmillis="1800000" 
    xmlns:android="http://schemas.android.com/apk/res/android">
</appwidget-provider>
Пройдёмся по основным составляющим этого файла.
configure определяет activity отвественную за конфигурацию виджета при добавлении и вообще.
minHeight и minWidth определяет минимальные размеры для корректного отображения виджета. К сожалению, тут нет механизма в стиле “1 клеточка на 2 клеточки”. На странице документации по andoroid есть неплохое описание какие размеры должны быть (См. ссылки в конце статьи). В этом примере 1 клетка в высоту, 2 в ширину.
updatePeriodMillis может быть установлен в  1800000 (30мин). Меньше вроде как не умеет (Вообще умеет, но требует реализации таймера).
resizeMode определяет как пользователь может изменять размеры виджета. Можно установить в “none”, тогда размеры изменять будет нельзя. Можно в “horizontal”, можно  в “vertical”, можно в оба одновременно “horizontal|vertical”. Названия говорят сами за себя.

Шаг 3
. Заходим в AndroidManifest.xml
Добавляем наш виджет и его кофигуратор в секцию <application>:
<!-- added -->
<receiver android:name="widgets.MyWidget">
    <intent-filter>
        <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
    </intent-filter>
    <meta-data android:name="android.appwidget.provider"
        android:resource="@xml/example_info" />
</receiver>
<activity android:name="widgets.MyWidgetConfig">
    <intent-filter>
        <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
    </intent-filter>
</activity>
<!-- added -->
Шаг 4. Добавляем UI. Добавляем в layout/ два новых файла через New->XML->Layout XML widget_main.xml widget_main_config.xml В widget_main_config.xml закидываем элементы EditText, Button со следующими ID: configWidgetText configWidgetBtn.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <EditText
        android:id="@+id/configWidgetText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ems="10"
        android:inputType="textPersonName"
        android:text="Name" />
    <Button
        android:id="@+id/configWidgetBtn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Button" />
</LinearLayout>
Добавим в widget_main следующие элементы: TextView, Button со следующими ID:
widgetText
widgetBtn

Установим ширину и высоту для layout в значения “wrap_content”.

Итого:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical">
    <TextView
        android:id="@+id/widgetText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="TextView" />
    <Button
        android:id="@+id/widgetBtn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Button" />
</LinearLayout>
Шаг 5. Вернёмся к MyWidget.java
Виджеты являются в определённом роде BroadcastReciever которые обрабатывают определённые события. Как и BroadcastReciever, обрабатывают действия они методом onRevieve() который в дальнейшем обрабатывает данные другими методами. Тем не менее, виджет является наследником класса AppWidgetProvider (в свою очередь, наследник того самого BroadcastReciever), который всё несколько упрощает.
Рассмотрим что нам предлагает AppWidgetProvider:
onEnabled — вызывается один раз, когда виджет создаётся (добавляется на экран).
onUpdate — вызывается при обновлении виджета в заданный в XML файле интервал. Основной функционал виджета находится именно здесь. Как уже было сказано, если вам нужно вызывать этот метод чаще чем раз в 30 минут, следует добавить таймер (используя класс AlarmManager).
onDeleted — вызывается, когда виджет удалён с экрана.
onDisabled — вызывается, когда все виджеты удалены с экрана. Т.е. можно добавлять сколько угодно экземпляров этого виджета, и когда последний из добавленных будет удалён, этот метод сработает. В этом и отличие от onDeleted
onRestored — вызывается при восстановлении AppWidget из бекапа. Откликается на ACTION_WIDGET_RESTORED. В обычном случае будет сразу вызван onUpdate. Даже не знаю как этот метод можно использовать.
OnAppWidgetOptionsChanged — вызывается при изменении размеров виджета (да, если такие изменения вообще разрешены в XML настройках).
onReceive — вызывается для обработки событий другими методами.

Наследуем наш класс от AppWidgetProvider (extends AppWidgetProvider).
Жмём правой кнопкой на классе, потом Generate -> Override Methods…
Выбираем onUpdate и onDelete. Первый будет вызываться при обновлении виджета, второй — при удалении. 

Для меня стало открытием, так что сейчас покажу :)

Для разнообразия, добавим всплывающее окно при удалении виджета через Toast:
@Override
public void onDeleted(Context context, int[] appWidgetIds) {
    super.onDeleted(context, appWidgetIds);
    Toast.makeText(context, "Widget deleted", Toast.LENGTH_SHORT).show();
}
Теперь переместимся в метод onUpdate.
Важно!
Этот метод будет вызываться в зависимости от настроек из  res/xml/example_info.xml но при создании через конфигуратор он вызван не будет. Будет вызван onEnabled() (но и то как-то, по моим ощущениям, кривовато).

Для начала, установим что-нибудь в первое поле TextView. Какой смысл добавлять что-то в TextView, если это изменит текст через заданный интервал, тем более, что мы этот текст выставляем в конфигураторе? Смысла в этом нет. Просто это даст чуть более полную картину того, как работать с виджетами. В чём есть смысл, так это в определении кнопке действия, т.к. после перезагрузки устройства работать она перестанет (мы говорим о том, что если действие для кнопки будет определяться в конфигруаторе, то эта привязка перестанет работать после перезагурзки, т.к. в этом случае созданием виджета будет заниматься не конфигуратор а система). Но определять их надо в onEnabled, т.к. после перезагруки именно он и будет вызван.
Создадим экземпляр класса RemoteViews (референс: RemoteViews(String packageName, int layoutId) ). Где обращаемся к context из пренимаемых onUpdate параметров (берем оттуда имя) и layout самого виджета. Установим текст "Hello widget!" методом .setTextViewText  (референс: setTextViewText(int viewId, CharSequence text)). После чего добавим ещё одну строку, которая укажет приложению на необходимость применить сделанные изменения. Такой финт не типичен для работы с Activity, но для виджетов каждое изменение в виджете должно сопровождаться обновлением.
Без такого обновления будет вылетать ошибка “Problem Loading Widget”. Проделывать эти действия надо со всеми appWidgetIds. Используем конструкцию for (foreach):
for (int appWidgetId : appWidgetIds) {
    RemoteViews view = new RemoteViews(context.getPackageName(), R.layout.widget_main);
    view.setTextViewText(R.id.widgetText, "Hello widget!");
    appWidgetManager.updateAppWidget(appWidgetId, view);
}

Добавим метод onEnabled и назначим кнопке на виджете действе: вызов конфигуратора по нажатию. В обычной Activity достаточно было бы создать Intent, но в виджетах используется несколько более сложная схема. Итак, необходимо:
  1. Создать Intent. В качетсве параметров — контекст и имя класса Activity-сонфигуратора с .class в конце. 
  2. Добавить в него ID виджета, который запрашивает вызов конфигуратора (с помошью .putExtra). Ожидается переменная int для «поля» AppWidgetManager.EXTRA_APPWIDGET_ID 
  3. Создать объект PendingIntent, который будет назначен кнопке для посылки-по-нажатию. См.аргументы ниже. 
Попутно определим все необходимые параметры ещё раз (тут не плохо было бы подумать о том, как не повторяться с тем, что мы описывали  в onUpdate). В отличии от onUpdate, принимаемое значение метода только context, так что надо вытащить откуда-то и ID. Для этого вводим объект класса ComponentName. Итак код:
    @Override
    public void onEnabled(Context context) {
        RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_main);
        AppWidgetManager awm = AppWidgetManager.getInstance(context);
        ComponentName compName = new ComponentName(context, MyWidget.class);
        int[] widgetIds = awm.getAppWidgetIds(compName);
        for (int widgetId : widgetIds) {
            Intent intentBtnPwr = new Intent(context, MyWidgetConfig.class);
            intentBtnPwr.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId);
            PendingIntent pi = PendingIntent.getActivity(context, widgetId, intentBtnPwr, PendingIntent.FLAG_UPDATE_CURRENT);
            remoteViews.setOnClickPendingIntent(R.id.widgetBtn, pi);
            awm.updateAppWidget(widgetId, remoteViews);
        }
    }

Шаг 6. Перейдём к MyWidgetConfig.java Объявляем переменные для всего класса. EdText — поле ввода на экране конфигурации виджета awManager — менеджер, который понадобиться для работы с виджетом, thisContext — контекст awID — переменная для хранения ID добавляемого виджета Кнопку добавлять не будем, просто привяжем её к Listener и опишем необходимое поведение позже.
public class NoWolWidgetConfig extends Activity {
    EditText EdText;
    AppWidgetManager awManager;
    Context thisContext;
    int awID;
...
Наследуем класс от Activity, добавляем метод onCreate через Generate… (тем же способом, что описан выше). Ну и сразу привязываем класс к layout.
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.widget_main_config);
…
Теперь установим связку для EditText
EdText = (EditText)findViewById(R.id.configWidgetText);
Опишем Intent, чтобы получить информацию для текущей Activity о том, какой виджет вызвал эту Activity. В первый раз, при добавлении виджета, он помещается на экран и получает свой ID, после чего запускается конфигуратор. Если результат выполнения конфигуратора не успешен, или он был закрыт (кнопкой назад, например) виджет удаляется, т. к. не получает результрующего статуса RESULT_OK от конфигуратора. Также во входящем Intent конфигуратор получает ID экземпляра виджета и обязан по завершению передать его обратно, также используя механизм Intent. Объявим Intent и привяжем его к полученному:
Intent intent = getIntent();
Создадим хранилище (Bundle) и выгрузим в него данные из полученного интента —присвоем awID значение ID виджета. В случае неудачи просто закроем конфигуратор и тогда виджет создан не будет.
Bundle bundleExtras = intent.getExtras();
if (bundleExtras != null){
    awID = bundleExtras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
}else
 finish();
Теперь определим контекст в переменную и с его использованием привяжем AppWidgetManager к нашему классу (нужен для такой же работы с виджетом, как и внутри самого кода виджета). И да, мы уже делали такое чуть ранее:
thisContext = NoWolWidgetConfig.this;
awManager = AppWidgetManager.getInstance(thisContext);
Создадим переменную для RemoteViews которая позволит обращаться к элементам виджета.
final RemoteViews awRV = new RemoteViews(thisContext.getPackageName(), R.layout.widget_main);
Теперь привяжем нашу единственную кнопку к listener и опишем её поведение.
findViewById(R.id.configWidgetBtn).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        // Action starts here ;)
    }
});
Как вы заметили, для неё нет отдельной переменной. Но работать будет :)
Теперь заставим по клику в конфигураторе брать значение из EditText и засовывать его во второй TextView нашего виджета:
awRV.setTextViewText(R.id.widgetText, ((EditText) findViewById(R.id.configWidgetText)).getText().toString());
Назначим кнопке действие тем же способом, что и ранее мы проделывали в виджете в методе onEnabled.
Intent intentBtnPwr = new Intent(thisContext, MyWidgetConfig.class);
                intentBtnPwr.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, awID);
                PendingIntent pi = PendingIntent.getActivity(thisContext, awID, intentBtnPwr, PendingIntent.FLAG_UPDATE_CURRENT);
                awRV.setOnClickPendingIntent(R.id.widgetBtn, pi);
После всего этого необходимо обновить виджет используя метод .updateAppWidget объекта класса AppWidgetManager. Он обновит в виджете только то, что мы изменили в виджете и не тронет другое. Проще говоря, смотрите на это как на кнопку «Применить».
awManager.updateAppWidget(awID, awRV);
Теперь мы передаём результат (ID виджета) в Intent в наш виджет и закрываем Activity конфигуратора. Так виджет будет знать, что конфигуратор успешно выполнился и будет отображен на экране. Точнее сказать, он не будет с него удалён, что произошло бы при закрытии конфигуратора (повторяюсь).
Intent resultIntent = new Intent();
resultIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, awID);
setResult(RESULT_OK, resultIntent);
finish();
Вот что у нас получилось:

Получилось вертикальное видео. На этом пожалуй всё. Как и обещал, код основных компонентов полностью:
manifests/AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.blogspot.developersu.we.widgetexample">

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

    <!-- added -->
    <receiver android:name="widgets.MyWidget">
        <intent-filter>
            <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
        </intent-filter>
        <meta-data android:name="android.appwidget.provider"
            android:resource="@xml/example_info" />
    </receiver>
    <activity android:name="widgets.MyWidgetConfig">
        <intent-filter>
            <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
        </intent-filter>
    </activity>
    <!-- added -->
</application>

</manifest>
com.blogspot.developersu.we.widgetexample/MainActivity
package com.blogspot.developersu.we.widgetexample;
/*
*  It doesn't make sense to cover this code by any license, 'cause everything described here
*  has been widely used by thousands of coders all around the world. So most likely I've already
*  stolen this code in their eyes and you're going to steal it from me. LOL.
*  Bye!
*
*  Dmitry Isaenko,
*  2017, Russia
*  https://developersu.blogspot.com/2017/07/androidwidget.html
*
 */
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {
int awID;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

    }
}
widgets/MyWidget
package widgets;

import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.widget.RemoteViews;
import android.widget.Toast;

import com.blogspot.developersu.we.widgetexample.R;

public class MyWidget extends AppWidgetProvider {

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        super.onUpdate(context, appWidgetManager, appWidgetIds);
        for (int appWidgetId : appWidgetIds) {
            RemoteViews view = new RemoteViews(context.getPackageName(), R.layout.widget_main);
            view.setTextViewText(R.id.widgetText, "Hello widget!");
            appWidgetManager.updateAppWidget(appWidgetId, view);
        }
    }

    @Override
    public void onDeleted(Context context, int[] appWidgetIds) {
        super.onDeleted(context, appWidgetIds);
        Toast.makeText(context, "Widget deleted", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onEnabled(Context context) {
        RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_main);
        AppWidgetManager awm = AppWidgetManager.getInstance(context);
        ComponentName compName = new ComponentName(context, MyWidget.class);
        int[] widgetIds = awm.getAppWidgetIds(compName);
        for (int widgetId : widgetIds) {
            Intent intentBtnPwr = new Intent(context, MyWidgetConfig.class);
            intentBtnPwr.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId);
            PendingIntent pi = PendingIntent.getActivity(context, widgetId, intentBtnPwr, PendingIntent.FLAG_UPDATE_CURRENT);
            remoteViews.setOnClickPendingIntent(R.id.widgetBtn, pi);
            awm.updateAppWidget(widgetId, remoteViews);
        }
    }
}
widgets/MyWidgetConfig
package widgets;


import android.app.Activity;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.RemoteViews;

import com.blogspot.developersu.we.widgetexample.R;


public class MyWidgetConfig extends Activity {

    EditText EdText;
    AppWidgetManager awManager;
    Context thisContext;
    int awID;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.widget_main_config);

        EdText = (EditText)findViewById(R.id.configWidgetText);

        Intent intent = getIntent();
        Bundle bundleExtras = intent.getExtras();
        if (bundleExtras != null){
            awID = bundleExtras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
        }else
            finish();

        thisContext = this.getApplicationContext();
        awManager = AppWidgetManager.getInstance(thisContext);
        final RemoteViews awRV = new RemoteViews(thisContext.getPackageName(), R.layout.widget_main);

        findViewById(R.id.configWidgetBtn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                awRV.setTextViewText(R.id.widgetText, ((EditText) findViewById(R.id.configWidgetText)).getText().toString());
                //*added
                Intent intentBtnPwr = new Intent(thisContext, MyWidgetConfig.class);
                intentBtnPwr.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, awID);
                PendingIntent pi = PendingIntent.getActivity(thisContext, awID, intentBtnPwr, PendingIntent.FLAG_UPDATE_CURRENT);
                awRV.setOnClickPendingIntent(R.id.widgetBtn, pi);
                // added end */
                awManager.updateAppWidget(awID, awRV);

                Intent resultIntent = new Intent();
                resultIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, awID);
                setResult(RESULT_OK, resultIntent);
                finish();
            }
        });
    }
}
res/layout/activity_main
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.blogspot.developersu.we.widgetexample.MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>
res/layout/widget_main
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <TextView
        android:id="@+id/widgetText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="TextView" />

    <Button
        android:id="@+id/widgetBtn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Button" />

</LinearLayout>
res/layout/widget_main_config
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <EditText
        android:id="@+id/configWidgetText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ems="10"
        android:inputType="textPersonName"
        android:text="Name" />

    <Button
        android:id="@+id/configWidgetBtn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Button" />
</LinearLayout>
res/xml/example_info
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:configure="widgets.MyWidgetConfig"
    android:minHeight="40dp"
    android:minWidth="110dp"
    android:updatePeriodMillis="1800000"
    android:resizeMode="horizontal|vertical"
    />

 p.s. Есть небольшая проблема с MainActivity. Если он открыт (onPause, а не onActive, очевидно), и вы вызываете конфигуратор кнопкой на виджете, то MainActivity будет отображен после внесения изменений, хотя ожидается увидеть экран с виджетом. Виджет при этом отобразит сделанные в кнофигураторе изменения.

0 коммент.:

Отправить комментарий