Как избежать повторной компоновки общей иерархии представлений при использовании EditText?

Задний план

У меня довольно сложная компоновка, показываемая пользователю в действии.

Одним из видов является EditText.

Поскольку я должен был сделать один из видов, оставшихся за мягкой клавиатурой, но все же над ним, мне пришлось прислушиваться к изменениям вида-макета (написано об этом здесь ).

Проблема

Я заметил, что всякий раз, когда EditText имеет фокус и показывает свой карет, вся иерархия представлений получает ре-макет.

Вы можете увидеть это, просмотрев журнал созданного слушателя или включив «show surface updates» через настройки разработчиков.

Это приводит к плохой производительности на некоторых устройствах, особенно если компоновка активности сложна или имеет фрагменты, которые имеют сложные макеты.

Код

Я не буду показывать оригинальный код, но есть простой способ воспроизвести проблему:

activity_main.xml

<?xml version="1.0" encoding="utf-8"?> <FrameLayout 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" tools:context="com.example.user.myapplication.MainActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="just some text"/> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:inputType="phone" android:text="write here" android:textSize="18dp"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="just some text 2"/> </LinearLayout> </FrameLayout> 

MainActivity.java

 public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(android.R.id.content).getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() { @Override public boolean onPreDraw() { Log.d("AppLog", "onPreDraw"); return true; } }); } } 

Что я пробовал

При отключении каретки (используя «cursorVisible», который по какой-то причине называется «курсором»), я вижу, что проблема не существует.

Я попытался найти альтернативу встроенному каретному поведению, но я не могу найти. Единственное, что я нашел, это сообщение , но, похоже, оно статично, и я не уверен, насколько хорошо он работает (производительность и совместимость с обычным кареткой).

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

Я также заметил, что в исходном приложении журналы могут (по какой-то причине) продолжать писать, даже когда приложение переходит в фоновый режим.

Я сообщил об этой проблеме (включая образец и видео) здесь , надеясь, что Google покажет, что случилось, или исправить это.

Вопрос

Есть ли способ избежать повторной компоновки всей иерархии представлений? Способ, который все равно позволит EditText иметь одинаковый внешний вид обычного EditText?

Может быть, способ настроить, как EditText ведет себя с кареткой?

Я заметил, что всякий раз, когда EditText имеет фокус и показывает свой карет, вся иерархия представлений получает ре-макет.

Это неправда. Размер и положение EditText постоянны – повторная компоновка отсутствует. Вы можете проверить это, используя код ниже.

 findViewById(android.R.id.content).getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { Log.d("AppLog", "Layout phase"); } }); 

Из-за мигания каретки – EditText constatly вызывает invalidate() . Это заставляет графический процессор снова рисовать EditText.

На моей связи 5 (зефир) я вижу, что только EditText перерисовывается (Показывать обновления просмотра GPU – включено).

Введите описание изображения здесь

Как насчет переопределения dispatchOnPreDraw () всех соперников, которые вы используете в действии, и имеющих флаг для проверки того, нужно ли перерисовывать это конкретное представление?

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

Если метод dispatchOnPreDraw () возвращает false, то обновление представления будет продолжаться иначе. Я не знаю, насколько сложна ваша компоновка и сколько просмотров используется, но здесь отдельный класс должен расширять используемое представление и переопределять этот метод, а также нужен механизм / переменная для выделения объекта в текущем фокусе.

Надеюсь, этот метод поможет!

Intereting Posts
Samsung Galaxy Tablet не позволяет вводить числа с плавающей запятой входы с типом «число» Наложения фрагментов Android перекрываются Кнопка добавления, например, «Новая почта» в приложении Gmail. Список нарушений строгих правил Как использовать привязку данных и Kotlin в Android Studio 3.0.0 Как открыть основное приложение с носимого? Как получить AID для читателя Как показать панель загрузки при рендеринге с помощью Webview.loadUrl в android? Поддержка NDK – экспериментальные функции, и все варианты использования еще не подтверждены ошибкой в ​​Android Studio? Переход на общий элемент Lollipop при повторной активации с изменением размера клавиатуры StaggeredGridLayout испортится при прокрутке вверх Является ли это новой версией в Android 4.0? Статистика сети Eclipse DDMS пустая SERVER_ERROR: 1675030 : ошибка при выполнении запроса Можем ли мы установить INSTALL_REFERRER два раза в одном приложении для Android?