Zaawansowane funkcje rysika

Android i ChromeOS udostępniają różne interfejsy API, które ułatwiają tworzenie aplikacji zapewniających użytkownikom wyjątkowe korzystanie z rysika. Klasa MotionEvent udostępnia informacje o interakcjach rysika z ekranem, w tym o naciskaniu rysika, orientacji, przechylaniu, najeżdżaniu kursorem i wykrywaniu dłoni. Biblioteki prognozowania ruchu i grafiki z małym opóźnieniem ulepszają renderowanie rysika na ekranie, zapewniając naturalne wrażenie, jak w papierze.

MotionEvent

Klasa MotionEvent reprezentuje interakcje użytkowników, takie jak pozycja i ruch wskaźników dotykowych na ekranie. Podczas wpisywania danych rysikiem MotionEvent pokazuje też dane dotyczące nacisku, orientacji, nachylenia i najechania kursorem.

Dane zdarzenia

Aby uzyskać dostęp do danych MotionEvent, skonfiguruj onTouchListener:

Kotlin

val onTouchListener = View.OnTouchListener { view, event ->
  // Process motion event.
}

Java

View.OnTouchListener listener = (view, event) -> {
  // Process motion event.
};

Detektor odbiera obiekty MotionEvent z systemu, aby aplikacja mogła je przetworzyć.

Obiekt MotionEvent dostarcza dane związane z tymi aspektami zdarzenia w interfejsie:

  • Działania: fizyczna interakcja z urządzeniem – dotykanie ekranu, przesuwanie wskaźnika po powierzchni ekranu, najeżdżanie kursorem na powierzchnię ekranu
  • Wskaźniki: identyfikatory obiektów wchodzących w interakcję z ekranem – palca, stylu, myszki
  • Oś: typ danych – współrzędne X i Y, ciśnienie, nachylenie, orientacja i odległość (odległość).

Działania

Aby zaimplementować obsługę rysika, musisz wiedzieć, jakie działanie wykonuje użytkownik.

Funkcja MotionEvent udostępnia wiele różnych stałych ACTION, które definiują zdarzenia ruchu. 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 na przykład rozpoczynanie nowego pociągnięcia, gdy wystąpi ACTION_DOWN, rysowanie kreski znakiem ACTION_MOVE, i jej zakończenie po uruchomieniu ACTION_UP.

Zbiór działań MotionEvent od ACTION_DOWN do ACTION_UP dla danego wskaźnika nazywany jest zestawem akcji.

Wskaźniki

Większość ekranów jest wielodotykowych: system przypisuje wskaźnik do każdego palca, rysika, myszy lub innego obiektu wskazującego, który styka się z ekranem. Indeks wskaźnika pozwala uzyskać informacje o osi dla konkretnego wskaźnika, na przykład położenia pierwszego palca dotykającego ekranu 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ść pierwszego wskaźnika, czyli wskaźnika 0 (0).

Obiekty MotionEvent zawierają informacje o typie używanego wskaźnika. Aby uzyskać typ wskaźnika, wykonaj iteracje za pomocą indeksów wskaźników i wywołaj metodę getToolType(pointerIndex).

Więcej informacji o wskaźnikach znajdziesz w artykule Obsługa gestów wielodotykowych.

Dane wejściowe rysika

Dane wejściowe rysika możesz filtrować za pomocą TOOL_TYPE_STYLUS:

Kotlin

val isStylus = TOOL_TYPE_STYLUS == event.getToolType(pointerIndex)

Java

boolean isStylus = TOOL_TYPE_STYLUS == event.getToolType(pointerIndex);

Rysik może też zgłosić, że jest używany jako gumka w TOOL_TYPE_ERASER:

Kotlin

val isEraser = TOOL_TYPE_ERASER == event.getToolType(pointerIndex)

Java

boolean isEraser = TOOL_TYPE_ERASER == event.getToolType(pointerIndex);

Dane osi rysika

ACTION_DOWN i ACTION_MOVE udostępniają dane osi dotyczące rysika, takie jak współrzędne X i Y, nacisk, orientacja, nachylenie i najechanie kursorem.

Aby umożliwić dostęp do tych danych, interfejs MotionEvent API udostępnia zmienną getAxisValue(int), w której 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 pierwszego wskaźnika.

Zobacz też Obsługa gestów wielodotykowych.

Pozycja

Współrzędne x i y wskaźnika można pobrać przy użyciu tych wywołań:

