Informuj o zwijaniu aplikacji

Duże rozłożone wyświetlacze i wyjątkowy sposób złożenia urządzenia pozwalają wygodnie urządzeniach składanych. Aby umożliwić korzystanie z aplikacji na składanych urządzeniach, użyj biblioteki Jetpack WindowManager, która udostępnia interfejs API do obsługi funkcji okien na składanych urządzeniach, takich jak składanie i zginanie. Gdy aplikacja jest składana, może dostosować swój układ. aby uniknąć umieszczania ważnych treści w częściach fałdowych lub zawiasach, i zawiasów jako naturalny separator.

Wiedza o tym, czy urządzenie obsługuje konfiguracje takie jak ułożenie na stole czy w księgu, może pomóc w podejmowaniu decyzji o obsługiwaniu różnych układów lub udostępnianiu określonych funkcji.

Informacje o oknie

Interfejs WindowInfoTracker w Jetpack WindowManager udostępnia informacje o układzie okna. Metoda windowLayoutInfo() interfejsu zwraca strumień danych WindowLayoutInfo, który informuje aplikację o stanie składania składanego urządzenia. Metoda WindowInfoTracker#getOrCreate() tworzy wystąpienia WindowInfoTracker.

WindowManager zapewnia obsługę zbierania danych z WindowLayoutInfo przy użyciu Przepływy Kotlin i wywołania zwrotne Java.

Płynące w kotlinie

Aby rozpocząć i zatrzymać zbieranie danych w usłudze WindowLayoutInfo, możesz użyć polecanego ponownie wspólnoty dostosowanej do cyklu życia, w której znajduje się blok kodu repeatOnLifecycle jest wykonywane, gdy cykl życia wynosi co najmniej STARTED i zostaje zatrzymany, gdy cykl życia wynosi STOPPED. Wykonanie bloku kodu jest automatycznie ponownie uruchamiane gdy cykl życia ponownie wynosi STARTED. W tym przykładzie blok kodu zbiera i używa danych WindowLayoutInfo:

class DisplayFeaturesActivity : AppCompatActivity() {

    private lateinit var binding: ActivityDisplayFeaturesBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = ActivityDisplayFeaturesBinding.inflate(layoutInflater)
        setContentView(binding.root)

        lifecycleScope.launch(Dispatchers.Main) {
            lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
                WindowInfoTracker.getOrCreate(this@DisplayFeaturesActivity)
                    .windowLayoutInfo(this@DisplayFeaturesActivity)
                    .collect { newLayoutInfo ->
                        // Use newLayoutInfo to update the layout.
                    }
            }
        }
    }
}

Wywołania zwrotne w Javie

Warstwa zgodności wywołania zwrotnego zawarta w zależności androidx.window:window-java umożliwia zbieranie aktualizacji WindowLayoutInfo bez używania kodu Kotlin. Artefakt zawiera klasy WindowInfoTrackerCallbackAdapter, która dostosowuje WindowInfoTracker do obsługi rejestrowania (i wyrejestrowania) wywołań zwrotnych do otrzymywać aktualizacje z zakresu WindowLayoutInfo, na przykład:

public class SplitLayoutActivity extends AppCompatActivity {

    private WindowInfoTrackerCallbackAdapter windowInfoTracker;
    private ActivitySplitLayoutBinding binding;
    private final LayoutStateChangeCallback layoutStateChangeCallback =
            new LayoutStateChangeCallback();

   @Override
   protected void onCreate(@Nullable Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);

       binding = ActivitySplitLayoutBinding.inflate(getLayoutInflater());
       setContentView(binding.getRoot());

       windowInfoTracker =
                new WindowInfoTrackerCallbackAdapter(WindowInfoTracker.getOrCreate(this));
   }

   @Override
   protected void onStart() {
       super.onStart();
       windowInfoTracker.addWindowLayoutInfoListener(
                this, Runnable::run, layoutStateChangeCallback);
   }

   @Override
   protected void onStop() {
       super.onStop();
       windowInfoTracker
           .removeWindowLayoutInfoListener(layoutStateChangeCallback);
   }

   class LayoutStateChangeCallback implements Consumer<WindowLayoutInfo> {
       @Override
       public void accept(WindowLayoutInfo newLayoutInfo) {
           SplitLayoutActivity.this.runOnUiThread( () -> {
               // Use newLayoutInfo to update the layout.
           });
       }
   }
}

Obsługa RxJava

Jeśli używasz już RxJava (w wersji 2 lub 3), możesz korzystać z elementów, które umożliwiają użycie Observable lub Flowable do zbierania aktualizacji WindowLayoutInfo bez korzystania z przepływu w Kotlinie.

