Książka kucharska na duży ekran

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.

5 gwiazdek Spełnia kryteria poziomu 1, różnica w dużym ekranie
Czterogwiazdkowy Spełnia kryteria poziomu 2, optymalizacja na duży ekran
Trzy gwiazdki Spełnia kryteria poziomu 3, obsługa dużego ekranu
2 gwiazdki Oferuje niektóre funkcje dużego ekranu, ale nie spełnia wymagań dotyczących jakości aplikacji na duży ekran
Jedna gwiazdka Spełnia potrzeby określonego przypadku użycia, ale nie obsługuje poprawnie dużych ekranów.

Obsługa kamery Chromebooka

Trzy gwiazdki

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

2 gwiazdki

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 jako this.
  • Zamiast computeCurrentWindowMetrics() używana jest metoda computeMaximumWindowMetrics(), 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

Czterogwiazdkowy

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

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 przypadku onPreviewKeyEvent ta zasada Modifier 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

5 gwiazdek

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 obiekty MotionEvent.
  • MotionEvent#getActionMasked(): zwraca działanie powiązane ze zdarzeniem ruchu.
  • ACTION_CANCEL: stała MotionEvent, która wskazuje, że gest powinien zostać cofnięty.
  • ACTION_POINTER_UP: stała MotionEvent, która wskazuje, że wskaźnik inny niż pierwszy wyszedł w górę (czyli zrezygnował z kontaktu z ekranem urządzenia).
  • FLAG_CANCELED: stała MotionEvent, która wskazuje, że wzniesienie wskaźnika spowodowało niezamierzone zdarzenie polegające na dotknięciu. Dodano do zdarzeń ACTION_POINTER_UP i ACTION_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 i FLAG_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:

Zarządzanie stanem komponentu WebView

Trzy gwiazdki

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

Trzy gwiazdki

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

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ą

Trzy gwiazdki

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:

  1. Pozwól systemowi na odtworzenie aktualnie uruchomionej aktywności, a Ty będziesz zarządzać stanem aplikacji.
  2. 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.