Android i ChromeOS oferują różne interfejsy API, które pomagają tworzyć aplikacje zapewniające
użytkownicy nie tylko korzystają z rysika.
MotionEvent
klasa ujawnia
informacje o interakcji rysika z ekranem, w tym o docisku rysika,
orientacji, przechylenia, po najechaniu kursorem i wykrywaniu dłoni. Grafika i ruch z małym opóźnieniem
biblioteki prognoz ulepszają renderowanie rysika na ekranie,
w naturalny sposób przypominający papier i pióro.
MotionEvent
Klasa MotionEvent
reprezentuje interakcje wejściowe użytkownika, np. pozycję
oraz ruch wskaźników dotykowych na ekranie. Do wprowadzania tekstu za pomocą rysika: MotionEvent
ujawniają również dane dotyczące nacisku, orientacji, nachylenia i danych po najechaniu kursorem.
Dane zdarzenia
To access MotionEvent
data, add a pointerInput
modifier to components:
@Composable
fun Greeting() {
Text(
text = "Hello, Android!", textAlign = TextAlign.Center, style = TextStyle(fontSize = 5.em),
modifier = Modifier
.pointerInput(Unit) {
awaitEachGesture {
while (true) {
val event = awaitPointerEvent()
event.changes.forEach { println(it) }
}
}
},
)
}
Obiekt MotionEvent
dostarcza dane związane z tymi aspektami interfejsu użytkownika
zdarzenie:
- Działania: fizyczna interakcja z urządzeniem – dotykanie ekranu. przesuwając wskaźnik po powierzchni ekranu, najeżdżając wskaźnikiem na ekran platforma
- Wskaźniki: identyfikatory obiektów wchodzących w interakcję z ekranem – palca rysik, mysz
- Oś: typ danych – współrzędne X i Y, ciśnienie, nachylenie, orientacja, i najedź (odległość)
Działania
Aby zaimplementować obsługę rysika, musisz wiedzieć, co robi użytkownik skuteczności.
Funkcja MotionEvent
udostępnia różne stałe ACTION
, które definiują ruch
zdarzeń. Najważniejsze działania związane z rysikiem to:
Działanie | Opis |
---|---|
ACTION_DOWN ACTION_POINTER_DOWN |
Wskaźnik skontaktował się z ekranem. |
PRZENIESIENIE | Wskaźnik porusza się po ekranie. |
ACTION_UP ACTION_POINTER_UP |
Wskaźnik nie kontaktuje się już z ekranem. |
ACTION_CANCEL | Kiedy ma zostać anulowany poprzedni lub bieżący ustawiony ruch. |
Aplikacja może wykonywać takie zadania jak uruchamianie nowego stylu, gdy ACTION_DOWN
Narysuję kreskę kreską ACTION_MOVE,
, a kończę
Aktywowano ACTION_UP
.
Zbiór działań MotionEvent
od ACTION_DOWN
do ACTION_UP
dla danego elementu
jest nazywany zestawem ruchu.
Wskaźniki
Większość ekranów obsługuje wielodotyk: system przypisuje wskaźnik do każdego palca, rysik, mysz lub inny obiekt wskazujący, który wchodzi w interakcję z ekranem. Wskaźnik umożliwia uzyskanie informacji o osi dla określonego wskaźnika, np. położenie pierwszego palca na ekranie lub drugiego.
Indeksy wskaźników mają zakres od 0 do liczby wskaźników zwracanych przez
MotionEvent#pointerCount()
minus 1.
Dostęp do wartości osi wskaźników można uzyskać za pomocą metody getAxisValue(axis,
pointerIndex)
.
Jeśli indeks wskaźnika zostanie pominięty, system zwraca wartość dla pierwszej wartości
wskaźnik, wskaźnik 0 (0).
Obiekty MotionEvent
zawierają informacje o typie używanego wskaźnika. Ty
może pobrać typ wskaźnika przez iterację za pomocą indeksów wskaźników i wywołanie
getToolType(pointerIndex)
.
Więcej informacji o wskaźnikach znajdziesz w sekcji Obsługa wielodotyku gestów.
Dane wejściowe rysika
Możesz filtrować dane wejściowe rysikiem za pomocą:
TOOL_TYPE_STYLUS
:
val isStylus = TOOL_TYPE_STYLUS == event.getToolType(pointerIndex)
Rysik może też zgłosić, że jest używany jako gumka,
TOOL_TYPE_ERASER
:
val isEraser = TOOL_TYPE_ERASER == event.getToolType(pointerIndex)
Dane osi rysika
ACTION_DOWN
i ACTION_MOVE
udostępniają dane osi dotyczące rysika, czyli x i
współrzędne Y, ciśnienie, orientacja, pochylenie i uniesienie wskaźnika.
Aby zapewnić dostęp do tych danych, interfejs API MotionEvent
udostępnia
getAxisValue(int)
,
gdzie parametrem jest dowolny z tych identyfikatorów osi:
Axis | Zwracana wartość getAxisValue() |
---|---|
AXIS_X |
Współrzędna X zdarzenia ruchu. |
AXIS_Y |
Współrzędna Y zdarzenia ruchu. |
AXIS_PRESSURE |
W przypadku ekranu dotykowego lub touchpada – nacisk wywierany przez palec, rysik lub inny wskaźnik. W przypadku myszy lub kulki: 1 – gdy jest naciśnięty przycisk główny, 0 – jeśli nie jest naciśnięty. |
AXIS_ORIENTATION |
w przypadku ekranu dotykowego lub touchpada – orientacja palca, rysika lub innego wskaźnika względem pionowej płaszczyzny urządzenia. |
AXIS_TILT |
Kąt nachylenia rysika w radianach. |
AXIS_DISTANCE |
Odległość rysika od ekranu. |
Na przykład funkcja MotionEvent.getAxisValue(AXIS_X)
zwraca współrzędną X dla argumentu
za pierwszym razem.
Zobacz też Obsługa wielodotyku gestów.
Pozycja
Współrzędne x i y wskaźnika można pobrać przy użyciu tych wywołań:
MotionEvent#getAxisValue(AXIS_X)
lubMotionEvent#getX()
MotionEvent#getAxisValue(AXIS_Y)
lubMotionEvent#getY()
Ciśnienie
Ciśnienie wskaźnika można pobrać za pomocą
MotionEvent#getAxisValue(AXIS_PRESSURE)
lub, w przypadku pierwszego wskaźnika,
MotionEvent#getPressure()
Wartość nacisku w przypadku ekranów dotykowych lub touchpadów wynosi od 0 (nie ciśnienie) i 1, ale w zależności od ekranu mogą być zwracane wyższe wartości. kalibracji.
.Orientacja
Orientacja wskazuje, w którym kierunku wskazuje rysik.
Orientację wskaźnika można pobrać za pomocą getAxisValue(AXIS_ORIENTATION)
lub
getOrientation()
.
(za pierwszy wskaźnik).
W przypadku rysika orientacja jest zwracana jako wartość radiana z zakresu od 0 do pi (π) w prawo lub od 0 do -pi w lewo.
Orientacja umożliwia wdrożenie rzeczywistego pędzla. Na przykład, jeśli plik rysik reprezentuje płaski pędzel, jego szerokość zależy od położenie rysika.
Przechylenie
Pochylenie mierzy pochylenie rysika względem ekranu.
Przechylenie zwraca kąt dodatni rysika w radianach, gdzie 0 to prostopadła do ekranu, a π/2 leży płasko na powierzchni.
Kąt nachylenia można określić za pomocą funkcji getAxisValue(AXIS_TILT)
(nie ma skrótu do
pierwszy wskaźnik).
Za pomocą przechylenia można uzyskać jak najbliższe rzeczywiste narzędzia, takie jak: imitując cieniowanie przechylonego ołówka.
.Najechanie
Odległość rysika od ekranu można określić za pomocą
getAxisValue(AXIS_DISTANCE)
Metoda zwraca wartość od 0,0 (kontakt z
ekranu) na wyższe wartości, w miarę jak rysik oddala się od ekranu. Po najechaniu kursorem
odległość między ekranem a stalką (punktem) rysika zależy od
od producenta ekranu i rysika. Ponieważ implementacje mogą
różnią się od siebie, w przypadku funkcji o znaczeniu krytycznym dla aplikacji nie używaj precyzyjnych wartości.
Najechanie rysikiem pozwala wyświetlić podgląd rozmiaru pędzla lub wskazać, że przycisk wyboru.
Uwaga: w narzędziu Compose dostępne są modyfikatory, które wpływają na interaktywny stan elementów interfejsu:
hoverable
: skonfiguruj komponent tak, aby można było najechać na niego kursorem, korzystając ze zdarzeń wejścia i wyjścia wskaźnika.indication
: po wystąpieniu interakcji generuje efekty wizualne dla tego komponentu.
Odrzucanie dłoni, nawigacja i niechciane dane wejściowe
Czasami ekrany wielodotykowe rejestrują niechciane dotknięcia, na przykład gdy
użytkownik w naturalny sposób opiera dłoń na ekranie, aby uzyskać wsparcie podczas pisania odręcznego.
Odrzucenie przez Palmę to mechanizm, który wykrywa takie zachowanie i powiadamia Cię o
ostatni zbiór elementów typu MotionEvent
powinien zostać anulowany.
Dlatego musisz przechowywać historię danych wejściowych użytkowników, aby uniknąć niepożądanych dotknięć można usunąć z ekranu, a dane wejściowe użytkownika mogą być wyrenderowano ponownie.
ACTION_CANCEL i FLAG_CANCELED
ACTION_CANCEL
i
FLAG_CANCELED
jest
informują, że poprzedni zestaw MotionEvent
powinien zostać
anulowano od ostatniego ACTION_DOWN
, więc możesz na przykład cofnąć ostatnie
kreska dla danego wskaźnika w aplikacji do rysowania.
ACTION_CANCEL
Dodane w Androidzie 1.0 (poziom API 1)
ACTION_CANCEL
wskazuje, że poprzedni zestaw zdarzeń ruchu powinien zostać anulowany.
Funkcja ACTION_CANCEL
jest wyzwalana, gdy zostanie wykryty dowolny z tych elementów:
- Gesty do nawigacji
- Odrzucenie palm
Po wywołaniu funkcji ACTION_CANCEL
aktywny wskaźnik należy wskazać za pomocą
getPointerId(getActionIndex())
Następnie usuń kreskę utworzoną przy użyciu tego wskaźnika z historii wprowadzania i ponownie wyrenderuj scenę.
FLAG_CANCELED (ANULOWANO)
Dodano w Androidzie 13 (poziom API 33)
FLAG_CANCELED
wskazuje, że wzrost wskaźnika jest wynikiem niezamierzonego dotknięcia użytkownika. Flaga to
zwykle ustawia się, gdy użytkownik przypadkowo dotyka ekranu, np. przez
lub położyć dłoń na ekranie.
Dostęp do wartości flagi możesz uzyskać w ten sposób:
val cancel = (event.flags and FLAG_CANCELED) == FLAG_CANCELED
Jeśli flaga jest ustawiona, trzeba cofnąć ostatnie ustawienie MotionEvent
, od ostatniego
ACTION_DOWN
od tego wskaźnika.
Podobnie jak ACTION_CANCEL
, wskaźnik można znaleźć w getPointerId(actionIndex)
.
Pełnoekranowy, obraz od krawędzi do krawędzi i gesty nawigacji
Jeśli aplikacja działa na pełnym ekranie i znajduje się obok niej interaktywne elementy, takie jak obszaru roboczego aplikacji do rysowania lub robienia notatek. Przesuń palcem od dołu ekranu, wyświetlanie nawigacji lub przeniesienie aplikacji w tle może spowodować niechciany dotyk obszaru roboczego.
Aby gesty nie powodowały niepożądanych dotknięć w aplikacji, możesz:
zaletą zestawów i
ACTION_CANCEL
Zobacz też artykuł Odrzucanie, nawigacja i niechciane dane wejściowe w Palm. .
Użyj
setSystemBarsBehavior()
BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
z
WindowInsetsController
aby zapobiec niechcianym dotykom za pomocą gestów nawigacyjnych:
// Configure the behavior of the hidden system bars.
windowInsetsController.systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
Więcej informacji o zarządzaniu wstawianiem i gestami znajdziesz tutaj:
- Ukrywanie pasków systemowych w trybie pojemnym
- Zapewnianie zgodności dzięki nawigacji przy użyciu gestów
- Wyświetlaj treści w aplikacji od krawędzi do krawędzi
Małe opóźnienie
Czas oczekiwania to czas potrzebny na przetworzenie przez sprzęt, system i aplikację i renderować dane wejściowe użytkownika.
Czas oczekiwania = przetwarzanie sprzętowe i wejściowe systemu operacyjnego + przetwarzanie aplikacji + komponowanie systemu
- renderowanie sprzętowe
Źródło czasu oczekiwania
- Rejestrowanie rysika na ekranie dotykowym (sprzęt): wstępne połączenie bezprzewodowe gdy rysik i system operacyjny mają komunikować się, aby zarejestrować i zsynchronizować.
- Częstotliwość próbkowania ekranu dotykowego (sprzęt): liczba wyświetleń ekranu dotykowego na sekundę. sprawdza, czy wskaźnik dotyka powierzchni. Częstotliwość odświeżania w zakresie od 60 do 1000 Hz.
- Przetwarzanie danych wejściowych (aplikacja): stosowanie kolorów, efektów graficznych i przekształcenia na dane wejściowe użytkownika.
- Renderowanie graficzne (system operacyjny i sprzęt): wymiana buforów, przetwarzanie sprzętowe.
Obrazy z małym opóźnieniem
Biblioteka grafiki z małym opóźnieniem w Jetpack skraca czas przetwarzania danych między danymi wpisywanymi przez użytkownika a renderowaniem na ekranie.
Biblioteka skraca czas przetwarzania dzięki unikaniu renderowania wielobuforowego i wykorzystując technikę renderowania „frontend bufora”, co oznacza pisanie bezpośrednio na ekranie.
Renderowanie bufora przedniego
Przedni bufor to pamięć używana przez ekran do renderowania. Jest najbliższy aplikacje mogą korzystać z rysunku bezpośrednio na ekranie. Biblioteka niskiego opóźnienia umożliwia do renderowania bezpośrednio w przednim buforze. Poprawia to wydajność o zapobiega zamienianiu buforów, co może mieć miejsce w przypadku standardowego renderowania z wieloma buforami. lub renderowanie z podwójnym buforem (najczęstszym przypadkiem).
.Chociaż renderowanie przedniego bufora to doskonała technika renderowania małego obszaru ekranu, nie służy do odświeżania całego ekranu. Na renderowanie bufora przedniego, aplikacja renderuje treść w buforze, z którego co czyta wyświetlacz. W rezultacie istnieje możliwość renderowania i zerwania (patrz poniżej).
Biblioteka o krótkim czasie oczekiwania jest dostępna na Androidzie 10 (poziom interfejsu API 29) i nowszych oraz na urządzeniach z ChromeOS z Androidem 10 (poziom interfejsu API 29) lub nowszym.
Zależności
Biblioteka o małych opóźnieniach zapewnia komponenty renderowania frontendu
implementacji. Biblioteka jest dodawana jako zależność w module aplikacji.
Plik build.gradle
:
dependencies {
implementation "androidx.graphics:graphics-core:1.0.0-alpha03"
}
Wywołania zwrotne GLFrontBufferRenderer
Biblioteka niskiego opóźnienia zawiera
GLFrontBufferRenderer.Callback
który definiuje następujące metody:
W bibliotece niskiego opóźnienia nie ma znaczenia, jakiego typu dane używasz
GLFrontBufferRenderer
Biblioteka przetwarza jednak dane jako strumień setek punktów danych, a potem projektować dane tak, aby zoptymalizować wykorzystanie i alokację pamięci.
Wywołania zwrotne
Aby włączyć renderowanie wywołań zwrotnych, zaimplementuj GLFrontBufferedRenderer.Callback
i
zastąp wartości onDrawFrontBufferedLayer()
i onDrawDoubleBufferedLayer()
.
GLFrontBufferedRenderer
używa wywołań zwrotnych, aby maksymalnie renderować dane
w sposób optymalizowany.
val callback = object: GLFrontBufferedRenderer.Callback<DATA_TYPE> {
override fun onDrawFrontBufferedLayer(
eglManager: EGLManager,
bufferInfo: BufferInfo,
transform: FloatArray,
param: DATA_TYPE
) {
// OpenGL for front buffer, short, affecting small area of the screen.
}
override fun onDrawMultiDoubleBufferedLayer(
eglManager: EGLManager,
bufferInfo: BufferInfo,
transform: FloatArray,
params: Collection<DATA_TYPE>
) {
// OpenGL full scene rendering.
}
}
Zadeklarowanie wystąpienia komponentu GLFrontBufferedRenderer
Przygotuj dokumenty: GLFrontBufferedRenderer
, podając SurfaceView
i
utworzonych wcześniej wywołań zwrotnych. GLFrontBufferedRenderer
optymalizuje renderowanie
na przód i do podwójnego buforowania przy użyciu wywołań zwrotnych:
var glFrontBufferRenderer = GLFrontBufferedRenderer<DATA_TYPE>(surfaceView, callbacks)
Renderowanie
Renderowanie frontendu bufora rozpoczyna się, gdy wywołasz funkcję
renderFrontBufferedLayer()
, która wywołuje wywołanie zwrotne onDrawFrontBufferedLayer()
.
Renderowanie z podwójnym buforowaniem jest wznawiane, gdy wywołasz funkcję
commit()
, która wywołuje wywołanie zwrotne onDrawMultiDoubleBufferedLayer()
.
W przykładzie poniżej proces jest renderowany w przednim buforze (szybko
renderowanie), gdy użytkownik zacznie rysować na ekranie (ACTION_DOWN
) i się porusza.
wskaźnik dookoła (ACTION_MOVE
). Proces renderuje się w podwójnym buforze
gdy wskaźnik opuści powierzchnię ekranu (ACTION_UP
).
Za pomocą
requestUnbufferedDispatch()
, by system wejściowy nie grupował zdarzeń ruchu, lecz zamiast tego
gdy tylko będą dostępne:
when (motionEvent.action) {
MotionEvent.ACTION_DOWN -> {
// Deliver input events as soon as they arrive.
view.requestUnbufferedDispatch(motionEvent)
// Pointer is in contact with the screen.
glFrontBufferRenderer.renderFrontBufferedLayer(DATA_TYPE)
}
MotionEvent.ACTION_MOVE -> {
// Pointer is moving.
glFrontBufferRenderer.renderFrontBufferedLayer(DATA_TYPE)
}
MotionEvent.ACTION_UP -> {
// Pointer is not in contact in the screen.
glFrontBufferRenderer.commit()
}
MotionEvent.CANCEL -> {
// Cancel front buffer; remove last motion set from the screen.
glFrontBufferRenderer.cancel()
}
}
Co robić, a czego unikać w przypadku renderowania
Małe fragmenty ekranu, pismo odręczne, rysunek, szkicowanie.
Aktualizacja na pełnym ekranie, przesuwanie, powiększanie. Może dojść do rozerwania.
Rozdarcie
Zerwanie ma miejsce, gdy ekran odświeża się podczas buforowania ekranu zmodyfikowane w tym samym czasie. Na jednej części ekranu widać nowe dane, a na innej pokazuje stare dane.
Przewidywanie ruchu
Jetpack: prognozowanie ruchu biblioteka zmniejsza przewidywane opóźnienie dzięki oszacowaniu ścieżki ruchowej użytkownika i tymczasowym, sztucznie wskazuje mechanizm renderowania.
Biblioteka prognozowania ruchu pobiera dane wejściowe użytkownika jako obiekty MotionEvent
.
Obiekty zawierają informacje o współrzędnych x i y, ciśnieniu i czasie,
które są wykorzystywane przez prognozy ruchu do prognozowania przyszłych MotionEvent
obiektów.
Prognozowane obiekty MotionEvent
są tylko szacunkami. Prognozowane zdarzenia mogą zmniejszyć
widoczny czas oczekiwania, ale dane prognozowane muszą zostać zastąpione rzeczywistymi danymi MotionEvent
danych po ich otrzymaniu.
Biblioteka prognozowania ruchu jest dostępna na Androidzie 4.4 (poziom interfejsu API 19) oraz oraz na urządzeniach z ChromeOS z Androidem 9 (poziom interfejsu API 28) lub nowszym.
Zależności
Implementację prognozowania umożliwia biblioteka prognozowania ruchu.
biblioteka jest dodawana jako zależność w pliku build.gradle
modułu aplikacji:
dependencies {
implementation "androidx.input:input-motionprediction:1.0.0-beta01"
}
Implementacja
Biblioteka prognozowania ruchu obejmuje
MotionEventPredictor
który definiuje następujące metody:
record()
: Przechowuje obiektyMotionEvent
w rejestrze działań użytkownikapredict()
: Zwraca przewidywaną wartośćMotionEvent
Zadeklaruj wystąpienie instancji MotionEventPredictor
var motionEventPredictor = MotionEventPredictor.newInstance(view)
Dodaj dane do prognozowania
motionEventPredictor.record(motionEvent)
Prognoza
when (motionEvent.action) {
MotionEvent.ACTION_MOVE -> {
val predictedMotionEvent = motionEventPredictor?.predict()
if(predictedMotionEvent != null) {
// use predicted MotionEvent to inject a new artificial point
}
}
}
Zalecenia i ograniczenia związane z prognozowaniem ruchu
Usuń punkty prognozy po dodaniu nowego przewidywanego punktu.
Nie używaj punktów prognozy do ostatecznego renderowania.
Notatki
ChromeOS umożliwia aplikacji deklarowanie niektórych działań związanych z robieniem notatek.
Aby zarejestrować aplikację jako aplikację do robienia notatek w ChromeOS, zobacz Wprowadzanie danych .
Aby zarejestrować aplikację jako aplikację do robienia notatek na Androidzie, przeczytaj artykuł Tworzenie notatek.
w Androidzie 14 (poziom API 34) wprowadziliśmy
ACTION_CREATE_NOTE
intencję, która umożliwia aplikacji rozpoczęcie aktywności związanej z robieniem notatek
ekranu.
Rozpoznawanie cyfrowego atramentu przy użyciu ML Kit
Z użyciem atramentu cyfrowego ML Kit rozpoznawanie twarzy, jest w stanie rozpoznać odręczny tekst na cyfrowej przestrzeni w setkach języki. Możesz też klasyfikować szkice.
ML Kit zapewnia
Ink.Stroke.Builder
klasa do tworzenia obiektów Ink
, które mogą być przetwarzane przez modele systemów uczących się
, by zamienić pismo odręczne na tekst.
Oprócz rozpoznawania pisma odręcznego model rozpoznaje też gestów, na przykład usuń i zakreśl.
Zobacz Atrament cyfrowy rozpoznawanie aby dowiedzieć się więcej.