Rysunek rysikiem na ekranie ze zmapowanymi współrzędnymi x i Y.
Rysunek 1. Współrzędne ekranu X i Y wskaźnika rysika.

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 mieści się w zakresie od 0 (brak nacisku) do 1, ale wyższe wartości mogą być zwracane w zależności od kalibracji ekranu.

Styl rysika symbolizujący kontynuację niskiego do wysokiego ciśnienia. Styl wąski i płytki po lewej stronie wskazuje na niskie ciśnienie. Styl staje się szerszy i ciemniejszy od lewej do prawej, aż jest najszerszy, a najciemniejszy po prawej stronie, wskazując najwyższe ciśnienie.
Rysunek 2. Reprezentacja ciśnienia – niskie ciśnienie po lewej, wysokie ciśnienie po prawej.

Orientacja

Orientacja wskazuje, w którym kierunku wskazuje rysik.

Orientację wskaźnika można pobrać za pomocą getAxisValue(AXIS_ORIENTATION) lub getOrientation() (w przypadku pierwszego wskaźnika).

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. Jeśli na przykład stylem jest płaski pędzel, jego szerokość zależy od orientacji stylu.

Rysunek 3. Rysik wskazujący w lewo około minus 0,57 radianów.

Przechylenie

Pochylenie mierzy pochylenie rysika względem ekranu.

Funkcja przechylania zwraca dodatni kąt rysika w radianach, gdzie 0 jest prostopadłe do ekranu, a liczba π/2 jest płaska na powierzchni.

Kąt nachylenia można określić za pomocą funkcji getAxisValue(AXIS_TILT) (nie można użyć skrótu do pierwszego wskaźnika).

Za pomocą przechylenia można uzyskać jak najbardziej zbliżone do rzeczywistych narzędzia, np. imitację cieniowania przechylonym ołówkiem.

Rysik został nachylony około 40 stopni od powierzchni ekranu.
Rysunek 4. Rysik został przechylony pod kątem około 0, 785 radianów lub o 45 stopni od prostopadłej.

Najechanie

Odległość rysika od ekranu można sprawdzić za pomocą funkcji getAxisValue(AXIS_DISTANCE). Gdy rysik oddala się od ekranu, metoda zwraca wartość z 0,0 (kontakt z ekranem) do wyższych wartości. Odległość między ekranem a stalnikiem (punktem) rysika zależy od producenta zarówno ekranu, jak i rysika. Implementacje mogą się różnić, dlatego w przypadku funkcji o znaczeniu krytycznym dla aplikacji nie należy polegać na precyzyjnych wartościach.

Najechanie rysikiem pozwala wyświetlić podgląd rozmiaru pędzla lub wskazać, że zostanie wybrany przycisk.

Rysunek 5. Rysik najeżdżający na ekran. Aplikacja reaguje, mimo że rysik nie dotyka powierzchni ekranu.

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ą niepożądane dotknięcia, np. 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 anulowaniu ostatniego zestawu MotionEvent.

Dlatego musisz przechowywać historię działań wprowadzonych przez użytkownika, aby umożliwić usunięcie z ekranu niepożądanych dotknięć i ponowne wyrenderowanie prawidłowych danych wejściowych użytkownika.

ACTION_CANCEL i FLAG_CANCELED

Zarówno ACTION_CANCEL, jak i FLAG_CANCELED informują, że poprzedni zestaw MotionEvent powinien zostać anulowany od ostatniego ACTION_DOWN. Dzięki temu można na przykład cofnąć ostatnie pociągnięcie aplikacji do rysowania w przypadku danego wskaźnika.

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 zdarzenia 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 wyświetlający się wskaźnik w górę było wynikiem niezamierzonego dotknięcia użytkownika. Flaga jest zwykle ustawiana, gdy użytkownik przypadkowo dotknie ekranu, np. przez chwytanie urządzenia lub położenie dłoni na ekranie.

Dostęp do wartości flagi możesz uzyskać w ten sposób:

Kotlin

val cancel = (event.flags and FLAG_CANCELED) == FLAG_CANCELED

Java

boolean cancel = (event.getFlags() & FLAG_CANCELED) == FLAG_CANCELED;

Jeśli flaga jest ustawiona, musisz cofnąć ostatnie ustawienie MotionEvent od ostatniego ACTION_DOWN od tego wskaźnika.

Podobnie jak ACTION_CANCEL, wskaźnik można znaleźć w getPointerId(actionIndex).

Rysunek 6. Styl rysika i dotyk dłoni tworzą zestawy MotionEvent. Dotyk dłoni jest anulowany, a wyświetlacz jest ponownie renderowany.

Pełnoekranowy, obraz od krawędzi do krawędzi i gesty nawigacji

