Как избежать повторной компоновки общей иерархии представлений при использовании 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, то обновление представления будет продолжено иначе. Я не знаю, насколько сложна ваша компоновка и сколько просмотров используется, но здесь отдельный класс должен расширять вид, используемый и переопределять этот метод, а также нужен механизм/переменная для выделения объекта в текущем фокусе.
Надеюсь, что этот метод поможет!