Informuj o zwijaniu aplikacji

Duży rozłożony wyświetlacz i unikalny tryb złożenia zapewniają nowe wrażenia użytkownikom składanych urządzeń. Aby o tym poinformować, użyj biblioteki Jetpack WindowManager, która udostępnia interfejs API dla funkcji składanych okien, takich jak składanie i zawiasy. Jeśli aplikacja jest złożona, może dostosować swój układ, aby uniknąć umieszczania ważnych treści w obszarze zagięcia lub zawiasów, oraz stosować zagięcia i zawiasy jako naturalne separatory.

Informacje o oknie

Interfejs WindowInfoTracker w Jetpack WindowManager udostępnia informacje o układzie okien. Metoda windowLayoutInfo() interfejsu zwraca strumień danych WindowLayoutInfo, które informują aplikację o stanie złożenia urządzenia. Metoda WindowInfoTracker getOrCreate() tworzy wystąpienie elementu WindowInfoTracker.

WindowManager obsługuje zbieranie danych WindowLayoutInfo za pomocą Kotlin Flows i wywołań zwrotnych Java.

Kotlin Flows

Aby rozpocząć i zatrzymać zbieranie danych w usłudze WindowLayoutInfo, możesz użyć korektywy rejestrującej cykl życia z możliwością ponownego uruchomienia, w której blok kodu repeatOnLifecycle jest wykonywany, gdy cykl życia ma co najmniej STARTED, a jest zatrzymany, gdy cykl życia to STOPPED. Wykonanie bloku kodu jest automatycznie wznawiane, gdy cykl życia wynosi STARTED. W tym przykładzie blok kodu zbiera i wykorzystuje dane 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 Java

Warstwa zgodności wywołania zwrotnego zawartej w zależności androidx.window:window-java umożliwia zbieranie aktualizacji WindowLayoutInfo bez użycia przepływu Kotlin. Artefakt zawiera klasę WindowInfoTrackerCallbackAdapter, która dostosowuje WindowInfoTracker do obsługi rejestrowania (i wyrejestrowania) wywołań zwrotnych w celu otrzymywania aktualizacji 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 (wersja 2 lub 3), możesz skorzystać z artefaktów umożliwiających korzystanie z artefaktów Observable lub Flowable do zbierania aktualizacji WindowLayoutInfo bez użycia usługi Kotlin Flow.

Warstwa zgodności zapewniana przez zależności androidx.window:window-rxjava2 i androidx.window:window-rxjava3 obejmuje metody WindowInfoTracker#windowLayoutInfoFlowable() i WindowInfoTracker#windowLayoutInfoObservable(), które umożliwiają otrzymywanie aktualizacji aplikacji WindowLayoutInfo, 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 the WindowLayoutInfo observable
        disposable?.dispose()
   }
}

Funkcje składanych ekranów

Klasa WindowLayoutInfo Jetpack WindowManager udostępnia funkcje okna wyświetlania w postaci listy elementów DisplayFeature.

FoldingFeature to rodzaj tagu DisplayFeature, który zawiera informacje o składanych ekranach, m.in.:

  • state: stan złożenia urządzenia (FLAT lub HALF_OPENED).
  • orientation: orientacja złożenia lub zawiasu: HORIZONTAL lub VERTICAL.
  • occlusionType: określa, czy składanie lub zawias zakrywa część wyświetlacza, NONE czy FULL
  • isSeparating: określa, czy zawijanie lub zawias tworzy 2 logiczne obszary wyświetlania: prawda lub fałsz.

Urządzenie składane, które jest HALF_OPENED, zawsze zgłasza isSeparating jako prawdę, ponieważ ekran jest podzielony na 2 obszary wyświetlania. Dodatkowo na urządzeniach z 2 ekranami zasada isSeparating ma zawsze wartość prawda, gdy aplikacja obejmuje oba ekrany.

Właściwość FoldingFeature bounds (odziedziczona z DisplayFeature) reprezentuje prostokąt blokujący elementy składane, takie jak składanie lub zawias. Granice mogą służyć do określania pozycji elementów na ekranie względem obiektu.

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    ...
    lifecycleScope.launch(Dispatchers.Main) {
        lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
            // Safely collects from windowInfoRepo 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()
                        .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
            }
        }
    }
}

Tryb na stole

Dzięki informacjom zawartym w obiekcie FoldingFeature aplikacja może obsługiwać różne pozycje, takie jak tryb stołu, zawias telefonu umieszczony na powierzchni, zawias w pozycji poziomej, a składany ekran jest w połowie otwarty.

Tryb Na stole zapewnia użytkownikom wygodę obsługi telefonu bez konieczności trzymania go w dłoniach. Tryb Na stole doskonale sprawdza się do oglądania multimediów, robienia zdjęć i rozmów wideo.

Aplikacja odtwarzacza wideo w trybie stołu

Za pomocą funkcji FoldingFeature.State i FoldingFeature.Orientation określ, czy urządzenie jest w trybie Na stole:

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 urządzenie działa w trybie Na stole, zaktualizuj odpowiednio układ aplikacji. W przypadku aplikacji multimedialnych oznacza to zazwyczaj umieszczenie odtwarzania w części strony widocznej na ekranie oraz elementów sterujących pozycjonowaniem i dodatkowymi treściami tuż poniżej, aby umożliwić oglądanie i słuchanie bez użycia rąk.

Przykłady

  • Aplikacja MediaPlayerActivity: zobacz, jak za pomocą Media3 Exoplayer i WindowManagera utworzyć składany odtwarzacz wideo.

  • Ćwiczenie z programowania dotyczące rozwijania funkcji aparatu: dowiedz się, jak wdrożyć tryb stołu w aplikacjach fotograficznych. Pokaż wizjer w górnej części ekranu, w części strony widocznej na ekranie, a elementy sterujące na dole, w części strony widocznej po przewinięciu.

Tryb książki

Innym przykładem urządzenia, który można złożyć, jest tryb książki, w którym urządzenie jest do połowy otwarte, a zawias – pionowy. Tryb książki doskonale nadaje się do czytania e-booków. Dwustronicowy układ na dużym, składanym ekranie, tak jak książka na oprawie, umożliwia czytanie prawdziwej książki.

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

Zaimplementuj tryb książki za pomocą tych samych technik, które są stosowane w trybie Na stole. Jedyną różnicą jest to, że kod powinien sprawdzać, czy funkcja zwijania ma orientację pionową, a nie poziomą:

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);
}

Zmiana rozmiaru okna

Wraz ze zmianą konfiguracji urządzenia, np. po złożeniu lub rozłożeniu, obróceniu lub zmianie rozmiaru okna w trybie wielu okien, może również zmieniać się obszar wyświetlania aplikacji.

Klasa Jetpack WindowManager WindowMetricsCalculator umożliwia pobieranie bieżących i maksymalnych wskaźników okna. Podobnie jak platforma WindowMetrics wprowadzona w interfejsie API poziomu 30, WindowManager WindowMetrics wyznacza granice okien, ale interfejs API jest zgodny wstecznie do poziomu API 14.

Informacje o obsłudze różnych rozmiarów okien znajdziesz w artykule Obsługa różnych rozmiarów ekranu.

Dodatkowe materiały

Próbki

Ćwiczenia z programowania