Как избежать повторной компоновки общей иерархии представлений при использовании 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 ведет себя с каретой?

Ответы

Ответ 1

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

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

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

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

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

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

Ответ 2

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

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

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

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