Zgodność urządzeń wejściowych na dużych ekranach

Na urządzeniach z dużym ekranem użytkownicy często wchodzą w interakcje z aplikacjami za pomocą klawiatury, myszy, trackpada, rysika lub pada do gier. Aby umożliwić aplikacji przyjmowanie danych wejściowych z urządzeń zewnętrznych:

  • Przetestuj podstawową obsługę klawiatury, np. Ctrl+Z do cofania, Ctrl+C do kopiowania i Ctrl+S do zapisywania. Listę domyślnych skrótów klawiszowych znajdziesz w artykule Obsługa działań wykonywanych za pomocą klawiatury.
  • Przetestuj zaawansowaną obsługę klawiatury, np. nawigację za pomocą klawisza Tab i strzałek, potwierdzanie wprowadzania tekstu za pomocą klawisza Enter, oraz odtwarzanie i wstrzymywanie w aplikacjach multimedialnych za pomocą spacji.
  • Przetestuj podstawowe interakcje z myszą, w tym kliknięcie prawym przyciskiem myszy w celu otwarcia menu kontekstowego, zmiany ikon po najechaniu kursorem oraz zdarzenia przewijania za pomocą kółka myszy lub trackpada w komponentach niestandardowych.
  • Przetestuj urządzenia wejściowe specyficzne dla aplikacji, takie jak rysik, kontrolery do gier i kontrolery MIDI w aplikacjach muzycznych.
  • Rozważ obsługę zaawansowanych danych wejściowych, które mogą wyróżnić aplikację w środowiskach komputerowych, np. trackpad jako crossfader w aplikacjach dla DJ-ów, przechwytywanie myszy w grach i skróty klawiszowe dla użytkowników, którzy głównie korzystają z klawiatury.

Klawiatura

Sposób, w jaki aplikacja reaguje na dane wejściowe z klawiatury, wpływa na wygodę użytkowników korzystających z dużego ekranu. Istnieją 3 rodzaje danych wejściowych z klawiatury: nawigacja, naciśnięcia klawiszy, i skróty.

Nawigacja za pomocą klawiatury jest rzadko implementowana w aplikacjach, w których główną rolę odgrywa dotyk, ale użytkownicy oczekują jej, gdy korzystają z aplikacji i mają ręce na klawiaturze. Nawigacja za pomocą klawiatury może być niezbędna na telefonach, tabletach, urządzeniach składanych i komputerach stacjonarnych dla użytkowników z potrzebami w zakresie ułatwień dostępu.

W przypadku wielu aplikacji nawigacja za pomocą strzałek i klawisza Tab jest obsługiwana automatycznie przez platformę Android. Na przykład niektóre elementy kompozycyjne są domyślnie możliwe do zaznaczenia, np. Button lub element kompozycyjny z clickable. Nawigacja za pomocą klawiatury powinna działać bez dodatkowego kodu. Aby włączyć nawigację za pomocą klawiatury w przypadku niestandardowych elementów kompozycyjnych, które nie są domyślnie możliwe do zaznaczenia, dodaj modyfikator focusable:

var color by remember { mutableStateOf(Green) }
Box(
    Modifier
        .background(color)
        .onFocusChanged { color = if (it.isFocused) Blue else Green }
        .focusable()
) {
    Text("Focusable 1")
}

Więcej informacji znajdziesz w artykule Umożliwianie zaznaczania elementu kompozycyjnego.

Gdy zaznaczenie jest włączone, platforma Android tworzy mapowanie nawigacyjne dla wszystkich komponentów, które można zaznaczyć, na podstawie ich pozycji. Zwykle działa to zgodnie z oczekiwaniami i nie wymaga dalszego rozwoju.

Compose nie zawsze jednak określa prawidłowy następny element do nawigacji za pomocą klawisza Tab w przypadku złożonych elementów kompozycyjnych, takich jak karty i listy, np. gdy jeden z elementów kompozycyjnych jest przewijany w poziomie i nie jest w pełni widoczny.