Warstwa zgodności udostępniana przez interfejsy androidx.window:window-rxjava2 oraz Zależności androidx.window:window-rxjava3 obejmują WindowInfoTracker#windowLayoutInfoFlowable() i WindowInfoTracker#windowLayoutInfoObservable(), które pozwalają włączyć aby otrzymywać WindowLayoutInfo aktualizacje, na przykład:

class RxActivity: AppCompatActivity {

    private lateinit var binding: ActivityRxBinding

    private var disposable: Disposable? = null
    private lateinit var observable: Observable<WindowLayoutInfo>

   @Override
   protected void onCreate(@Nullable Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);

       binding = ActivitySplitLayoutBinding.inflate(getLayoutInflater());
       setContentView(binding.getRoot());

        // Create a new observable.
        observable = WindowInfoTracker.getOrCreate(this@RxActivity)
            .windowLayoutInfoObservable(this@RxActivity)
   }

   @Override
   protected void onStart() {
       super.onStart();

        // Subscribe to receive WindowLayoutInfo updates.
        disposable?.dispose()
        disposable = observable
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe { newLayoutInfo ->
            // Use newLayoutInfo to update the layout.
        }
   }

   @Override
   protected void onStop() {
       super.onStop();

        // Dispose of the WindowLayoutInfo observable.
        disposable?.dispose()
   }
}

Funkcje składanych ekranów

Klasa WindowLayoutInfo obiektu Jetpack WindowManager tworzy funkcje okno wyświetlania dostępne w postaci listy elementów DisplayFeature.

FoldingFeature to typ elementu DisplayFeature, który zawiera informacje o składanych ekranach, w tym:

  • state: stan złożenia urządzenia, FLAT lub HALF_OPENED

  • orientation: orientacja strony widocznej na ekranie lub zawiasu, HORIZONTAL lub VERTICAL

  • occlusionType: czy zawias lub zawias ukrywa część wyświetlacza: NONE lub FULL

  • isSeparating: czy złożenie lub zawias tworzy 2 logiczne obszary wyświetlacza (prawda lub fałsz).

Urządzenie składane, które jest HALF_OPENED, zawsze zgłasza isSeparating jako prawda bo ekran jest podzielony na dwa obszary. Ponadto na urządzeniu z dwoma ekranami isSeparating jest zawsze true, gdy aplikacja obejmuje oba ekrany.

Właściwość FoldingFeature bounds (odziedziczona z DisplayFeature) reprezentuje prostokąt ograniczający element składany, np. zawias. Za pomocą tych granic możesz umieszczać elementy na ekranie względem funkcji:

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    ...
    lifecycleScope.launch(Dispatchers.Main) {
        lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
            // Safely collects from WindowInfoTracker when the lifecycle is
            // STARTED and stops collection when the lifecycle is STOPPED.
            WindowInfoTracker.getOrCreate(this@MainActivity)
                .windowLayoutInfo(this@MainActivity)
                .collect { layoutInfo ->
                    // New posture information.
                    val foldingFeature = layoutInfo.displayFeatures
                        .filterIsInstance<FoldingFeature>()
                        .firstOrNull()
                    // Use information from the foldingFeature object.
                }

        }
    }
}

Java

private WindowInfoTrackerCallbackAdapter windowInfoTracker;
private final LayoutStateChangeCallback layoutStateChangeCallback =
                new LayoutStateChangeCallback();

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    ...
    windowInfoTracker =
            new WindowInfoTrackerCallbackAdapter(WindowInfoTracker.getOrCreate(this));
}

@Override
protected void onStart() {
    super.onStart();
    windowInfoTracker.addWindowLayoutInfoListener(
            this, Runnable::run, layoutStateChangeCallback);
}

@Override
protected void onStop() {
    super.onStop();
    windowInfoTracker.removeWindowLayoutInfoListener(layoutStateChangeCallback);
}

class LayoutStateChangeCallback implements Consumer<WindowLayoutInfo> {
    @Override
    public void accept(WindowLayoutInfo newLayoutInfo) {
        // Use newLayoutInfo to update the Layout.
        List<DisplayFeature> displayFeatures = newLayoutInfo.getDisplayFeatures();
        for (DisplayFeature feature : displayFeatures) {
            if (feature instanceof FoldingFeature) {
                // Use information from the feature object.
            }
        }
    }
}

Postawa przy stole

Korzystając z informacji zawartych w obiekcie FoldingFeature, aplikacja może obsługiwać tryby takie jak stolik, w którym telefon leży na powierzchni, zawias jest w pozycji poziomej, a ekran składany jest w połowie otwarty.

Konstrukcja stołu zapewnia użytkownikom wygodę i umożliwia korzystanie z telefonów bez trzymając telefon w rękach. Pozycja na stole jest idealna do oglądania multimediów, robienia zdjęć i prowadzenia rozmów wideo.