Jeśli aplikacja działa na pełnym ekranie i w pobliżu krawędzi znajdują się interaktywne elementy, takie jak płótno służące do rysowania lub aplikacji do sporządzania notatek, przesunięcie palcem z dołu ekranu w celu wyświetlenia nawigacji lub przeniesienie aplikacji na tło może spowodować niezamierzone kliknięcie obszaru roboczego.

Rysunek 7. Gest przesuwania, aby przenieść aplikację w tle.

Aby nie dopuszczać do niechcianych dotknięć w aplikacji gestami, możesz skorzystać z zestawów i ACTION_CANCEL.

Zapoznaj się też z sekcją Odrzucanie, nawigacja i niechciane dane wejściowe.

Użyj metody setSystemBarsBehavior() i BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE z WindowInsetsController, aby zapobiegać wywoływaniu niepożądanych zdarzeń dotyku za pomocą gestów nawigacyjnych:

Kotlin

// Configure the behavior of the hidden system bars.
windowInsetsController.systemBarsBehavior =
    WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE

Java

// Configure the behavior of the hidden system bars.
windowInsetsController.setSystemBarsBehavior(
    WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
);

Więcej informacji o zarządzaniu wstawianiem i gestami znajdziesz tutaj:

Małe opóźnienie

Czas oczekiwania to czas wymagany przez sprzęt, system i aplikację na przetworzenie i wyrenderowanie danych wejściowych użytkownika.

Czas oczekiwania = przetwarzanie danych wejściowych przez sprzęt i system operacyjny + przetwarzanie aplikacji + komponowanie systemu

  • renderowanie sprzętowe
Z powodu opóźnienia renderowane pociągnięcie jest opóźnione w stosunku do pozycji rysika. Luka między wyrenderowanym kreską a położeniem rysika reprezentuje opóźnienie.
Rysunek 8. Z powodu opóźnienia renderowane pociągnięcie jest opóźnione w stosunku do pozycji rysika.

Źródło czasu oczekiwania

  • Rejestrowanie rysika na ekranie dotykowym (sprzęt): wstępne połączenie bezprzewodowe, gdy rysik i system operacyjny komunikowają się w celu zarejestrowania i zsynchronizowania.
  • Częstotliwość próbkowania ekranu dotykowego (sprzęt): liczba przypadków, gdy ekran dotykowy na sekundę sprawdza, czy wskaźnik dotyka powierzchni. Wartość mieści się w zakresie od 60 do 1000 Hz.
  • Przetwarzanie danych wejściowych (aplikacja): stosowanie kolorów, efektów graficznych i przekształcenia na danych wejściowych użytkownika.
  • Renderowanie graficzne (system operacyjny i sprzęt): wymiana buforów, przetwarzanie sprzętowe.

Obrazy z małym opóźnieniem

Biblioteka grafiki Jetpack z krótkimi opóźnieniami skraca czas przetwarzania między danymi wprowadzonymi przez użytkownika a renderowaniem na ekranie.

Biblioteka skraca czas przetwarzania, ponieważ eliminuje renderowanie na wielu buforach i wykorzystuje technikę renderowania bufora przedniego, co oznacza pisanie bezpośrednio na ekranie.

Renderowanie bufora przedniego

Przedni bufor to pamięć używana przez ekran do renderowania. To aplikacja, która najlepiej znajduje się na ekranie. Biblioteka o małym opóźnieniu umożliwia renderowanie aplikacji bezpośrednio w przednim buforze. Zwiększa to wydajność, zapobiegając zamienianiu buforów, które ma miejsce w przypadku standardowego renderowania w kilku buforach lub w przypadku podwójnego bufora (najczęstsza sytuacja).

Aplikacja zapisuje w buforze ekranu i odczytuje z bufora ekranu.
Rysunek 9. Renderowanie bufora przedniego.
Aplikacja zapisuje do wielu buforów, które są zastępowane buforem ekranu. Aplikacja odczytuje dane z bufora ekranu.
Rys. 10. Renderowanie z wieloma buforami.

Renderowanie przedniego bufora to świetna technika do renderowania małego obszaru ekranu, ale nie służy do odświeżania całego ekranu. Dzięki renderowaniu przedniego bufora aplikacja renderuje treści w buforze, z którego odczytuje wyświetlacz. W związku z tym istnieje możliwość renderowania artefaktów lub podrażania (patrz poniżej).

Biblioteka niskiego opóźnienia jest dostępna na urządzeniach z Androidem 10 (poziom interfejsu API 29) i nowszym oraz na urządzeniach z ChromeOS z Androidem 10 (poziom interfejsu API 29) i nowszym.