Aby kontrolować zachowanie zaznaczenia, dodaj modyfikator focusGroup do nadrzędnego elementu kompozycyjnego kolekcji elementów kompozycyjnych. Zaznaczenie przechodzi do grupy, a następnie przez grupę, zanim przejdzie do następnego komponentu, który można zaznaczyć, np.:

Row {
    Column(Modifier.focusGroup()) {
        Button({}) { Text("Row1 Col1") }
        Button({}) { Text("Row2 Col1") }
        Button({}) { Text("Row3 Col1") }
    }
    Column(Modifier.focusGroup()) {
        Button({}) { Text("Row1 Col2") }
        Button({}) { Text("Row2 Col2") }
        Button({}) { Text("Row3 Col2") }
    }
}

Więcej informacji znajdziesz w artykule Zapewnianie spójnej nawigacji za pomocą grup zaznaczenia.

Przetestuj dostęp do każdego elementu interfejsu aplikacji za pomocą samej klawiatury. Często używane elementy powinny być dostępne bez użycia myszy lub dotyku.

Pamiętaj, że obsługa klawiatury może być niezbędna dla użytkowników z potrzebami w zakresie ułatwień dostępu.

Naciśnięcia klawiszy

W przypadku wprowadzania tekstu, które byłoby obsługiwane przez ekranową klawiaturę wirtualną (IME), takich jak a TextField , aplikacje powinny działać zgodnie z oczekiwaniami na urządzeniach z dużym ekranem bez dodatkowych prac programistycznych. W przypadku naciśnięć klawiszy, których platforma nie może przewidzieć, aplikacje muszą samodzielnie obsługiwać zachowanie. Dotyczy to zwłaszcza aplikacji z widokami niestandardowymi.

Przykłady to aplikacje do czatowania, które używają klawisza Enter do wysyłania wiadomości, aplikacje multimedialne, które rozpoczynają i zatrzymują odtwarzanie za pomocą spacji, oraz gry które sterują ruchem za pomocą klawiszy w, a, s i d.

Poszczególne naciśnięcia klawiszy możesz obsługiwać za pomocą modyfikatora onKeyEvent, który akceptuje lambdę wywoływaną, gdy zmodyfikowany komponent otrzyma zdarzenie klawisza. Właściwość KeyEvent#type umożliwia określenie, czy zdarzenie jest naciśnięciem klawisza (KeyDown) czy jego zwolnieniem (KeyUp):

Box(
    modifier = Modifier.focusable().onKeyEvent {
        if(
            it.type == KeyEventType.KeyUp &&
            it.key == Key.S
        ) {
            doSomething()
            true
        } else {
            false
        }
    }
)  {
    Text("Press S key")
}

Możesz też zastąpić wywołanie zwrotne onKeyUp() i dodać oczekiwane zachowanie dla każdego otrzymanego kodu klawisza:

kotlin override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean { return when (keyCode) { KeyEvent.KEYCODE_ENTER -> { sendChatMessage() true } KeyEvent.KEYCODE_SPACE -> { playOrPauseMedia() true } else -> super.onKeyUp(keyCode, event) } }

Zdarzenie onKeyUp występuje, gdy klawisz zostanie zwolniony. Użycie wywołania zwrotnego zapobiega konieczności przetwarzania przez aplikacje wielu onKeyDown zdarzeń, jeśli klawisz jest przytrzymywany lub zwalniany powoli. Gry i aplikacje, które muszą wykrywać moment naciśnięcia klawisza lub to, czy użytkownik przytrzymuje klawisz, mogą nasłuchiwać zdarzenia onKeyDown i samodzielnie obsługiwać powtarzające się zdarzenia onKeyDown.

Więcej informacji znajdziesz w artykule Obsługa działań wykonywanych za pomocą klawiatury.

Skróty

Podczas korzystania z klawiatury sprzętowej oczekuje się typowych skrótów klawiszowych, które obejmują klawisze Ctrl, Alt, Shift i Meta. Jeśli aplikacja nie implementuje skrótów, użytkownicy mogą być sfrustrowani. Zaawansowani użytkownicy doceniają też skróty do często używanych zadań specyficznych dla aplikacji. Skróty ułatwiają korzystanie z aplikacji i odróżniają ją od aplikacji, które nie mają skrótów.

