Android zapewnia wszystkie składniki 5-gwiazdkowych aplikacji z dużym ekranem. Przepisy zawarte w tej książce kucharskiej polegają na dobieraniu i łączeniu wybranych składników, aby rozwiązywać konkretne problemy kuchenne. Każdy przepis zawiera sprawdzone metody, przykłady wysokiej jakości kodu i szczegółowe instrukcje, które pomogą Ci zostać mistrzem kuchni na dużym ekranie.
Liczba gwiazdek
Przepisy są oceniane na podstawie gwiazdek na podstawie zgodności ze wskazówkami dotyczącymi jakości aplikacji na duże ekrany.
Spełnia kryteria poziomu 1, różnica w dużym ekranie | |
Spełnia kryteria poziomu 2, optymalizacja na duży ekran | |
Spełnia kryteria poziomu 3, obsługa dużego ekranu | |
Oferuje niektóre funkcje dużego ekranu, ale nie spełnia wymagań dotyczących jakości aplikacji na duży ekran | |
Spełnia potrzeby określonego przypadku użycia, ale nie obsługuje poprawnie dużych ekranów. |
Obsługa kamery Chromebooka
Daj się zauważyć w Google Play przez użytkowników Chromebooków.
Jeśli aplikacja aparatu obsługuje tylko podstawowe funkcje aparatu, nie zezwalaj sklepom z aplikacjami na zapobieganie instalowaniu aplikacji przez użytkowników Chromebooków tylko dlatego, że przypadkowo określono zaawansowane funkcje aparatu, które występują w telefonach zaawansowanych.
Chromebooki mają wbudowaną kamerę przednią (skierowaną do użytkownika), która sprawdza się podczas rozmów wideo, robienia zrzutów i wykonywania innych czynności. Jednak nie wszystkie Chromebooki są wyposażone w tylny aparat, a większość z aparatów skierowanych do użytkownika na Chromebookach nie obsługuje autofokusa ani lampy błyskowej.
Sprawdzone metody
Uniwersalne aplikacje do obsługi aparatu działają na wszystkich urządzeniach niezależnie od konfiguracji kamery – z aparatem przednim, tylnym i zewnętrznym podłączonym przez USB.
Aby mieć pewność, że sklepy z aplikacjami udostępniają Twoją aplikację na jak największej liczbie urządzeń, zawsze zadeklaruj wszystkie funkcje aparatu, których używa aplikacja, i wyraźnie wskaż, czy są one wymagane.
Składniki
- Uprawnienie
CAMERA
: zapewnia aplikacji dostęp do kamer urządzenia. <uses-feature>
Element w pliku manifestu: informuje sklepy z aplikacjami o funkcjach używanych przez Twoją aplikację- Atrybut
required
: wskazuje sklepom z aplikacjami, czy aplikacja może działać bez określonej funkcji.
Kroki
Podsumowanie
Zadeklaruj uprawnienie CAMERA
. Zadeklaruj funkcje kamery, które zapewniają jej podstawową obsługę. Określ, czy każda funkcja jest wymagana.
1. Zadeklaruj uprawnienie CAMERA
Dodaj te uprawnienia do pliku manifestu aplikacji:
<uses-permission android:name="android.permission.CAMERA" />
2. Deklarowanie podstawowych funkcji aparatu
Dodaj do manifestu aplikacji te funkcje:
<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. Określ, czy każda funkcja jest wymagana
Ustaw android:required="false"
dla funkcji android.hardware.camera.any
, aby umożliwić dostęp do aplikacji urządzeniom z wbudowanym lub zewnętrznym aparatem albo w ogóle ich nie używać.
W przypadku pozostałych funkcji ustaw android:required="false"
, aby mieć dostęp do aplikacji w sklepach z aplikacjami na urządzeniach takich jak Chromebooki, które nie mają tylnego aparatu, autofokusa ani lampy błyskowej.
Wyniki
Użytkownicy Chromebooków mogą pobrać i zainstalować Twoją aplikację z Google Play i innych sklepów z aplikacjami. Aparat urządzeń z pełną obsługą aparatu, np. telefon, nie będzie ograniczony.
Przez jawne ustawienie funkcji aparatu obsługiwanych przez aplikację i określenie funkcji, których ona wymaga, aplikacja jest dostępna na jak największej liczbie urządzeń.
Dodatkowe materiały
Więcej informacji znajdziesz w sekcji Funkcje aparatu w dokumentacji <uses-feature>
.
Wyświetlanie aplikacji jest ograniczone na telefonach, ale nie na urządzeniach z dużym ekranem
Aplikacja działa świetnie na telefonach w orientacji pionowej, więc ograniczono ją tylko do orientacji pionowej. Dostrzegasz jednak możliwość zastosowania większej liczby funkcji na dużych ekranach w orientacji poziomej.
Co zrobić, aby aplikacja działała w obu tych przypadkach – aby na małych ekranach była dostępna tylko w orientacji pionowej, a w przypadku dużych – w orientacji poziomej?
Sprawdzone metody
Najlepsze aplikacje uwzględniają preferencje użytkownika, np. orientację urządzenia.
Zgodnie ze wskazówkami dotyczącymi jakości aplikacji na duży ekran aplikacje muszą obsługiwać wszystkie konfiguracje urządzeń, w tym orientację pionową i poziomą, tryb wielu okien oraz stan złożony i rozłożony na urządzeniach składanych. Aplikacje powinny optymalizować układy i interfejsy pod kątem różnych konfiguracji, a aplikacje powinny zapisywać i przywracać stan podczas wprowadzania zmian w konfiguracji.
Ten przepis to rozwiązanie tymczasowe – obsługa dużego ekranu. Korzystaj z tego przepisu, dopóki nie ulepszysz aplikacji w taki sposób, aby była w pełni obsługiwana we wszystkich konfiguracjach urządzeń.
Składniki
screenOrientation
: ustawienie w pliku manifestu aplikacji, które pozwala określić, jak aplikacja ma reagować na zmiany orientacji urządzenia- Jetpack WindowManager: zbiór bibliotek, które umożliwiają określanie rozmiaru i formatu obrazu okna aplikacji; zgodność wsteczna z interfejsem API poziomu 14
Activity#setRequestedOrientation()
: metoda zmiany orientacji aplikacji w czasie działania.
Kroki
Podsumowanie
Włącz domyślną obsługę zmian orientacji w pliku manifestu aplikacji. Określ rozmiar okna aplikacji na czas działania. Jeśli okno aplikacji jest małe, ogranicz orientację aplikacji, zastępując ustawienie orientacji pliku manifestu.
1. Określ ustawienie orientacji w pliku manifestu aplikacji
Możesz nie zadeklarować elementu screenOrientation
w pliku manifestu aplikacji (w takim przypadku orientacja domyślna to unspecified
) lub ustawić orientację ekranu na fullUser
. Jeśli użytkownik nie zablokował obrotu w oparciu o czujnik, aplikacja będzie obsługiwać wszystkie orientacje urządzenia.
<activity
android:name=".MyActivity"
android:screenOrientation="fullUser">
Różnica między użyciem właściwości unspecified
i fullUser
jest subtelna, ale ważna. Jeśli nie zadeklarujesz wartości screenOrientation
, system wybierze orientację, a zasada, której używa do jej określenia, może być inna w zależności od urządzenia. Z drugiej strony ustawienie zasady fullUser
odpowiada działaniu zdefiniowanemu przez użytkownika dla urządzenia: jeśli użytkownik zablokował obrót z wykorzystaniem czujnika, aplikacja działa zgodnie z preferencjami użytkownika. W przeciwnym razie system zezwala na dowolną z 4 możliwych orientacji ekranu (pionowa, pozioma, odwrotna pionowa lub odwrotna). Zobacz android:screenOrientation
.
2. Ustalanie rozmiaru ekranu
Jeśli plik manifestu obsługuje wszystkie orientacje dozwolone przez użytkownika, możesz automatycznie określić orientację aplikacji na podstawie rozmiaru ekranu.
Dodaj biblioteki Jetpack WindowManager do pliku build.gradle
lub build.gradle.kts
modułu:
Kotlin
implementation("androidx.window:window:version
") implementation("androidx.window:window-core:version
")
Odlotowy
implementation 'androidx.window:window:version
' implementation 'androidx.window:window-core:version
'
Użyj metody Jetpack WindowManager WindowMetricsCalculator#computeMaximumWindowMetrics()
, aby uzyskać rozmiar ekranu urządzenia jako obiekt WindowMetrics
. Dane dotyczące okien można porównywać z klasami rozmiaru okna, aby zdecydować, kiedy ograniczyć orientację.
Klasy rozmiaru systemu Windows określają punkty przerwania między małymi i dużymi ekranami.
Za pomocą punktów przerwania WindowWidthSizeClass#COMPACT
i WindowHeightSizeClass#COMPACT
określ rozmiar ekranu:
Kotlin
/** 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 }
Java
/** 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; }
- Uwaga:
- Powyższe przykłady są zaimplementowane jako metody działania, więc w argumencie
computeMaximumWindowMetrics()
działanie jest określane jakothis
. - Zamiast
computeCurrentWindowMetrics()
używana jest metodacomputeMaximumWindowMetrics()
, ponieważ aplikację można uruchomić w trybie wielu okien (ignorując ustawienie orientacji ekranu). Określenie rozmiaru okna aplikacji i zastępowanie ustawienia orientacji nie ma sensu, chyba że okno aplikacji zajmuje cały ekran urządzenia.
Instrukcje deklarowania zależności, aby udostępnić metodę computeMaximumWindowMetrics()
w aplikacji, znajdziesz w sekcji WindowManager.
3. Zastąp ustawienie manifestu aplikacji
Gdy ustalisz, że urządzenie ma kompaktowy rozmiar ekranu, możesz wywołać metodę Activity#setRequestedOrientation()
, aby zastąpić ustawienie screenOrientation
pliku manifestu:
Kotlin
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 } }) }
Java
@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); } } }); }
Dodając logikę do metod onCreate()
i View.onConfigurationChanged()
, możesz uzyskiwać maksymalne dane dotyczące okien i zastępować ustawienie orientacji, gdy zmieniasz rozmiar aktywności lub przenosisz ją między wyświetlaczami, np. po obróceniu urządzenia albo po złożeniu lub rozłożeniu urządzenia.
Więcej informacji o tym, kiedy występują zmiany w konfiguracji i kiedy powodują ponowne odtworzenie aktywności, znajdziesz w sekcji Obsługa zmian konfiguracji.
Wyniki
Na małych ekranach aplikacja powinna teraz wyświetlać się w orientacji pionowej bez względu na obrócenie urządzenia. Na dużych ekranach aplikacja powinna obsługiwać orientację poziomą i pionową.
Dodatkowe materiały
Informacje o uaktualnianiu aplikacji w celu obsługi wszystkich konfiguracji urządzeń znajdziesz w tych artykułach:
Naciśnij spację na zewnętrznej klawiaturze i wstrzymaj odtwarzanie multimediów
Optymalizacja na dużym ekranie umożliwia obsługę zewnętrznych narzędzi do wprowadzania danych z klawiatury, np. reagowanie na naciśnięty klawisz spacji w celu wstrzymania lub wznowienia odtwarzania filmów i innych multimediów. Jest to szczególnie przydatne w przypadku tabletów, które często łączą się z zewnętrznymi klawiaturami, i Chromebooków, które zwykle mają zewnętrzną klawiaturę, ale mogą być używane w trybie tabletu.
Gdy multimedia są jedynym elementem okna (np. odtwarzanie filmu na pełnym ekranie), reagują na zdarzenia kliknięcia na poziomie aktywności lub w Jetpack Compose na poziomie ekranu.
Sprawdzone metody
Za każdym razem, gdy aplikacja odtwarza plik multimedialny, użytkownicy powinni mieć możliwość wstrzymywania i wznawiania odtwarzania, naciskając spację na klawiaturze fizycznej.
Składniki
KEYCODE_SPACE
: stała kodowa klawisza spacji.
Nowy post
onPreviewKeyEvent
:Modifier
, który umożliwia komponentowi przechwytywanie kluczowych zdarzeń sprzętowych, gdy jest on zaznaczony (lub jego element podrzędny).onKeyEvent
: podobnie jak w przypadkuonPreviewKeyEvent
ta zasadaModifier
umożliwia komponentowi przechwytywanie zdarzeń klucza sprzętowego, gdy jest on aktywny (lub jedno z jego elementów podrzędnych).
Widoki danych
onKeyUp()
: wywoływane po zwolnieniu klucza i nieobsługiwanym przez widok w aktywności.
Kroki
Podsumowanie
Aplikacje oparte na widokach i oparte na Jetpack Compose reagują na naciśnięcia klawiszy w podobny sposób: aplikacja musi nasłuchiwać zdarzeń naciśnięcia klawiszy, filtrować zdarzenia i reagować na wybrane naciśnięcia klawiszy, takie jak spacja.
1. Wykrywaj zdarzenia z klawiatury
Widoki danych
W aktywności w aplikacji zastąp metodę onKeyUp()
:
Kotlin
override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean { ... }
Java
@Override public boolean onKeyUp(int keyCode, KeyEvent event) { ... }
Metoda jest wywoływana przy każdym zwolnieniu naciśniętego klawisza, więc uruchamia się dokładnie raz na każde naciśnięcie klawisza.
Nowy post
Jetpack Compose umożliwia stosowanie na ekranie modyfikatora onPreviewKeyEvent
lub onKeyEvent
do zarządzania naciśnięciami klawiszy:
Column(modifier = Modifier.onPreviewKeyEvent { event ->
if (event.type == KeyEventType.KeyUp) {
...
}
...
})
lub
Column(modifier = Modifier.onKeyEvent { event ->
if (event.type == KeyEventType.KeyUp) {
...
}
...
})
2. Filtruj naciśnięcia spacji
W metodach onKeyUp()
lub metodach tworzenia onPreviewKeyEvent
i onKeyEvent
modyfikatora przefiltruj dane według parametru KeyEvent.KEYCODE_SPACE
, aby wysłać odpowiednie zdarzenie do komponentu multimediów:
Widoki danych
Kotlin
if (keyCode == KeyEvent.KEYCODE_SPACE) { togglePlayback() return true } return false
Java
if (keyCode == KeyEvent.KEYCODE_SPACE) { togglePlayback(); return true; } return false;
Nowy post
Column(modifier = Modifier.onPreviewKeyEvent { event ->
if (event.type == KeyEventType.KeyUp && event.key == Key.Spacebar) {
...
}
...
})
lub
Column(modifier = Modifier.onKeyEvent { event ->
if (event.type == KeyEventType.KeyUp && event.key == Key.Spacebar) {
...
}
...
})
Wyniki
Aplikacja może teraz reagować na naciśnięcia klawisza spacji, aby wstrzymać i wznowić odtwarzanie filmu lub innych multimediów.
Dodatkowe materiały
Więcej informacji o zdarzeniach z klawiatury i sposobach zarządzania nimi znajdziesz w sekcji Obsługa wprowadzania danych z klawiatury.
Odrzucenie dłoni rysika
Rysik może być niezwykle produktywnym i kreatywnym narzędziem na dużych ekranach. Jednak gdy użytkownicy rysują, piszą lub korzystają z aplikacji za pomocą rysika, czasem dotyka ekranu dłońmi. Zdarzenie dotknięcia może zostać zgłoszone do aplikacji, zanim system rozpozna i zamknie to zdarzenie jako przypadkowe dotknięcie dłoni.
Sprawdzone metody
Aplikacja musi wykrywać i ignorować zbędne zdarzenia dotknięcia. Android anuluje dotknięcie dłoni, wysyłając obiekt MotionEvent
. Sprawdź obiekt ACTION_CANCEL
lub ACTION_POINTER_UP
i FLAG_CANCELED
, aby określić, czy chcesz odrzucić gest dotknięcie dłoni.
Składniki
MotionEvent
: reprezentuje zdarzenia dotyku i ruchu. Zawiera informacje niezbędne do określenia, czy wydarzenie należy zignorować.OnTouchListener#onTouch()
: odbiera obiektyMotionEvent
.MotionEvent#getActionMasked()
: zwraca działanie powiązane ze zdarzeniem ruchu.ACTION_CANCEL
: stałaMotionEvent
, która wskazuje, że gest powinien zostać cofnięty.ACTION_POINTER_UP
: stałaMotionEvent
, która wskazuje, że wskaźnik inny niż pierwszy wyszedł w górę (czyli zrezygnował z kontaktu z ekranem urządzenia).FLAG_CANCELED
: stałaMotionEvent
, która wskazuje, że wzniesienie wskaźnika spowodowało niezamierzone zdarzenie polegające na dotknięciu. Dodano do zdarzeńACTION_POINTER_UP
iACTION_CANCEL
na Androidzie 13 (poziom API 33) i nowszych.
Kroki
Podsumowanie
Sprawdź obiekty MotionEvent
wysłane do Twojej aplikacji. Za pomocą interfejsów API MotionEvent
określ cechy zdarzeń:
- Wydarzenia z jednym punktem – sprawdź
ACTION_CANCEL
. W Androidzie 13 i nowszych sprawdź też aplikacjęFLAG_CANCELED
. - Wydarzenia z wieloma punktami – na Androidzie 13 i nowszych sprawdź
ACTION_POINTER_UP
iFLAG_CANCELED
.
Reaguj na wydarzenia ACTION_CANCEL
i ACTION_POINTER_UP
/FLAG_CANCELED
.
1. Pobieranie obiektów zdarzeń ruchu
Dodaj OnTouchListener
do aplikacji:
Kotlin
val myView = findViewById<View>(R.id.myView).apply { setOnTouchListener { view, event -> // Process motion event. } }
Java
View myView = findViewById(R.id.myView); myView.setOnTouchListener( (view, event) -> { // Process motion event. });
2. Określanie akcji i flag zdarzenia
Poszukaj parametru ACTION_CANCEL
, który oznacza zdarzenie z jednym wskaźnikiem na wszystkich poziomach interfejsu API. W Androidzie 13 i nowszych sprawdź dostępność aplikacji FLAG_CANCELED.
w aplikacji ACTION_POINTER_UP
Kotlin
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 } }
Java
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. Cofnij gest
Po rozpoznaniu dotknięcia dłoni możesz cofnąć działanie tego gestu na ekranie.
Aplikacja musi przechowywać historię działań użytkownika, aby umożliwić cofnięcie niezamierzonych działań (np. dotknięcia dłoni). Przykład znajdziesz w sekcji Wdrażanie podstawowej aplikacji do rysowania z modułu Wzbogacanie obsługi rysika w aplikacji na Androida.
Wyniki
Aplikacja może teraz rozpoznawać i odrzucać dotknięcia dłonią w przypadku wydarzeń z użyciem wielu punktów na Androidzie 13 i wyższych oraz w przypadku takich zdarzeń na wszystkich poziomach interfejsu API.
Dodatkowe materiały
Więcej informacji:
- Funkcje i interfejsy API Androida 13 – ulepszone odrzucanie dłoni
- Przewodniki dla programistów
- Ćwiczenia z programowania – Ulepszona obsługa rysika w aplikacji na Androida
Zarządzanie stanem komponentu WebView
WebView
to powszechnie stosowany komponent stanowiący zaawansowany system zarządzania stanem. Element WebView
musi utrzymywać swój stan i przewinąć pozycję po zmianie konfiguracji. Urządzenie WebView
może stracić pozycję przewijania, gdy użytkownik obróci urządzenie lub rozłoży składany telefon, co wymusza ponowne przewinięcie urządzenia z góry ekranu WebView
do poprzedniej pozycji przewijania.
Sprawdzone metody
Zminimalizuj liczbę odtworzeń elementu WebView
. WebView
dobrze sobie radzi z zarządzaniem swoim stanem, więc możesz wykorzystać tę jakość, zarządzając jak największą liczbą zmian w konfiguracji. Aplikacja musi obsługiwać zmiany konfiguracji, ponieważ odtwarzanie Activity
(sposób obsługi zmian konfiguracji przez system) również odtwarza WebView
, co powoduje, że WebView
traci swój stan.
Składniki
android:configChanges
: atrybut elementu<activity>
manifestu. Zawiera listę zmian konfiguracji obsługiwanych przez działanie.View#invalidate()
: metoda, która powoduje ponowne rysowanie widoku. Odziedziczone przez:WebView
.
Kroki
Podsumowanie
Aby zapisać stan WebView
, w miarę możliwości unikaj odtwarzania Activity
, a następnie zezwól WebView
na unieważnienie rozmiaru, aby zmienić rozmiar bez utraty stanu.
1. Dodaj zmiany konfiguracji do pliku AndroidManifest.xml
aplikacji
Aby uniknąć odtwarzania aktywności, określ zmiany konfiguracji obsługiwane przez aplikację (a nie przez system):
<activity
android:name=".MyActivity"
android:configChanges="screenLayout|orientation|screenSize
|keyboard|keyboardHidden|smallestScreenSize" />
2. Unieważnij WebView
za każdym razem, gdy aplikacja otrzyma zmianę konfiguracji
Kotlin
override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) webView.invalidate() }
Java
@Override public void onConfigurationChanged(@NonNull Configuration newConfig) { super.onConfigurationChanged(newConfig); webview.invalidate(); }
Ten krok dotyczy tylko systemu wyświetlania, ponieważ Jetpack Compose nie musi niczego unieważniać, aby prawidłowo zmienić rozmiar elementów Composable
. Jednak często funkcja tworzenia kopii zapasowych odtwarza element WebView
, jeśli nie jest on prawidłowo zarządzany. Użyj otoki Accompanist WebView, aby zapisać i przywrócić stan WebView
w aplikacjach do tworzenia wiadomości.
Wyniki
Komponenty WebView
w Twojej aplikacji zachowują teraz swój stan i położenie przewijają się w przypadku wielu zmian konfiguracji – od zmiany rozmiaru przez zmianę orientacji po złożenie i rozwinięcie.
Dodatkowe materiały
Więcej informacji o zmianach w konfiguracji i sposobach zarządzania nimi znajdziesz w artykule Obsługa zmian w konfiguracji.
Zarządzanie stanem RecyclerView
RecyclerView
może wyświetlać duże ilości danych przy minimalnej ilości zasobów graficznych. Gdy element RecyclerView
przewija listę elementów, RecyclerView
wykorzystuje ponownie wystąpienia elementów (View
), które wycofały się z ekranu, do tworzenia nowych. Jednak zmiany w konfiguracji (np. obrót urządzenia) mogą zresetować stan elementu RecyclerView
. Użytkownicy muszą ponownie przewinąć listę elementów RecyclerView
do poprzedniej pozycji.
Sprawdzone metody
Podczas wszystkich zmian konfiguracji RecyclerView
powinna zachowywać swój stan – a w szczególności pozycję przewijania – i stan elementów listy.
Składniki
RecyclerView.Adapter#setStateRestorationPolicy()
: określa, w jaki sposóbRecyclerView.Adapter
przywraca swój stan po zmianie konfiguracji.ViewModel
: blokuje stan aktywności lub fragmentu.
Kroki
Podsumowanie
Ustaw zasadę przywracania stanu RecyclerView.Adapter
, aby zapisać pozycję przewijania RecyclerView
. Zapisz stan RecyclerView
elementu listy. Dodaj stan elementów listy do adaptera RecyclerView
i przywróć stan elementów listy, gdy są powiązane z elementem ViewHolder
.
1. Włącz zasadę przywracania stanu Adapter
Włącz zasadę przywracania stanu adaptera RecyclerView
, aby utrzymać pozycję przewijania RecyclerView
po zmianie konfiguracji. Dodaj specyfikację zasad do konstruktora adaptera:
Kotlin
class MyAdapter() : RecyclerView.Adapter() { init { stateRestorationPolicy = StateRestorationPolicy.PREVENT_WHEN_EMPTY } ... }
Java
class MyAdapter extends RecyclerView.Adapter{ public Adapter() { setStateRestorationPolicy(StateRestorationPolicy.PREVENT_WHEN_EMPTY); } ... }
2. Zapisz stan elementów listy stanowej
Zapisz stan złożonych elementów listy RecyclerView
, np. elementów zawierających elementy EditText
. Jeśli na przykład chcesz zapisać stan elementu EditText
, dodaj wywołanie zwrotne podobne do modułu obsługi onClick
w celu przechwycenia zmian w tekście. W wywołaniu zwrotnym określ, jakie dane chcesz zapisać:
Kotlin
input.addTextChangedListener( afterTextChanged = { text -> text?.let { // Save state here. } } )
Java
input.addTextChangedListener(new TextWatcher() { ... @Override public void afterTextChanged(Editable s) { // Save state here. } });
Zadeklaruj wywołanie zwrotne w Activity
lub Fragment
. W polu ViewModel
możesz zapisywać informacje o stanie.
3. Dodaj stan elementu listy do: Adapter
Dodaj stan elementów listy do RecyclerView.Adapter
. Przekaż stan elementu do konstruktora adaptera podczas tworzenia hosta Activity
lub Fragment
:
Kotlin
val adapter = MyAdapter(items, viewModel.retrieveState())
Java
MyAdapter adapter = new MyAdapter(items, viewModel.retrieveState());
4. Przywróć stan elementu listy w elemencie ViewHolder
adaptera
Gdy w usłudze RecyclerView.Adapter
powiążesz element ViewHolder
z elementem, przywróć jego stan:
Kotlin
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) } }
Java
@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)); }
Wyniki
Urządzenie RecyclerView
może teraz przywrócić pozycję przewijania i stan wszystkich elementów na liście RecyclerView
.
Dodatkowe materiały
Zarządzanie odłączaną klawiaturą
Obsługa odłączanych klawiatur pomaga zmaksymalizować produktywność użytkowników korzystających z urządzeń z dużymi ekranami. Android wywołuje zmianę konfiguracji za każdym razem, gdy do urządzenia zostanie podłączona klawiatura lub od niej odłączona, co może spowodować utratę stanu interfejsu. Aplikacja może zapisać i przywracać swój stan, pozwalając systemowi na odtwarzanie aktywności lub ograniczać jej odtwarzanie w przypadku zmian konfiguracji klawiatury.
We wszystkich przypadkach wszystkie dane związane z klawiaturą są przechowywane w obiekcie Configuration
. Pola keyboard
i keyboardHidden
obiektu konfiguracji zawierają informacje o typie klawiatury i jej dostępności.
Sprawdzone metody
Aplikacje zoptymalizowane pod kątem dużych ekranów obsługują wszystkie typy urządzeń wejściowych, od klawiatur programowych i sprzętowych po rysik, mysz, trackpad i inne urządzenia peryferyjne.
Obsługa klawiatur zewnętrznych wymaga zmian w konfiguracji, którymi można zarządzać na jeden z dwóch sposobów:
- Pozwól systemowi na odtworzenie aktualnie uruchomionej aktywności, a Ty będziesz zarządzać stanem aplikacji.
- Samodzielnie zarządzaj zmianą konfiguracji (aktywność nie zostanie odtworzona):
- Deklarowanie wszystkich wartości konfiguracyjnych związanych z klawiaturą
- Tworzenie modułu obsługi zmian konfiguracji
Aplikacje zwiększające produktywność, które często wymagają dokładnej kontroli interfejsu w zakresie wpisywania tekstu i innych wprowadzania danych, mogą skorzystać z samodzielnego wprowadzania zmian w konfiguracji.
W szczególnych przypadkach może Ci być potrzebna zmiana układu aplikacji po podłączeniu lub odłączeniu klawiatury sprzętowej, na przykład aby zrobić więcej miejsca na narzędzia lub edytować okna.
Jedynym niezawodnym sposobem wychwytywania zmian konfiguracji jest zastąpienie metody onConfigurationChanged()
widoku, dlatego możesz dodać nową instancję View do aktywności w aplikacji i odpowiadać w module obsługi onConfigurationChanged()
widoku na zmiany konfiguracji spowodowane podłączeniem lub odłączeniem klawiatury.
Składniki
android:configChanges
: atrybut elementu<activity>
manifestu aplikacji. Informuje system o zmianach w konfiguracji, którymi zarządza aplikacja.View#onConfigurationChanged()
: metoda, która reaguje na propagację nowej konfiguracji aplikacji.
Kroki
Podsumowanie
Zadeklaruj atrybut configChanges
i dodaj wartości związane z klawiaturą. Dodaj obiekt View
do hierarchii widoków aktywności i nasłuchuj zmian w konfiguracji.
1. Zadeklaruj atrybut configChanges
Zaktualizuj element <activity>
w manifeście aplikacji, dodając wartości keyboard|keyboardHidden
do listy już zarządzanych zmian konfiguracji:
<activity
…
android:configChanges="...|keyboard|keyboardHidden">
2. Dodawanie pustego widoku do hierarchii widoków
Zadeklaruj nowy widok i dodaj kod modułu obsługi w metodzie onConfigurationChanged()
widoku:
Kotlin
val v = object : View(this) { override fun onConfigurationChanged(newConfig: Configuration?) { super.onConfigurationChanged(newConfig) // Handler code here. } }
Java
View v = new View(this) { @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); // Handler code here. } };
Wyniki
Aplikacja będzie teraz reagowała na podłączenie lub odłączenie klawiatury zewnętrznej bez odtwarzania aktualnie uruchomionej aktywności.
Dodatkowe materiały
Aby dowiedzieć się, jak zapisać stan interfejsu aplikacji podczas zmian konfiguracji, takich jak podłączenie lub odłączenie klawiatury, przeczytaj artykuł Zapisywanie stanów interfejsu użytkownika.