Informuj o zwijaniu aplikacji

Duże rozłożone ekrany i wyjątkowy sposób złożenia urządzenia pozwalają wygodniej korzystać z urządzeń składanych. Aby Twoja aplikacja była widoczna bez przewijania, użyj biblioteki Jetpack WindowManager, która udostępnia interfejs API do obsługi funkcji w oknie urządzenia składanego, takich jak złożenie czy zawias. Aplikacja może dostosować swój układ, aby uniknąć umieszczania ważnych treści w zawiasach i farbach, i używać zawiasów oraz fałdów jako naturalnych separatorów.

Informacje o oknie

Interfejs WindowInfoTracker w programie Jetpack WindowManager udostępnia informacje o układzie okien. Metoda windowLayoutInfo() interfejsu zwraca strumień danych WindowLayoutInfo, który informuje aplikację o stanie złożenia urządzenia składanego. Metoda WindowInfoTracker getOrCreate() tworzy instancję WindowInfoTracker.

WindowManager obsługuje zbieranie danych WindowLayoutInfo przy użyciu przepływów Kotlin i wywołań zwrotnych Java.

Kotlin

Aby rozpocząć i zatrzymać zbieranie danych w usłudze WindowLayoutInfo, możesz użyć ponownego uruchomienia uwzględniającego cykl życia, w którym blok kodu repeatOnLifecycle jest wykonywany, gdy cykl życia wynosi co najmniej STARTED i zatrzymuje się, gdy cykl życia wynosi STOPPED. Wykonanie bloku kodu jest automatycznie ponownie uruchamiane po powrocie cyklu życia STARTED. W poniższym 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 w Javie

Warstwa zgodności wywołań zwrotnych zawarty w zależności androidx.window:window-java umożliwia zbieranie aktualizacji WindowLayoutInfo bez użycia platformy Kotlin Flow. Artefakt zawiera klasę WindowInfoTrackerCallbackAdapter, która dostosowuje WindowInfoTracker do obsługi rejestrowania (i wyrejestrowania) wywołań zwrotnych, aby otrzymywać aktualizacje 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 korzystasz już z RxJava (wersja 2 lub 3), możesz skorzystać z artefaktów umożliwiających gromadzenie WindowLayoutInfo aktualizacji za pomocą urządzenia Observable lub Flowable bez użycia przepływu Kotlin.

Warstwa zgodności udostępniana przez zależności androidx.window:window-rxjava2 i androidx.window:window-rxjava3 obejmuje metody WindowInfoTracker#windowLayoutInfoFlowable() i WindowInfoTracker#windowLayoutInfoObservable(), które umożliwiają aplikacji otrzymywanie aktualizacji 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 usługi Jetpack WindowManager udostępnia funkcje okna wyświetlania w postaci listy elementów DisplayFeature.

FoldingFeature to typ elementu DisplayFeature, który zawiera informacje o składanych wyświetlaczach, m.in.:

  • state: stan złożenia urządzenia, FLAT lub HALF_OPENED
  • orientation: orientacja strony widocznej na ekranie lub zawiasu, HORIZONTAL lub VERTICAL.
  • occlusionType: czy złożenie lub zawias zakrywa część wyświetlacza, NONE lub FULL
  • isSeparating: określa, czy zawias lub strona zwinięcia tworzy 2 logiczne obszary wyświetlania – true lub false (prawda) lub false (fałsz).

W przypadku urządzenia składanego, które jest HALF_OPENED, zawsze zgłaszana jest wartość isSeparating jako prawda, ponieważ ekran jest podzielony na 2 obszary wyświetlania. Oprócz tego zasada isSeparating ma wartość prawda na urządzeniu z 2 ekranami, gdy aplikacja obejmuje oba ekrany.

Właściwość FoldingFeature bounds (odziedziczona z DisplayFeature) reprezentuje prostokąt ograniczający element strony zawijanej, taki jak zawias lub zwinięcie. Granice mogą służyć do określania pozycji elementów na ekranie względem danego 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 stany, takie jak tryb stołu, telefon opiera się na powierzchni, zawias jest w pozycji poziomej, a składany ekran jest w połowie otwarty.

Tryb Na stole pozwala użytkownikom wygodnie obsługiwać telefon bez trzymania go w rękach. Tryb Na stole świetnie nadaje się do oglądania multimediów, robienia zdjęć i prowadzenia rozmów wideo.

Aplikacja odtwarzacza wideo w trybie Na stole

Użyj FoldingFeature.State i FoldingFeature.Orientation, aby określić, 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 będzie w trybie Na stole, zaktualizuj odpowiednio układ aplikacji. W przypadku aplikacji do multimediów zazwyczaj oznacza to umieszczenie odtwarzania w części strony widocznej po przewinięciu oraz pozycjonowanie elementów sterujących i dodatkowych materiałów poniżej, aby można było je oglądać lub słuchać bez użycia rąk.

Przykłady

  • Aplikacja MediaPlayerActivity: zobacz, jak za pomocą Media3 Exoplayer i WindowManager utworzyć rozłożony odtwarzacz wideo.

  • Zaawansowane funkcje aparatu – ćwiczenia z programowania: 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 i elementach sterujących w dolnej części, w części strony widocznej po przewinięciu.

Tryb rezerwacji

Inną wyjątkową cechą urządzenia składanego jest tryb książki, w którym urządzenie jest w połowie otwarte, a zawias jest ustawiony w pionie. Tryb książki świetnie sprawdza się podczas czytania e-booków. Dzięki dwustronicowemu układowi na dużym, składanym ekranie (tak jak zawiętych książek) możesz dowiedzieć się, jak czytać prawdziwą książkę.

Przydaje się to też do fotografowania, gdy chcesz robić zdjęcia bez użycia rąk w innym formacie obrazu.

Zaimplementuj tryb książki przy użyciu tych samych metod co 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

Obszar wyświetlania aplikacji może się zmienić w wyniku zmiany konfiguracji urządzenia, na przykład po złożeniu lub rozłożeniu urządzenia, obróceniu go albo zmianie rozmiaru okna w trybie wielu okien.

Klasa Jetpack WindowManager WindowMetricsCalculator umożliwia pobieranie bieżących i maksymalnych wskaźników okien. Podobnie jak w przypadku platformy WindowMetrics wprowadzonej na poziomie interfejsu API 30, narzędzie WindowManager WindowMetrics podaje granice okien, ale interfejs API jest zgodny wstecznie do poziomu 14.

Zobacz Klasy rozmiaru okna.

Dodatkowe materiały

Próbki

Ćwiczenia z programowania