Do typowych skrótów należą Ctrl+S (zapisz), Ctrl+Z (cofnij) i Ctrl+Shift+Z (przywróć). Listę domyślnych skrótów znajdziesz w artykule Obsługa działań wykonywanych za pomocą klawiatury.

Obiekt KeyEvent ma te atrybuty, które wskazują, czy klawisze modyfikujące są naciśnięte:

Przykład:

Box(
    Modifier.onKeyEvent {
        if (it.isAltPressed && it.key == Key.A) {
            println("Alt + A is pressed")
            true
        } else {
            false
        }
    }
    .focusable()
)

Więcej informacji znajdziesz w tych artykułach:

Rysik

Wiele urządzeń z dużym ekranem jest wyposażonych w rysik. Aplikacje na Androida obsługują rysiki jako dane wejściowe z ekranu dotykowego. Niektóre urządzenia mogą też mieć tablet graficzny USB lub Bluetooth, np. Wacom Intuos. Aplikacje na Androida mogą odbierać dane wejściowe przez Bluetooth, ale nie przez USB.

Aby uzyskać dostęp do obiektów MotionEvent rysika, dodaj modyfikator pointerInteropFilter do powierzchni rysowania. Zaimplementuj klasę ViewModel z metodą, która przetwarza zdarzenia ruchu. Przekaż metodę jako lambdę onTouchEvent modyfikatora pointerInteropFilter:

@Composable
@OptIn(ExperimentalComposeUiApi::class)
fun DrawArea(modifier: Modifier = Modifier) {
   Canvas(modifier = modifier
       .clipToBounds()
       .pointerInteropFilter {
           viewModel.processMotionEvent(it)
       }

   ) {
       // Drawing code here.
   }
}

Obiekt MotionEvent zawiera informacje o zdarzeniu:

Punkty historyczne

Android grupuje zdarzenia wejściowe i dostarcza je raz na klatkę. Rysik może zgłaszać zdarzenia z dużo większą częstotliwością niż wyświetlacz. Podczas tworzenia aplikacji do rysowania sprawdzaj zdarzenia, które mogły wystąpić w niedalekiej przeszłości, za pomocą interfejsów API getHistorical:

Odrzucanie dotyku dłonią

Gdy użytkownicy rysują, piszą lub wchodzą w interakcje z Twoją aplikacją za pomocą rysika, czasami dotykają ekranu dłonią. Zdarzenie dotknięcia (ustawione na ACTION_DOWN lub ACTION_POINTER_DOWN) może zostać zgłoszone do Twojej aplikacji zanim system rozpozna i zignoruje przypadkowy dotyk dłonią.

Android anuluje zdarzenia dotyku dłonią, wysyłając MotionEvent. Jeśli Twoja aplikacja otrzyma ACTION_CANCEL, anuluj gest. Jeśli Twoja aplikacja otrzyma ACTION_POINTER_UP, sprawdź, czy ustawiona jest flaga FLAG_CANCELED. Jeśli tak, anuluj gest.

Nie sprawdzaj tylko FLAG_CANCELED. W Androidzie 13 (API na poziomie 33) i nowszym system ustawia FLAG_CANCELED dla zdarzeń ACTION_CANCEL, ale w starszych wersjach Androida system nie ustawia tej flagi.

Android 12

W Androidzie 12 (API na poziomie 32) i starszym wykrywanie odrzucania dotyku dłonią jest możliwe tylko w przypadku zdarzeń dotyku jednym palcem. Jeśli dotyk dłonią jest jedynym wskaźnikiem, system anuluje zdarzenie, ustawiając ACTION_CANCEL w obiekcie zdarzenia ruchu. Jeśli inne wskaźniki są wciśnięte, system ustawia ACTION_POINTER_UP, co jest niewystarczające do wykrycia odrzucania dotyku dłonią.

Android 13

W Androidzie 13 (API na poziomie 33) i nowszym, jeśli dotyk dłonią jest jedynym wskaźnikiem, system anuluje zdarzenie, ustawiając ACTION_CANCEL i FLAG_CANCELED w obiekcie zdarzenia ruchu. Jeśli inne wskaźniki są wciśnięte, system ustawia ACTION_POINTER_UP i FLAG_CANCELED.