Zależności

Biblioteka o małym opóźnieniu dostarcza komponenty implementacji renderowania frontendu w trybie bufora. Biblioteka jest dodawana jako zależność w pliku build.gradle modułu aplikacji:

dependencies {
    implementation "androidx.graphics:graphics-core:1.0.0-alpha03"
}

Wywołania zwrotne GLFrontBufferRenderer

Biblioteka z krótkim czasem oczekiwania zawiera interfejs GLFrontBufferRenderer.Callback, który definiuje te metody:

W bibliotece małego opóźnienia nie ma znaczenia, jakiego typu danych używasz w usłudze GLFrontBufferRenderer.

Biblioteka przetwarza jednak dane w postaci strumienia setek punktów danych, projektując je w taki sposób, aby zoptymalizować wykorzystanie i alokację pamięci.

Wywołania zwrotne

Aby włączyć renderowanie wywołań zwrotnych, zaimplementuj funkcję GLFrontBufferedRenderer.Callback i zastąp onDrawFrontBufferedLayer() oraz onDrawDoubleBufferedLayer(). Funkcja GLFrontBufferedRenderer używa wywołań zwrotnych, aby renderować dane w możliwie najbardziej zoptymalizowany sposób.

Kotlin

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.
   }
}

Java

GLFrontBufferedRenderer.Callback<DATA_TYPE> callbacks =
    new GLFrontBufferedRenderer.Callback<DATA_TYPE>() {
        @Override
        public void onDrawFrontBufferedLayer(@NonNull EGLManager eglManager,
            @NonNull BufferInfo bufferInfo,
            @NonNull float[] transform,
            DATA_TYPE data_type) {
                // OpenGL for front buffer, short, affecting small area of the screen.
        }

    @Override
    public void onDrawDoubleBufferedLayer(@NonNull EGLManager eglManager,
        @NonNull BufferInfo bufferInfo,
        @NonNull float[] transform,
        @NonNull Collection<? extends DATA_TYPE> collection) {
            // OpenGL full scene rendering.
    }
};
Zadeklarowanie wystąpienia komponentu GLFrontBufferedRenderer

Przygotuj GLFrontBufferedRenderer, podając utworzone wcześniej wywołania SurfaceView i wywołania zwrotne. GLFrontBufferedRenderer optymalizuje renderowanie na przedniej stronie i podwójne buforowanie za pomocą wywołań zwrotnych:

Kotlin

var glFrontBufferRenderer = GLFrontBufferedRenderer<DATA_TYPE>(surfaceView, callbacks)

Java

GLFrontBufferedRenderer<DATA_TYPE> glFrontBufferRenderer =
    new GLFrontBufferedRenderer<DATA_TYPE>(surfaceView, callbacks);
Renderowanie

Renderowanie bufora frontendu rozpoczyna się po wywołaniu metody renderFrontBufferedLayer(), która aktywuje wywołanie zwrotne onDrawFrontBufferedLayer().

Renderowanie z podwójnym buforem jest wznawiane po wywołaniu funkcji commit(), która wywołuje wywołanie zwrotne onDrawMultiDoubleBufferedLayer().

W poniższym przykładzie proces renderuje się w przednim buforze (szybkie renderowanie), gdy użytkownik zaczyna rysować na ekranie (ACTION_DOWN) i przesuwa wskaźnik (ACTION_MOVE). Proces jest renderowany w podwójnym buforze, gdy wskaźnik opuści powierzchnię ekranu (ACTION_UP).

Możesz użyć parametru requestUnbufferedDispatch(), aby zapytać, czy system wejściowy nie grupuje zdarzeń ruchu, ale zamiast tego dostarcza je, gdy tylko są dostępne:

Kotlin

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

Java

switch (motionEvent.getAction()) {
   case MotionEvent.ACTION_DOWN: {
       // Deliver input events as soon as they arrive.
       surfaceView.requestUnbufferedDispatch(motionEvent);

       // Pointer is in contact with the screen.
       glFrontBufferRenderer.renderFrontBufferedLayer(DATA_TYPE);
   }
   break;
   case MotionEvent.ACTION_MOVE: {
       // Pointer is moving.
       glFrontBufferRenderer.renderFrontBufferedLayer(DATA_TYPE);
   }
   break;
   case MotionEvent.ACTION_UP: {
       // Pointer is not in contact in the screen.
       glFrontBufferRenderer.commit();
   }
   break;
   case MotionEvent.ACTION_CANCEL: {
       // Cancel front buffer; remove last motion set from the screen.
       glFrontBufferRenderer.cancel();
   }
   break;
}

