1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/whyyy-ExToast

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
В этом репозитории не указан файл с открытой лицензией (LICENSE). При использовании обратитесь к конкретному описанию проекта и его зависимостям в коде.
Клонировать/Скачать
Внести вклад в разработку кода
Синхронизировать код
Отмена
Подсказка: Поскольку Git не поддерживает пустые директории, создание директории приведёт к созданию пустого файла .keep.
Loading...
README.md

ExToast

Основные возможности:

  1. Расширение времени отображения toast, возможность установки произвольного времени или постоянного отображения
  2. Расширение анимации появления и исчезновения toast

Краткое описание:
В Android-приложении часто требуется выводить небольшие информационные сообщения на экран, которые не влияют на работу приложения. Обычно это делается с помощью встроенной в Android библиотеки Toast.

Toast.makeText(mContext, "Сообщение", Toast.LENGTH_SHORT).show();

Начинаешь использовать его и думаешь, что он удобен, и начинаешь показывать любую информацию через Toast. Однако со временем выясняется, что стандартный вид Toast недостаточно привлекателен, а также ограничены варианты анимации появления и исчезновения. Время отображения-toast можно выбрать только между SHORT и LONG.


После изучения исходного кода Toast была создана расширенная версия ExToast, которая предоставляет следующие методы для изменения содержимого:

setView(View):void
setDuration(int):void
setMargin(float,float):void
setGravity(int,int,int):void
setText(int):void
setText(CharSequence):void

Это позволяет заменять представление, устанавливать время отображения, задавать отступы, управлять положением отображения и менять текстовое содержание.

На основе стандартной реализации Toast были добавлены два новых метода:

setDuration(int):void
setAnimations(int):void
```Метод setDuration теперь позволяет задавать произвольное время отображения в секундах. Предоставлены три значения по умолчанию: `LENGTH_SHORT`, `LENGTH_LONG`, `LENGTH_ALWAYS`. Последнее значение (`LENGTH_ALWAYS`) означает, что Toast будет постоянно отображаться до вызова метода hide().

---

Пример использования ExToast:

```java
ExToast exToast = ExToast.makeText(context, "сообщение", ExToast.LENGTH_ALWAYS);
exToast.setAnimations(R.style.anim_view);
exToast.show();
// При использовании LENGTH_ALWAYS следует вызвать hide() в подходящий момент
exToast.hide();

Вышеуказанный код позволяет задавать собственные xml-анимации для Toast и обеспечивает длительное отображение сообщений. Далее рассмотрим пример стилизованных анимаций.

styles.xml
<style name="anim_view">
    <item name="@android:windowEnterAnimation">@anim/anim_in</item>
    <item name="@android:windowExitAnimation">@anim/anim_out</item>
</style>
anim_in.xml
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:fromXDelta="0"
        android:fromYDelta="0"
        android:toXDelta="0"
        android:toYDelta="85"
        android:duration="1"
        />
    <translate
        android:fromXDelta="0"
        android:fromYDelta="0"
        android:toXDelta="0"
        android:toYDelta="-105"
        android:duration="350"
        android:fillAfter="true"
        android:interpolator="@android:anim/decelerate_interpolator"
        />
    <alpha
        android:fromAlpha="0"
        android:toAlpha="1"
        android:duration="100"
        />
    <translate
        android:fromXDelta="0"
        android:fromYDelta="0"
        android:toXDelta="0"
        android:toYDelta="20"
        android:duration="80"
        android:fillAfter="true"
        android:startOffset="350"
        />
</set>
`anim_out.xml````xml
```

Для просмотра конкретного эффекта запустите демонстрацию.


Анализ работы ExToast

Каждый, кто использовал Toast, знает, что он предоставляет два временных интервала — LENGTH_SHORT и LENGTH_LONG, продолжительность которых составляет 2 секунды и около 3 секунд соответственно. Для всех Toast'ов, которые отображаются менее чем за 3 секунды, можно использовать toast.cancel(), чтобы прекратить их отображение. Однако если требуется отобразить Toast с длительностью более 3 секунд, это становится невозможным.

Проблема времени отображения ещё не самая серьёзная. Самой серьёзной проблемой является то, что системные Toast'ы отображаются в порядке очереди, и следующий Toast начинает отображаться только после того, как текущий завершится.