Za każdym razem, gdy Twoja aplikacja otrzyma zdarzenie ruchu z ACTION_POINTER_UP, sprawdź, czy ustawiona jest flaga FLAG_CANCELED, aby określić, czy zdarzenie wskazuje na odrzucenie dotyku dłonią (lub inne anulowanie zdarzenia).

Aplikacje do robienia notatek

ChromeOS ma specjalny intent, który wyświetla użytkownikom zarejestrowane aplikacje do robienia notatek. Aby zarejestrować aplikację jako aplikację do robienia notatek, dodaj do manifestu aplikacji te informacje:

<intent-filter>
    <action android:name="org.chromium.arc.intent.action.CREATE_NOTE" />
    <category android:name="android.intent.category.DEFAULT" />
</intent-filter>

Gdy aplikacja jest zarejestrowana w systemie, użytkownik może wybrać ją jako domyślną aplikację do robienia notatek. Gdy zostanie poproszony o utworzenie nowej notatki, aplikacja powinna utworzyć pustą notatkę gotową do wprowadzania danych za pomocą rysika. Gdy użytkownik chce dodać adnotacje do obrazu (np. zrzutu ekranu lub pobranego obrazu), aplikacja uruchamia się z ClipData zawierającym co najmniej 1 element z identyfikatorami URI content://. Aplikacja powinna utworzyć notatkę, która używa pierwszego załączonego obrazu jako obrazu tła, i przejść do trybu, w którym użytkownik może rysować na ekranie za pomocą rysika.

Testowanie intentów do robienia notatek bez rysika

Aby sprawdzić, czy aplikacja prawidłowo reaguje na intenty do robienia notatek bez aktywnego rysika, użyj tej metody, aby wyświetlić opcje robienia notatek w ChromeOS:

  1. Przejdź do trybu deweloperskiego i włącz zapisywanie na urządzeniu.
  2. Naciśnij Ctrl+Alt+F2, aby otworzyć terminal.
  3. Uruchom polecenie sudo vi /etc/chrome_dev.conf.
  4. Naciśnij i, aby edytować, i dodaj --ash-enable-palette do nowego wiersza na końcu pliku.
  5. Zapisz, naciskając Esc, a następnie wpisując :, w, q i naciskając Enter
  6. Naciśnij Ctrl+Alt+F1, aby wrócić do zwykłego interfejsu ChromeOS.
  7. Wyloguj się, a następnie zaloguj się ponownie.

Na półce powinna teraz znajdować się ikona rysika:

  • Kliknij przycisk rysika na półce i wybierz Nowa notatka. Powinna się otworzyć pusta notatka do rysowania.
  • Zrób zrzut ekranu. Na półce kliknij przycisk rysika > Zrzut ekranu lub pobierz obraz. W powiadomieniu powinna być opcja Dodaj adnotacje do obrazu. Powinno to spowodować uruchomienie aplikacji z obrazem gotowym do dodania adnotacji.

Obsługa myszy i touchpada

Większość aplikacji musi zwykle obsługiwać tylko 3 zdarzenia związane z dużym ekranem: kliknięcie prawym przyciskiem myszy, najechanie i przeciąganie i upuszczanie.

Kliknięcie prawym przyciskiem

Wszystkie działania, które powodują wyświetlenie przez aplikację menu kontekstowego, np. dotknięcie i przytrzymanie elementu listy, powinny też reagować na zdarzenia kliknięcia prawym przyciskiem myszy.

Aby obsługiwać zdarzenia kliknięcia prawym przyciskiem myszy, aplikacje powinny zarejestrować View.OnContextClickListener:

Box(modifier = Modifier.fillMaxSize()) {
    AndroidView(
        modifier = Modifier.fillMaxSize(),
        factory = { context ->
            val rootView = FrameLayout(context)
            val onContextClickListener =
                View.OnContextClickListener { view ->
                    showContextMenu()
                    true
                }
            rootView.setOnContextClickListener(onContextClickListener)
            rootView
        },
    )
}