Co robić, a czego unikać w przypadku renderowania

Tak

Małe fragmenty ekranu, pismo odręczne, rysunek, szkicowanie.

Nie wolno

Aktualizacja na pełnym ekranie, przesuwanie, powiększanie. Może dojść do rozerwania.

Rozdarcie

Zerwanie ma miejsce, gdy ekran odświeża się, gdy jednocześnie zmieniasz bufor ekranu. Na innej części ekranu widać nowe dane, a inna – stare.

Górne i dolne części obrazu Androida są niewłaściwie wyrównane z powodu rozerwania się podczas odświeżania ekranu.
Rysunek 11. Zerwanie podczas odświeżania ekranu od góry do dołu

Przewidywanie ruchu

Biblioteka prognozowania ruchu Jetpacka zmniejsza widoczne opóźnienie, szacując ścieżkę użytkownika i przekazując mechanizmowi renderowania tymczasowe, sztuczne punkty.

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ą używane przez prognozy ruchu do prognozowania przyszłych obiektów MotionEvent.

Prognozowane obiekty MotionEvent są tylko szacunkami. Prognozowane zdarzenia mogą zmniejszyć postrzegany czas oczekiwania, ale dane prognozowane muszą zostać zastąpione rzeczywistymi danymi MotionEvent po ich otrzymaniu.

Biblioteka prognozowania ruchu jest dostępna od Androida 4.4 (poziom interfejsu API 19) i nowszych oraz na urządzeniach z ChromeOS z Androidem 9 (poziom interfejsu API 28) lub nowszym.

Z powodu opóźnienia renderowane pociągnięcie jest opóźnione w stosunku do pozycji rysika. Luka między kreską a rysikiem jest wypełniona punktami prognozy. Pozostała przerwa to spodziewany czas oczekiwania.
Rysunek 12. Czas oczekiwania został zmniejszony przez przewidywanie ruchu.

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 zawiera interfejs MotionEventPredictor, który definiuje te metody:

  • record(): przechowuje obiekty MotionEvent jako zapis działań użytkownika
  • predict(): Zwraca przewidywaną wartość MotionEvent
Zadeklaruj wystąpienie instancji MotionEventPredictor

Kotlin

var motionEventPredictor = MotionEventPredictor.newInstance(view)

Java

MotionEventPredictor motionEventPredictor = MotionEventPredictor.newInstance(surfaceView);
Dodaj dane do prognozowania

Kotlin

motionEventPredictor.record(motionEvent)

Java

motionEventPredictor.record(motionEvent);
Prognoza

Kotlin

when (motionEvent.action) {
   MotionEvent.ACTION_MOVE -> {
       val predictedMotionEvent = motionEventPredictor?.predict()
       if(predictedMotionEvent != null) {
            // use predicted MotionEvent to inject a new artificial point
       }
   }
}

Java

switch (motionEvent.getAction()) {
   case MotionEvent.ACTION_MOVE: {
       MotionEvent predictedMotionEvent = motionEventPredictor.predict();
       if(predictedMotionEvent != null) {
           // use predicted MotionEvent to inject a new artificial point
       }
   }
   break;
}

Zalecenia i ograniczenia związane z prognozowaniem ruchu

Tak

Usuń punkty prognozy po dodaniu nowego przewidywanego punktu.

Nie wolno

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, zapoznaj się z sekcją Zgodność urządzeń wejściowych.

Aby zarejestrować aplikację do robienia notatek na Androidzie, przeczytaj artykuł Tworzenie aplikacji do notatek.

W Androidzie 14 (poziom interfejsu API 34) wprowadziliśmy intencję ACTION_CREATE_NOTE, która umożliwia aplikacji uruchamianie notatek na ekranie blokady.

Rozpoznawanie cyfrowego atramentu przy użyciu ML Kit

Dzięki funkcjom rozpoznawania cyfrowego atramentu ML Kit aplikacja rozpoznaje odręczny tekst na platformie cyfrowej w setkach języków. Możesz też klasyfikować szkice.

ML Kit udostępnia klasę Ink.Stroke.Builder do tworzenia obiektów Ink, które modele systemów uczących się mogą przetworzyć w celu konwertowania pisma odręcznego na tekst.

Oprócz rozpoznawania pisma odręcznego model rozpoznaje gesty, takie jak usuwanie czy zakreślanie.

Więcej informacji znajdziesz w sekcji Rozpoznawanie cyfrowego atramentu.

Dodatkowe materiały

Przewodniki dla programistów

Ćwiczenia z programowania