Android предоставляет все необходимое для пятизвездочных приложений с большим экраном. Рецепты в этой кулинарной книге выбирают и комбинируют ингредиенты для решения конкретных проблем развития. Каждый рецепт включает в себя лучшие практики, качественные примеры кода и пошаговые инструкции, которые помогут вам стать лучшим шеф-поваром на большом экране.
Звездные рейтинги
Рецептам присваивается звездный рейтинг в зависимости от того, насколько они соответствуют рекомендациям по обеспечению качества приложений для большого экрана .
Соответствует критериям уровня 1, дифференцированный большой экран | |
Соответствует критериям уровня 2, оптимизирован для большого экрана | |
Соответствует критериям уровня 3, готовность к большому экрану | |
Предоставляет некоторые возможности большого экрана, но не соответствует рекомендациям по качеству приложений для больших экранов. | |
Соответствует потребностям конкретного варианта использования, но не поддерживает должным образом большие экраны. |
Поддержка камеры Chromebook
Будьте замечены в Google Play пользователями Chromebook.
Если ваше приложение камеры может работать только с базовыми функциями камеры, не позволяйте магазинам приложений запрещать пользователям Chromebook устанавливать приложение только потому, что вы случайно указали расширенные функции камеры, доступные на телефонах высокого класса.
Chromebook имеет встроенную переднюю камеру, обращенную к пользователю, которая хорошо подходит для видеоконференций, создания снимков и других приложений. Но не все Chromebook имеют заднюю (обращенную к миру) камеру, а большинство камер Chromebook, обращенных к пользователю, не поддерживают автофокусировку или вспышку.
Лучшие практики
Универсальные приложения для камеры поддерживают все устройства независимо от конфигурации камеры — устройства с фронтальными камерами, задними камерами, внешними камерами, подключенными через USB.
Чтобы магазины приложений сделали ваше приложение доступным для наибольшего числа устройств, всегда объявляйте все функции камеры, используемые вашим приложением, и явно указывайте, требуются ли эти функции или нет.
Ингредиенты
- Разрешение
CAMERA
: предоставляет вашему приложению доступ к камерам устройства. - Элемент манифеста
<uses-feature>
: информирует магазины приложений о функциях, используемых вашим приложением. -
required
атрибут: указывает магазинам приложений, может ли ваше приложение работать без указанной функции.
Шаги
Краткое содержание
Объявите разрешение CAMERA
. Объявите функции камеры, обеспечивающие базовую поддержку камеры. Укажите, требуется ли каждая функция.
1. Объявите разрешение CAMERA
.
Добавьте следующее разрешение в манифест приложения:
<uses-permission android:name="android.permission.CAMERA" />
2. Объявите основные функции камеры.
Добавьте в манифест приложения следующие функции:
<uses-feature android:name="android.hardware.camera.any" android:required="false" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
<uses-feature android:name="android.hardware.camera.flash" android:required="false" />
3. Укажите, требуется ли каждая функция
Установите android:required="false"
для функции android.hardware.camera.any
, чтобы разрешить доступ к вашему приложению устройствам, имеющим любую встроенную или внешнюю камеру или вообще не имеющим камеры.
Для других функций установите android:required="false"
чтобы такие устройства, как Chromebook, у которых нет задней камеры, автофокусировки или вспышки, могли получить доступ к вашему приложению в магазинах приложений.
Результаты
Пользователи Chromebook могут загрузить и установить ваше приложение из Google Play и других магазинов приложений. А устройства с полнофункциональной поддержкой камеры, такие как телефоны, не будут ограничены в функциях камеры.
Явно задав функции камеры, поддерживаемые вашим приложением, и указав функции, которые требуются вашему приложению, вы сделали свое приложение доступным для как можно большего числа устройств.
Дополнительные ресурсы
Дополнительные сведения см. в разделе Аппаратные функции камеры в документации <uses-feature>
.
Ориентация приложения ограничена на телефонах, но не на устройствах с большим экраном.
Ваше приложение отлично работает на телефонах с книжной ориентацией, поэтому вы ограничили его только портретной ориентацией. Но вы видите возможность делать больше на больших экранах в альбомной ориентации.
Как можно сделать и то, и другое — ограничить приложение портретной ориентацией на маленьких экранах, но включить альбомную ориентацию на больших?
Лучшие практики
Лучшие приложения учитывают предпочтения пользователя, такие как ориентация устройства.
В рекомендациях по обеспечению качества приложений для большого экрана рекомендуется, чтобы приложения поддерживали все конфигурации устройств, включая книжную и альбомную ориентацию, многооконный режим, а также сложенное и развернутое состояния складных устройств. Приложения должны оптимизировать макеты и пользовательские интерфейсы для различных конфигураций, а приложения должны сохранять и восстанавливать состояние во время изменений конфигурации.
Этот рецепт — временная мера — щепотка поддержки больших экранов. Используйте этот рецепт до тех пор, пока не сможете улучшить свое приложение, чтобы обеспечить полную поддержку всех конфигураций устройств.
Ингредиенты
-
screenOrientation
: параметр манифеста приложения, который позволяет указать, как ваше приложение реагирует на изменения ориентации устройства. - Jetpack WindowManager : набор библиотек, позволяющих определять размер и соотношение сторон окна приложения; обратная совместимость с уровнем API 14
-
Activity#setRequestedOrientation()
: метод, с помощью которого вы можете изменить ориентацию приложения во время выполнения.
Шаги
Краткое содержание
Включите приложение для обработки изменений ориентации по умолчанию в манифесте приложения. Во время выполнения определите размер окна приложения. Если окно приложения маленькое, ограничьте ориентацию приложения, переопределив настройку ориентации манифеста.
1. Укажите настройку ориентации в манифесте приложения.
Вы можете либо не объявлять элемент screenOrientation
манифеста приложения (в этом случае ориентация по умолчанию имеет значение unspecified
), либо установить ориентацию экрана на fullUser
. Если пользователь не заблокировал вращение с помощью датчика, ваше приложение будет поддерживать все ориентации устройства.
<activity
android:name=".MyActivity"
android:screenOrientation="fullUser">
Разница между использованием unspecified
fullUser
незначительна, но важна. Если вы не объявляете значение screenOrientation
, система выбирает ориентацию, и политика, которую система использует для определения ориентации, может отличаться от устройства к устройству. С другой стороны, указание fullUser
более точно соответствует поведению, которое пользователь определил для устройства: если пользователь заблокировал вращение на основе датчика, приложение следует предпочтениям пользователя; в противном случае система допускает любую из четырех возможных ориентаций экрана (портретная, альбомная, перевернутая книжная или перевернутая альбомная). См. android:screenOrientation
.
2. Определите размер экрана
Поскольку манифест настроен на поддержку всех разрешенных пользователем ориентаций, вы можете программно указать ориентацию приложения в зависимости от размера экрана.
Добавьте библиотеки Jetpack WindowManager в файл build.gradle
или build.gradle.kts
модуля:
Котлин
implementation("androidx.window:window:version
") implementation("androidx.window:window-core:version
")
классный
implementation 'androidx.window:window:version
' implementation 'androidx.window:window-core:version
'
Используйте метод Jetpack WindowManager WindowMetricsCalculator#computeMaximumWindowMetrics()
чтобы получить размер экрана устройства в виде объекта WindowMetrics
. Метрики окна можно сравнить с классами размеров окон, чтобы решить, когда ограничить ориентацию.
Классы размеров Windows обеспечивают точки останова между маленькими и большими экранами.
Используйте точки останова WindowWidthSizeClass#COMPACT
и WindowHeightSizeClass#COMPACT
чтобы определить размер экрана:
Котлин
/** Determines whether the device has a compact screen. **/ fun compactScreen() : Boolean { val metrics = WindowMetricsCalculator.getOrCreate().computeMaximumWindowMetrics(this) val width = metrics.bounds.width() val height = metrics.bounds.height() val density = resources.displayMetrics.density val windowSizeClass = WindowSizeClass.compute(width/density, height/density) return windowSizeClass.windowWidthSizeClass == WindowWidthSizeClass.COMPACT || windowSizeClass.windowHeightSizeClass == WindowHeightSizeClass.COMPACT }
Ява
/** Determines whether the device has a compact screen. **/ private boolean compactScreen() { WindowMetrics metrics = WindowMetricsCalculator.getOrCreate().computeMaximumWindowMetrics(this); int width = metrics.getBounds().width(); int height = metrics.getBounds().height(); float density = getResources().getDisplayMetrics().density; WindowSizeClass windowSizeClass = WindowSizeClass.compute(width/density, height/density); return windowSizeClass.getWindowWidthSizeClass() == WindowWidthSizeClass.COMPACT || windowSizeClass.getWindowHeightSizeClass() == WindowHeightSizeClass.COMPACT; }
- Примечание:
- Приведенные выше примеры реализованы как методы деятельности; и поэтому действие разыменовывается как
this
в аргументеcomputeMaximumWindowMetrics()
. - Метод
computeMaximumWindowMetrics()
используется вместо методаcomputeCurrentWindowMetrics()
поскольку приложение можно запустить в многооконном режиме, который игнорирует настройку ориентации экрана. Нет смысла определять размер окна приложения и переопределять настройку ориентации, если окно приложения не занимает весь экран устройства.
Инструкции по объявлению зависимостей, чтобы сделать метод computeMaximumWindowMetrics()
доступным в вашем приложении, см. в WindowManager.
3. Переопределить настройку манифеста приложения.
Когда вы определили, что устройство имеет компактный размер экрана, вы можете вызвать Activity#setRequestedOrientation()
чтобы переопределить настройку screenOrientation
манифеста:
Котлин
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) requestedOrientation = if (compactScreen()) ActivityInfo.SCREEN_ORIENTATION_PORTRAIT else ActivityInfo.SCREEN_ORIENTATION_FULL_USER ... // Replace with a known container that you can safely add a // view to where the view won't affect the layout and the view // won't be replaced. val container: ViewGroup = binding.container // Add a utility view to the container to hook into // View.onConfigurationChanged. This is required for all // activities, even those that don't handle configuration // changes. You can't use Activity.onConfigurationChanged, // since there are situations where that won't be called when // the configuration changes. View.onConfigurationChanged is // called in those scenarios. container.addView(object : View(this) { override fun onConfigurationChanged(newConfig: Configuration?) { super.onConfigurationChanged(newConfig) requestedOrientation = if (compactScreen()) ActivityInfo.SCREEN_ORIENTATION_PORTRAIT else ActivityInfo.SCREEN_ORIENTATION_FULL_USER } }) }
Ява
@Override protected void onCreate(Bundle savedInstance) { super.onCreate(savedInstanceState); if (compactScreen()) { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } else { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_USER); } ... // Replace with a known container that you can safely add a // view to where the view won't affect the layout and the view // won't be replaced. ViewGroup container = binding.container; // Add a utility view to the container to hook into // View.onConfigurationChanged. This is required for all // activities, even those that don't handle configuration // changes. You can't use Activity.onConfigurationChanged, // since there are situations where that won't be called when // the configuration changes. View.onConfigurationChanged is // called in those scenarios. container.addView(new View(this) { @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); if (compactScreen()) { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } else { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_USER); } } }); }
Добавив логику в методы onCreate()
и View.onConfigurationChanged()
, вы сможете получить максимальные метрики окна и переопределить настройку ориентации всякий раз, когда действие изменяется или перемещается между дисплеями, например, после поворота устройства или когда складное устройство сложено или разложено. Дополнительные сведения о том, когда происходят изменения конфигурации и когда они вызывают воссоздание активности, см. в разделе Обработка изменений конфигурации.
Результаты
Ваше приложение теперь должно оставаться в книжной ориентации на маленьких экранах независимо от поворота устройства. На больших экранах приложение должно поддерживать альбомную и портретную ориентацию.
Дополнительные ресурсы
Чтобы получить помощь по обновлению вашего приложения для постоянной поддержки всех конфигураций устройств, см. следующее:
- Поддержка различных размеров экрана
- Обработка изменений конфигурации
- Сохранение состояний пользовательского интерфейса
Приостановка и возобновление воспроизведения мультимедиа с помощью клавиши пробела на внешней клавиатуре
Оптимизация большого экрана включает в себя возможность обрабатывать ввод с внешней клавиатуры, например, реагировать на нажатие клавиши пробела для приостановки или возобновления воспроизведения видео и других медиафайлов. Это особенно полезно для планшетов, которые часто подключаются к внешним клавиатурам, и Chromebook, которые обычно поставляются с внешними клавиатурами, но могут использоваться в режиме планшета.
Когда мультимедиа является единственным элементом окна (например, полноэкранное воспроизведение видео), реагируйте на события нажатия клавиш на уровне активности или, в Jetpack Compose, на уровне экрана.
Лучшие практики
Всякий раз, когда ваше приложение воспроизводит медиафайл, пользователи должны иметь возможность приостанавливать и возобновлять воспроизведение, нажимая клавишу пробела на физической клавиатуре.
Ингредиенты
-
KEYCODE_SPACE
: константа кода клавиши для клавиши пробела.
Сочинить
-
onPreviewKeyEvent
:Modifier
, который позволяет компоненту перехватывать события аппаратных клавиш, когда он (или один из его дочерних элементов) находится в фокусе. -
onKeyEvent
: ПодобноonPreviewKeyEvent
, этотModifier
позволяет компоненту перехватывать события аппаратных клавиш, когда он (или один из его дочерних элементов) находится в фокусе.
Просмотры
-
onKeyUp()
: вызывается, когда клавиша отпускается и не обрабатывается представлением внутри действия.
Шаги
Краткое содержание
Приложения на основе представления и приложения на основе Jetpack Compose реагируют на нажатия клавиш клавиатуры аналогичным образом: приложение должно прослушивать события нажатия клавиш, фильтровать события и реагировать на выбранные нажатия клавиш, например нажатие клавиши пробела.
1. Слушайте события клавиатуры
Просмотры
В действии вашего приложения переопределите метод onKeyUp()
:
Котлин
override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean { ... }
Ява
@Override public boolean onKeyUp(int keyCode, KeyEvent event) { ... }
Метод вызывается каждый раз при отпускании нажатой клавиши, поэтому он срабатывает ровно один раз для каждого нажатия клавиши.
Сочинить
С помощью Jetpack Compose вы можете использовать модификатор onPreviewKeyEvent
или onKeyEvent
на экране, который управляет нажатием клавиши:
Column(modifier = Modifier.onPreviewKeyEvent { event ->
if (event.type == KeyEventType.KeyUp) {
...
}
...
})
или
Column(modifier = Modifier.onKeyEvent { event ->
if (event.type == KeyEventType.KeyUp) {
...
}
...
})
2. Фильтрация нажатия клавиши пробела
Внутри метода onKeyUp()
или методов-модификаторов Compose onPreviewKeyEvent
и onKeyEvent
отфильтруйте KeyEvent.KEYCODE_SPACE
, чтобы отправить правильное событие в ваш медиа-компонент:
Просмотры
Котлин
if (keyCode == KeyEvent.KEYCODE_SPACE) { togglePlayback() return true } return false
Ява
if (keyCode == KeyEvent.KEYCODE_SPACE) { togglePlayback(); return true; } return false;
Сочинить
Column(modifier = Modifier.onPreviewKeyEvent { event ->
if (event.type == KeyEventType.KeyUp && event.key == Key.Spacebar) {
...
}
...
})
или
Column(modifier = Modifier.onKeyEvent { event ->
if (event.type == KeyEventType.KeyUp && event.key == Key.Spacebar) {
...
}
...
})
Результаты
Теперь ваше приложение может реагировать на нажатия клавиш пробела, чтобы приостановить и возобновить воспроизведение видео или другого мультимедиа.
Дополнительные ресурсы
Дополнительные сведения о событиях клавиатуры и о том, как ими управлять, см. в разделе Обработка ввода с клавиатуры .
Отказ от стилуса
Стилус может быть исключительно продуктивным и творческим инструментом на больших экранах. Но когда пользователи рисуют, пишут или взаимодействуют с приложением с помощью стилуса, они иногда касаются экрана ладонью. О событии касания можно сообщить в ваше приложение до того, как система распознает и отклонит это событие как случайное прикосновение ладони.
Лучшие практики
Ваше приложение должно распознавать посторонние события касания и игнорировать их. Android отменяет прикосновение ладони, отправляя объект MotionEvent
. Проверьте объект на наличие ACTION_CANCEL
или ACTION_POINTER_UP
и FLAG_CANCELED
, чтобы определить, следует ли отклонять жест, вызванный прикосновением ладони.
Ингредиенты
-
MotionEvent
: представляет события касания и движения. Содержит информацию, необходимую для определения того, следует ли игнорировать событие. -
OnTouchListener#onTouch()
: получает объектыMotionEvent
. -
MotionEvent#getActionMasked()
: возвращает действие, связанное с событием движения. -
ACTION_CANCEL
: константаMotionEvent
, указывающая, что жест следует отменить. -
ACTION_POINTER_UP
: константаMotionEvent
, указывающая, что указатель, отличный от первого, поднялся вверх (то есть прекратил контакт с экраном устройства). -
FLAG_CANCELED
: константаMotionEvent
, указывающая, что подъем указателя вверх вызвал непреднамеренное событие касания. Добавлено в событияACTION_POINTER_UP
иACTION_CANCEL
на Android 13 (уровень API 33) и более поздних версиях.
Шаги
Краткое содержание
Изучите объекты MotionEvent
, отправленные в ваше приложение. Используйте API MotionEvent
для определения характеристик событий:
- События с одним указателем — проверьте
ACTION_CANCEL
. В Android 13 и более поздних версиях также проверьте наличиеFLAG_CANCELED
. - События с несколькими указателями . В Android 13 и более поздних версиях проверьте
ACTION_POINTER_UP
иFLAG_CANCELED
.
Реагируйте на события ACTION_CANCEL
и ACTION_POINTER_UP
/ FLAG_CANCELED
.
1. Получите объекты событий движения.
Добавьте OnTouchListener
в свое приложение:
Котлин
val myView = findViewById<View>(R.id.myView).apply { setOnTouchListener { view, event -> // Process motion event. } }
Ява
View myView = findViewById(R.id.myView); myView.setOnTouchListener( (view, event) -> { // Process motion event. });
2. Определите действие и флаги события.
Проверьте ACTION_CANCEL
, который указывает на событие с одним указателем на всех уровнях API. В Android 13 и более поздних версиях проверьте ACTION_POINTER_UP
на FLAG_CANCELED.
Котлин
val myView = findViewById<View>(R.id.myView).apply { setOnTouchListener { view, event -> when (event.actionMasked) { MotionEvent.ACTION_CANCEL -> { //Process canceled single-pointer motion event for all SDK versions. } MotionEvent.ACTION_POINTER_UP -> { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && (event.flags and MotionEvent.FLAG_CANCELED) == MotionEvent.FLAG_CANCELED) { //Process canceled multi-pointer motion event for Android 13 and higher. } } } true } }
Ява
View myView = findViewById(R.id.myView); myView.setOnTouchListener( (view, event) -> { switch (event.getActionMasked()) { case MotionEvent.ACTION_CANCEL: // Process canceled single-pointer motion event for all SDK versions. case MotionEvent.ACTION_UP: if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && (event.getFlags() & MotionEvent.FLAG_CANCELED) == MotionEvent.FLAG_CANCELED) { //Process canceled multi-pointer motion event for Android 13 and higher. } } return true; });
3. Отменить жест
После того как вы определили прикосновение ладони, вы можете отменить экранные эффекты этого жеста.
Ваше приложение должно хранить историю действий пользователя, чтобы можно было отменить непреднамеренные действия, такие как прикосновения ладоней. Пример см. в разделе «Реализация базового приложения для рисования» в разделе «Улучшение поддержки стилуса» в кодовой лаборатории приложения Android .
Результаты
Теперь ваше приложение может идентифицировать и отклонять прикосновения ладони для событий с несколькими указателями на уровнях API Android 13 и более поздних версий, а также для событий с одним указателем на всех уровнях API.
Дополнительные ресурсы
Для получения дополнительной информации см. следующее:
- Функции и API Android 13 — улучшенная функция отклонения ладони.
- Руководства для разработчиков
- Codelab — улучшенная поддержка стилуса в приложении для Android.
Управление состоянием WebView
WebView
— это широко используемый компонент, предлагающий расширенную систему управления состоянием. WebView
должен сохранять свое состояние и положение прокрутки при изменении конфигурации. WebView
может потерять позицию прокрутки, когда пользователь поворачивает устройство или разворачивает складной телефон, что заставляет пользователя снова прокручивать от верхней части WebView
до предыдущей позиции прокрутки.
Лучшие практики
Минимизируйте количество воссозданий WebView
. WebView
хорошо управляет своим состоянием, и вы можете использовать это качество, управляя как можно большим количеством изменений конфигурации. Ваше приложение должно обрабатывать изменения конфигурации, поскольку воссоздание Activity
(способ обработки изменений конфигурации системой) также воссоздает WebView
, что приводит к потере WebView
своего состояния.
Ингредиенты
-
android:configChanges
: Атрибут элемента манифеста<activity>
. Перечисляет изменения конфигурации, обрабатываемые действием. -
View#invalidate()
: метод, вызывающий перерисовку представления. УнаследованоWebView
.
Шаги
Краткое содержание
Чтобы сохранить состояние WebView
, по возможности избегайте воссоздания Activity
, а затем дайте WebView
сделать недействительным, чтобы он мог изменить размер, сохраняя свое состояние.
1. Добавьте изменения конфигурации в файл AndroidManifest.xml
вашего приложения.
Избегайте повторения активности, указав изменения конфигурации, обрабатываемые вашим приложением (а не системой):
<activity
android:name=".MyActivity"
android:configChanges="screenLayout|orientation|screenSize
|keyboard|keyboardHidden|smallestScreenSize" />
2. Делайте WebView
недействительным всякий раз, когда ваше приложение получает изменение конфигурации.
Котлин
override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) webView.invalidate() }
Ява
@Override public void onConfigurationChanged(@NonNull Configuration newConfig) { super.onConfigurationChanged(newConfig); webview.invalidate(); }
Этот шаг применим только к системе представлений, поскольку Jetpack Compose не нужно ничего делать недействительным для правильного изменения размера Composable
элементов. Однако Compose часто воссоздает WebView
, если им неправильно управлять. Используйте оболочку Accompanist WebView для сохранения и восстановления состояния WebView
в ваших приложениях Compose.
Результаты
Компоненты WebView
вашего приложения теперь сохраняют свое состояние и положение прокрутки при многочисленных изменениях конфигурации: от изменения размера до изменения ориентации, а также сворачивания и развертывания.
Дополнительные ресурсы
Дополнительные сведения об изменениях конфигурации и о том, как ими управлять, см. в разделе Обработка изменений конфигурации .
Управление состоянием RecyclerView
RecyclerView
может отображать большие объемы данных, используя минимум графических ресурсов. Когда RecyclerView
прокручивает свой список элементов, RecyclerView
повторно использует экземпляры View
элементов, которые прокрутились за пределы экрана, для создания новых элементов по мере их прокрутки на экране. Но изменения конфигурации, такие как ротация устройства, могут сбросить состояние RecyclerView
, заставляя пользователей снова прокручивать их до предыдущей позиции в списке элементов RecyclerView
.
Лучшие практики
RecyclerView
должен сохранять свое состояние, в частности положение прокрутки, и состояние элементов списка во время всех изменений конфигурации.
Ингредиенты
-
RecyclerView.Adapter#setStateRestorationPolicy()
: определяет, какRecyclerView.Adapter
восстанавливает свое состояние после изменения конфигурации. -
ViewModel
: сохраняет состояние действия или фрагмента.
Шаги
Краткое содержание
Установите политику восстановления состояния RecyclerView.Adapter
, чтобы сохранить положение прокрутки RecyclerView
. Сохраните состояние элементов списка RecyclerView
. Добавьте состояние элементов списка в адаптер RecyclerView
и восстановите состояние элементов списка, когда они привязаны к ViewHolder
.
1. Включите политику восстановления состояния Adapter
Включите политику восстановления состояния адаптера RecyclerView
, чтобы положение прокрутки RecyclerView
сохранялось при изменении конфигурации. Добавьте спецификацию политики в конструктор адаптера:
Котлин
class MyAdapter() : RecyclerView.Adapter<RecyclerView.ViewHolder>() { init { stateRestorationPolicy = StateRestorationPolicy.PREVENT_WHEN_EMPTY } ... }
Ява
class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { public Adapter() { setStateRestorationPolicy(StateRestorationPolicy.PREVENT_WHEN_EMPTY); } ... }
2. Сохраните состояние элементов списка с сохранением состояния.
Сохраняйте состояние сложных элементов списка RecyclerView
, например элементов, содержащих элементы EditText
. Например, чтобы сохранить состояние EditText
, добавьте обратный вызов, аналогичный обработчику onClick
для фиксации изменений текста. В обратном вызове определите, какие данные следует сохранить:
Котлин
input.addTextChangedListener( afterTextChanged = { text -> text?.let { // Save state here. } } )
Ява
input.addTextChangedListener(new TextWatcher() { ... @Override public void afterTextChanged(Editable s) { // Save state here. } });
Объявите обратный вызов в своем Activity
или Fragment
. Используйте ViewModel
для хранения состояния.
3. Добавьте состояние элемента списка в Adapter
Добавьте состояние элементов списка в свой RecyclerView.Adapter
. Передайте состояние элемента конструктору адаптера при создании Activity
или Fragment
хоста:
Котлин
val adapter = MyAdapter(items, viewModel.retrieveState())
Ява
MyAdapter adapter = new MyAdapter(items, viewModel.retrieveState());
4. Восстановить состояние элемента списка в ViewHolder
адаптера.
В RecyclerView.Adapter
при привязке ViewHolder
к элементу восстановите состояние элемента:
Котлин
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { ... val item = items[position] val state = states.firstOrNull { it.item == item } if (state != null) { holder.restore(state) } }
Ява
@Override public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { ... Item item = items[position]; Arrays.stream(states).filter(state -> state.item == item) .findFirst() .ifPresent(state -> holder.restore(state)); }
Результаты
Теперь ваш RecyclerView
может восстановить положение прокрутки и состояние каждого элемента в списке RecyclerView
.
Дополнительные ресурсы
Съемное управление с клавиатуры
Поддержка съемных клавиатур помогает максимизировать производительность пользователей на устройствах с большим экраном. Android инициирует изменение конфигурации каждый раз, когда клавиатура подключается к устройству или отсоединяется от него, что может привести к потере состояния пользовательского интерфейса. Ваше приложение может либо сохранять и восстанавливать свое состояние , позволяя системе обрабатывать воссоздание активности, либо ограничивать воссоздание активности при изменении конфигурации клавиатуры. Во всех случаях все данные, относящиеся к клавиатуре, хранятся в объекте Configuration
. Поля keyboard
и keyboardHidden
объекта конфигурации содержат информацию о типе клавиатуры и ее наличии.
Лучшие практики
Приложения, оптимизированные для больших экранов, поддерживают все типы устройств ввода: от программных и аппаратных клавиатур до стилуса, мыши, трекпада и других периферийных устройств.
Поддержка внешних клавиатур предполагает изменение конфигурации, управлять которым можно двумя способами:
- Позвольте системе воссоздать текущую активность, а вы сами позаботитесь об управлении состоянием вашего приложения.
- Управляйте изменением конфигурации самостоятельно (действие не будет воссоздано):
- Объявите все значения конфигурации, связанные с клавиатурой.
- Создайте обработчик изменения конфигурации
Приложения для повышения производительности, которым часто требуется точный контроль над пользовательским интерфейсом для ввода текста и другого ввода, могут извлечь выгоду из подхода «сделай сам» для обработки изменений конфигурации.
В особых случаях вам может потребоваться изменить макет приложения при подключении или отсоединении аппаратной клавиатуры, например, чтобы освободить больше места для инструментов или окон редактирования.
Поскольку единственный надежный способ прослушивания изменений конфигурации — это переопределить метод onConfigurationChanged()
представления, вы можете добавить новый экземпляр View в действие вашего приложения и реагировать в обработчике onConfigurationChanged()
представления на изменения конфигурации, вызванные переключением клавиатуры. прикрепленный или отсоединенный.
Ингредиенты
-
android:configChanges
: Атрибут элемента<activity>
манифеста приложения. Информирует систему об изменениях конфигурации, которыми управляет приложение. -
View#onConfigurationChanged()
: метод, реагирующий на распространение новой конфигурации приложения.
Шаги
Краткое содержание
Объявите атрибут configChanges
и добавьте значения, связанные с клавиатурой. Добавьте View
в иерархию представлений действия и прослушивайте изменения конфигурации.
1. Объявите атрибут configChanges
.
Обновите элемент <activity>
в манифесте приложения, добавив значения keyboard|keyboardHidden
в список уже управляемых изменений конфигурации:
<activity
…
android:configChanges="...|keyboard|keyboardHidden">
2. Добавьте пустое представление в иерархию представлений.
Объявите новое представление и добавьте код обработчика в метод onConfigurationChanged()
представления:
Котлин
val v = object : View(this) { override fun onConfigurationChanged(newConfig: Configuration?) { super.onConfigurationChanged(newConfig) // Handler code here. } }
Ява
View v = new View(this) { @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); // Handler code here. } };
Результаты
Ваше приложение теперь будет реагировать на подключение или отсоединение внешней клавиатуры, не воссоздавая текущее действие.
Дополнительные ресурсы
Чтобы узнать, как сохранить состояние пользовательского интерфейса вашего приложения во время изменений конфигурации, например присоединения или отсоединения клавиатуры, см. раздел Сохранение состояний пользовательского интерфейса .
{% дословно %}Рекомендуется для вас
- Примечание. Текст ссылки отображается, когда JavaScript отключен.
- Обработка изменений конфигурации