Многие студенты сталкиваются с этой проблемой, например, при создании кнопки, которая показывает Toast при нажатии. При проведении небольшого теста на нагрузку, такого как быстрое нажатие кнопки сохранения, очередь Toast'ов может стать очень длинной и продолжать отображаться несколько минут.

Чтобы понять работу метода show() в классе Toast, рассмотрим его реализацию:

public void show() {
    if (mNextView == null) {
        throw new RuntimeException("setView must have been called");
    }
```    INotificationManager service = getService();
    String pkg = mContext.getPackageName();
    TN tn = mTN;
    tn.mNextView = mNextView;

    try {
        service.enqueueToast(pkg, tn, mDuration);
    } catch (RemoteException e) {
        // Пустое
    }
}
```Можно заметить, что основное отображение и скрытие Toast заключены в методе `enqueueToast` интерфейса `INotificationManager`. Видя слово "enqueue", можно понять, что это функция обработки очереди, которая принимает три аргумента: packageName, объект tn и продолжительность. Соответственно с эффектом отображения Toast, можно предположить, что внутренняя реализация этого метода состоит в последовательном отображении и скрытии каждого переданного Toast. Package name и продолжительность мы уже знаем, так что акцент делается именно на этом объекте tn.

<font color=#0099ff><b>*Но что же такое этот объект tn?*</b></font>

Продолжая изучение исходного кода Toast, можно узнать, что Toast является конкретной формой системного плавающего окна. Его ключевым моментом является приватный статический вложенный класс `class TN`, который занимается отображением и скрытием Toast. <font color=#0099ff><b>*Поэтому, используя рефлексию, можно получить этот объект TN и активно управлять отображением и скрытием Toast, минуя системный сервис*</b></font>Исходный код класса TN:
```java
private static class TN extends ITransientNotification.Stub {
    final Runnable mShow = new Runnable() {
        @Override
        public void run() {
            handleShow();
        }
    };
    final Runnable mHide = new Runnable() {
        @Override
        public void run() {
            handleHide();
            // Do not do this in handleHide(), as it is also called by the handleShow() method
            mNextView = null;
        }
    };
    ...
    final Handler mHandler = new Handler();
    ...
    View mView;
    View mNextView;
    WindowManager mWM;
}
```    TN() {
        final WindowManager.LayoutParams params = mParams;
        params.height = WindowManager.LayoutParams.WRAP_CONTENT;
        params.width = WindowManager.LayoutParams.WRAP_CONTENT;
        params.format = PixelFormat.TRANSLUCENT;
        params.windowAnimations = com.android.internal.R.style.Animation_Toast;
        params.type = WindowManager.LayoutParams.TYPE_TOAST;
        params.title = "Toast";
        params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |
                        WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
                        WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
    }

    /**
     * планирование выполнения метода handleShow в правильном потоке
     */
    @Override
    public void show() {
        if (localLOGV) Log.v(TAG, "SHOW: " + this);
        mHandler.post(mShow);
    }

    /**
     * планирование выполнения метода handleHide в правильном потоке
     */
    @Override
    public void hide() {
        if (localLOGV) Log.v(TAG, "HIDE: " + this);
        mHandler.post(mHide);
    }```markdown
    private void trySendAccessibilityEvent() {. . . }
     public void handleHide() {
         . . .
         if (mView != null) {
             // Note: the parent() check is performed only to ensure that 
             // the view has been added. I have seen cases where we reach 
             // this point when the view has not yet been added, so let's 
             // try to avoid a crash.
             if (mView.getParent() != null) {
                 . . .
                 mWM.removeView(mView);
             }
             mView = null;
         }
     }
 }
``````java
public void show(){
	...
	WindowManager mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
	mWN.addView(mView, mParams);
}

public void hide(){
	...
	WindowManager mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
	mWN.removeView(mView);
}

Суть работы Toast заключается в добавлении и удалении view через WindowManager. Для реализации пользовательской длительности отображения можно переопределить методы show() и hide().

private void initTN() {
    try {
        Field tnField = toast.getClass().getDeclaredField("mTN");
        tnField.setAccessible(true);
        mTN = (ITransientNotification) tnField.get(toast);

        /** Установите mNextView перед вызовом tn.show() */
        Field tnNextViewField = mTN.getClass().getDeclaredField("mNextView");
        tnNextViewField.setAccessible(true);
        tnNextViewField.set(mTN, toast.getView());

    } catch (Exception e) {
        e.printStackTrace();
    }
}

public void show(){
	initTN();
	mTN.show();
}

Объект mTN получается из Toast с помощью рефлексии, тип объекта — ITransientNotification, который взят из исходников Android. После активного вызова метода mTN.show(), Toast будет долго отображаться на экране даже после выхода из приложения, пока не будет вызван метод mTN.hide().


Проблема с длительностью отображения Toast решена, но остаётся вопрос о возможности создания пользовательских анимаций. В методе инициализации TN используется объект WindowManager.LayoutParams. Если вы работали над созданием плавающих окон, вы должны были столкнуться с этим объектом. Вот ключевое место, где определяются анимации окна:```java params.windowAnimations = com.android.internal.R.style.Animation_Toast;


К сожалению, `params` является приватным свойством, поэтому придётся использовать рефлексию для получения доступа к нему и изменения анимации окна:

```java
private void initTN() {
    try {
        Field tnField = toast.getClass().getDeclaredField("mTN");
        tnField.setAccessible(true);
        mTN = (ITransientNotification) tnField.get(toast);

        /** Установите mNextView перед вызовом tn.show() */
        Field tnNextViewField = mTN.getClass().getDeclaredField("mNextView");
        tnNextViewField.setAccessible(true);
        tnNextViewField.set(mTN, toast.getView());

    } catch (Exception e) {
        e.printStackTrace();
    }
}

Получение params и повторное определение анимации окна

/**Получаем params и переопределяем анимацию окна*/
Field tnParamsField = mTN.getClass().getDeclaredField("mParams");
tnParamsField.setAccessible(true);
WindowManager.LayoutParams params = (WindowManager.LayoutParams) tnParamsField.get(mTN);
params.windowAnimations = R.style.anim_view;

} catch (Exception e) {
    e.printStackTrace();
}

---

## Чёрная технология Android: Toast. Системное плавающее окно без необходимости получения разрешений

Ранее мы уже говорили, что Toast является конкретной формой системного плавающего окна. Но какова же его суть? Какие различия между ним и обычным системным плавающим окном?

Давайте рассмотрим традиционную реализацию плавающего окна в Android:

```java
// Получаем контекст приложения
mContext = context.getApplicationContext();
// Получаем WindowManager
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
mView = setUpView(context);
``````markdown
Финальный объект WindowManager.LayoutParams используется для инициализации:

```java
final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
// Тип
params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;

int flags = WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
params.flags = flags;
params.format = PixelFormat.TRANSLUCENT;
params.width = LayoutParams.MATCH_PARENT;
params.height = LayoutParams.MATCH_PARENT;
params.gravity = Gravity.CENTER;
mWindowManager.addView(mView, params);

В отличие от этого, внутри внутреннего класса TN Toast инициализация WindowManager.LayoutParams происходит следующим образом:

// Тип
params.type = WindowManager.LayoutParams.TYPE_TOAST;

Как видно, мы используем Toast для создания постоянно отображаемого плавающего окна. Основное различие между обычным плавающим окном и плавающим окном Toast заключается в том, что последнее не требует специальных разрешений! При использовании Toast в нашем приложении мы не указываем никаких дополнительных разрешений, тогда как использование обычного плавающего окна требует наличия следующего разрешения:

<!-- Отображение верхнего уровня плавающего окна -->
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

Все это происходит вне поля зрения пользователя, и лишь разработчики знают эти различия. Из того, что известно пользователям, можно выделить то, что Toast не может покрывать системную панель состояния, в то время как большинство других типов плавающих окон могут её покрывать. Более подробные различия будут добавлены в будущем. ````Дополнительные материалы доступны здесь:`
Механизмы добавления окон Activity, Dialog, PopWindow, Toast в Android-приложении и анализ исходного кода


Пожалуйста, используйте "Issues" для отчета об ошибках или неправильной информации.```

Комментарии ( 0 )

Вы можете оставить комментарий после Вход в систему

Введение

Описание недоступно Развернуть Свернуть
Отмена

Обновления

Пока нет обновлений

Участники

все

Недавние действия

Загрузить больше
Больше нет результатов для загрузки
1
https://api.gitlife.ru/oschina-mirror/whyyy-ExToast.git
git@api.gitlife.ru:oschina-mirror/whyyy-ExToast.git
oschina-mirror
whyyy-ExToast
whyyy-ExToast
master