Rysunek 1. Aplikacja odtwarzacza wideo w trybie stołu.

Użyj FoldingFeature.StateFoldingFeature.Orientation, aby określić, czy urządzenie jest w pozycji poziomej:

Kotlin

fun isTableTopPosture(foldFeature : FoldingFeature?) : Boolean {
    contract { returns(true) implies (foldFeature != null) }
    return foldFeature?.state == FoldingFeature.State.HALF_OPENED &&
            foldFeature.orientation == FoldingFeature.Orientation.HORIZONTAL
}

Java

boolean isTableTopPosture(FoldingFeature foldFeature) {
    return (foldFeature != null) &&
           (foldFeature.getState() == FoldingFeature.State.HALF_OPENED) &&
           (foldFeature.getOrientation() == FoldingFeature.Orientation.HORIZONTAL);
}

Gdy ustalisz, że urządzenie jest w stanie stołu, zaktualizuj układ aplikacji odpowiednio się zmienia. W przypadku aplikacji multimedialnych oznacza to zwykle umieszczenie odtwarzania nad resztą interfejsu oraz elementów sterujących i treści dodatkowych poniżej, aby umożliwić oglądanie lub słuchanie bez użycia rąk.

W Androidzie 15 (poziom interfejsu API 35) i nowszych możesz wywołać interfejs API synchronicznego, aby wykryć, czy urządzenie obsługuje pozycję na stole niezależnie od jego bieżącego stanu.

Interfejs API udostępnia listę pozycji obsługiwanych przez urządzenie. Jeśli lista zawiera stan stołu, możesz podzielić układ aplikacji do obsługi stanu i wykonywać testy A/B w interfejsie aplikacji dla układów na stołach i w trybie pełnoekranowym.

Kotlin

if (WindowSdkExtensions.getInstance().extensionsVersion >= 6) {
    val postures = WindowInfoTracker.getOrCreate(context).supportedPostures
    if (postures.contains(TABLE_TOP)) {
        // Device supports tabletop posture.
   }
}

Java

if (WindowSdkExtensions.getInstance().getExtensionVersion() >= 6) {
    List<SupportedPosture> postures = WindowInfoTracker.getOrCreate(context).getSupportedPostures();
    if (postures.contains(SupportedPosture.TABLETOP)) {
        // Device supports tabletop posture.
    }
}

Przykłady

Postawa podczas czytania

Kolejną unikalną funkcją składanego urządzenia jest tryb książki, w którym urządzenie jest w połowie otwarte, a zawias jest pionowy. Stan książki świetnie sprawdza się podczas czytania e-booków. Układ dwustronny na dużym ekranie, który można otworzyć jak książkę, pozwala na odwzorowanie pozycji czytania prawdziwej książki.

Możesz też używać go do robienia zdjęć, jeśli chcesz uzyskać inny format obrazu podczas robienia zdjęć bez użycia rąk.

Wdróż stan książki przy użyciu tych samych technik, które są stosowane w przypadku stanu stołu. Jedyną różnicą jest to, że kod powinien sprawdzić, czy orientacja funkcji zwijania w pionie zamiast w poziomie:

Kotlin

fun isBookPosture(foldFeature : FoldingFeature?) : Boolean {
    contract { returns(true) implies (foldFeature != null) }
    return foldFeature?.state == FoldingFeature.State.HALF_OPENED &&
            foldFeature.orientation == FoldingFeature.Orientation.VERTICAL
}

Java

boolean isBookPosture(FoldingFeature foldFeature) {
    return (foldFeature != null) &&
           (foldFeature.getState() == FoldingFeature.State.HALF_OPENED) &&
           (foldFeature.getOrientation() == FoldingFeature.Orientation.VERTICAL);
}

Zmiany rozmiaru okna

Obszar wyświetlania aplikacji może się zmieniać w zależności od konfiguracji urządzenia, na przykład gdy urządzenie jest złożone lub rozłożone, gdy jest obracane lub gdy zmienia się rozmiar okna w trybie wielookiennym.

Klasa Jetpack WindowManager WindowMetricsCalculator umożliwia: pobierze bieżące i maksymalne wskaźniki dotyczące okien. Polub platformę WindowMetrics wprowadziliśmy w interfejsie API poziomu 30 – WindowManager WindowMetrics podaje granice okien, ale interfejs API jest zgodny wstecznie aż do poziomu API 14.

Zobacz Używanie klas rozmiaru okna.

Dodatkowe materiały

Próbki

  • Jetpack WindowManager: przykład korzystania z Jetpacka Biblioteka WindowManager
  • Jetcaster: implementacja postawy stołu w Compose

Ćwiczenia z programowania