Szczegółowe informacje o tworzeniu menu kontekstowych znajdziesz w artykule Tworzenie menu kontekstowego.

Najechanie

Obsługując zdarzenia najechania, możesz sprawić, że układy aplikacji będą bardziej dopracowane i łatwiejsze w użyciu. Dotyczy to zwłaszcza komponentów niestandardowych :

Oto 2 najczęstsze przykłady:

  • Wskazywanie użytkownikom, czy element ma zachowanie interaktywne, np. czy można go kliknąć lub edytować, przez zmianę ikony wskaźnika myszy.
  • Dodawanie wizualnych informacji zwrotnych do elementów na dużej liście lub w siatce, gdy wskaźnik znajduje się nad nimi.

Przeciągnij i upuść

W środowisku wielookienkowym użytkownicy oczekują, że będą mogli przeciągać elementy między aplikacjami. Dotyczy to zarówno komputerów stacjonarnych, jak i tabletów, telefonów i urządzeń składanych w trybie podzielonego ekranu.

Zastanów się, czy użytkownicy będą przeciągać elementy do Twojej aplikacji. Na przykład edytory zdjęć powinny oczekiwać otrzymywania zdjęć, odtwarzacze audio – plików audio, a programy do rysowania – zdjęć.

Aby dodać obsługę przeciągania, przeczytaj artykuł Przeciąganie i upuszczanie oraz post na blogu Android on ChromeOS – Implementing Drag & Drop.

Szczególne uwagi dotyczące ChromeOS

Zaawansowana obsługa wskaźnika

Aplikacje, które zaawansowanie obsługują dane wejściowe z myszy i touchpada, powinny implementować modyfikator pointerInput, aby uzyskać PointerEvent:

@Composable
private fun LogPointerEvents(filter: PointerEventType? = null) {
    var log by remember { mutableStateOf("") }
    Column {
        Text(log)
        Box(
            Modifier
                .size(100.dp)
                .background(Color.Red)
                .pointerInput(filter) {
                    awaitPointerEventScope {
                        while (true) {
                            val event = awaitPointerEvent()
                            // handle pointer event
                            if (filter == null || event.type == filter) {
                                log = "${event.type}, ${event.changes.first().position}"
                            }
                        }
                    }
                }
        )
    }
}

Sprawdź obiekt PointerEvent, aby określić te informacje:

Kontrolery do gier

Niektóre urządzenia z Androidem i dużym ekranem obsługują do 4 kontrolerów do gier. Do obsługi kontrolerów do gier używaj standardowych interfejsów API kontrolerów do gier na Androida (patrz Obsługa kontrolerów do gier).

Przyciski kontrolera do gier są mapowane na typowe wartości zgodnie z typowym mapowaniem. Nie wszyscy producenci kontrolerów do gier stosują jednak te same konwencje mapowania. Możesz znacznie poprawić wygodę użytkowników, jeśli pozwolisz im wybierać różne popularne mapowania kontrolerów. Więcej informacji znajdziesz w artykule Przetwarzanie naciśnięć przycisków gamepada.

Tryb tłumaczenia danych wejściowych

ChromeOS domyślnie włącza tryb tłumaczenia danych wejściowych. W przypadku większości aplikacji na Androida ten tryb pomaga aplikacjom działać zgodnie z oczekiwaniami w środowisku komputerowym. Przykłady to automatyczne włączanie przewijania 2 palcami na touchpadzie, przewijania kółkiem myszy i mapowania surowych współrzędnych wyświetlacza na współrzędne okna. Zwykle deweloperzy aplikacji nie muszą samodzielnie implementować żadnego z tych zachowań.

Jeśli aplikacja implementuje niestandardowe zachowanie danych wejściowych, np. definiuje niestandardowe działanie uszczypnięcia 2 palcami na touchpadzie, lub te tłumaczenia danych wejściowych nie zapewniają zdarzeń wejściowych oczekiwanych przez aplikację, możesz wyłączyć tryb tłumaczenia danych wejściowych, dodając ten tag do manifestu Androida:

<uses-feature
    android:name="android.hardware.type.pc"
    android:required="false" />

Dodatkowe materiały