Tworzenie widoku niestandardowego jako interaktywnego

Wypróbuj sposób tworzenia wiadomości
Jetpack Compose to zalecany zestaw narzędzi UI na Androida. Dowiedz się, jak korzystać z układów w funkcji Utwórz

Rysowanie interfejsu użytkownika to tylko jeden z etapów tworzenia widoku niestandardowego. Musisz także wykonać te czynności: Spraw, aby widok odpowiadał na dane wejściowe użytkownika w sposób zbliżony rzeczywiste działania, które naśladujesz.

Spraw, aby obiekty w aplikacji działały tak samo jak prawdziwe. Na przykład: obrazy w aplikacji przestają istnieć i pojawiają się w innym miejscu. że nie robią tego w prawdziwym świecie. Zamiast tego przenieś zdjęcia z jednego miejsca do innego użytkownika.

Użytkownicy wyczuwają nawet subtelne zachowania i działania interfejsu i najlepiej reagują na naśladujące prawdziwy świat. Jeśli na przykład użytkownik przesuwa obiekt interfejsu, zapewnią na początku poczucie bezwładności, które opóźni ruch. Na koniec ruchu, daj im poczucie pędu, które przenosi obiekt poza flirtowanie.

Na tej stronie pokazujemy, jak za pomocą funkcji platformy Androida dodawać ich rzeczywiste zachowania do widoku niestandardowego.

Dodatkowe powiązane informacje znajdziesz w Zdarzenia wejściowe – omówienie oraz Animacja właściwości .

Uchwyć gesty wprowadzania

Podobnie jak wiele innych platform interfejsu, Android obsługuje model zdarzeń wejściowych. Użytkownik te działania zmieniają się w zdarzenia, które wywołują wywołania zwrotne. aby dostosować sposób, w jaki aplikacja odpowiada użytkownikowi. Najczęściej używane dane wejściowe zdarzenie w systemie Android to dotyk, który uruchamia onTouchEvent(android.view.MotionEvent) Zastąp tę metodę obsługi zdarzenia w ten sposób:

Kotlin

override fun onTouchEvent(event: MotionEvent): Boolean {
    return super.onTouchEvent(event)
}

Java

@Override
   public boolean onTouchEvent(MotionEvent event) {
    return super.onTouchEvent(event);
   }

Zdarzenia dotknięcia nie są szczególnie przydatne. Nowoczesne interfejsy dotykowe określić interakcje za pomocą gestów, takich jak klikanie, ciągnięcie, pchanie przesuwać i powiększać. Aby przekonwertować nieprzetworzone zdarzenia dotyku na gesty, Android zapewnia GestureDetector

Utwórz GestureDetector, przekazując instancję klasy który stosuje GestureDetector.OnGestureListener Jeśli chcesz przetworzyć tylko kilka gestów, możesz rozszerzyć GestureDetector.SimpleOnGestureListener zamiast implementować GestureDetector.OnGestureListener. za pomocą prostego interfejsu online. Na przykład ten kod tworzy klasę, która rozszerza GestureDetector.SimpleOnGestureListener i zastąpienia onDown(MotionEvent)

Kotlin

private val myListener =  object : GestureDetector.SimpleOnGestureListener() {
    override fun onDown(e: MotionEvent): Boolean {
        return true
    }
}

private val detector: GestureDetector = GestureDetector(context, myListener)

Java

class MyListener extends GestureDetector.SimpleOnGestureListener {
   @Override
   public boolean onDown(MotionEvent e) {
       return true;
   }
}
detector = new GestureDetector(getContext(), new MyListener());

Niezależnie od tego, czy korzystasz z usługi GestureDetector.SimpleOnGestureListener, zawsze stosuj onDown() która zwraca true. Jest to konieczne, ponieważ wszystkie gesty rozpocząć od komunikatu onDown(). Jeśli zwrócisz false od onDown(), jak GestureDetector.SimpleOnGestureListener, system zakłada, że chcesz zignorować resztę gestu. Inne metody GestureDetector.OnGestureListener nie są wywoływane. Tylko zwrot false od onDown(), jeśli chcesz zignorować całą gest.

Po zaimplementowaniu GestureDetector.OnGestureListener i utworzeniu wystąpienia GestureDetector, możesz użyć GestureDetector na potrzeby interpretowania zdarzeń dotknięcia otrzymywanych w onTouchEvent()

Kotlin

override fun onTouchEvent(event: MotionEvent): Boolean {
    return detector.onTouchEvent(event).let { result ->
        if (!result) {
            if (event.action == MotionEvent.ACTION_UP) {
                stopScrolling()
                true
            } else false
        } else true
    }
}

Java

@Override
public boolean onTouchEvent(MotionEvent event) {
   boolean result = detector.onTouchEvent(event);
   if (!result) {
       if (event.getAction() == MotionEvent.ACTION_UP) {
           stopScrolling();
           result = true;
       }
   }
   return result;
}

Gdy przekazujesz onTouchEvent() zdarzenie dotknięcia, które nie jest rejestrowane rozpoznać jako część gestu, zwraca false. Następnie możesz uruchomić za pomocą własnego kodu wykrywania gestów.

Utwórz ruch, który może być fizycznie

Gesty to skuteczny sposób sterowania urządzeniami z ekranem dotykowym, ale mogą są sprzeczne z intuicją i trudne do zapamiętania, chyba że generują fizycznie miarodajnych wyników.

Załóżmy na przykład, że chcesz zaimplementować gest przesuwania w poziomie, który ustawia element narysowany w widoku obracający się wokół osi pionowej. Ten gest ma sens, jeśli interfejs szybko porusza się w kierunku przesunięcia, a potem zwalnia, jakby użytkownik naciskał koło zamachowe i zaczyna kręcić się wokół własnej osi.

Dokumentacja animowanie przewijania gest zawiera szczegółowe objaśnienie, jak wdrożyć własny styl zachowanie użytkownika. Jednak symulowanie działania koła zamachowego nie jest takie proste. Dużo fizyki i matematyki są niezbędne, aby model wiatraka działał prawidłowo. Na szczęście Android udostępnia klasy pomocnicze symulujące to i inne zachowania. Scroller jest podstawą do obsługi gestów przesuwania przypominających koło zamachowe.

Aby rozpocząć romans, zadzwoń: fling() z prędkością początkową oraz minimalną i maksymalną x i y jego wartości. Jako wartości prędkości możesz użyć wartości obliczonej przez GestureDetector

Kotlin

fun onFling(e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float): Boolean {
    scroller.fling(
            currentX,
            currentY,
            (velocityX / SCALE).toInt(),
            (velocityY / SCALE).toInt(),
            minX,
            minY,
            maxX,
            maxY
    )
    postInvalidate()
    return true
}

Java

@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
   scroller.fling(currentX, currentY, velocityX / SCALE, velocityY / SCALE, minX, minY, maxX, maxY);
   postInvalidate();
    return true;
}

Wywołanie fling() powoduje skonfigurowanie modelu fizycznego dla rzutu. gest. Następnie zaktualizuj Scroller, wywołując Scroller.computeScrollOffset() w regularnych odstępach czasu. computeScrollOffset() aktualizuje wewnętrzny stan obiektu Scroller, odczytując bieżącą godzinę i za pomocą modelu fizycznego do obliczenia pozycji x i y w tej obecnie się znajdujesz. Zadzwoń do nas getCurrX() oraz getCurrY() aby je pobrać.

Większość widoków przekazuje x i y obiektu Scroller. bezpośrednio scrollTo() Ten przykład wygląda trochę inaczej: wykorzystuje bieżącą pozycję przewijania x – aby ustawić kąt obrotu widoku.

Kotlin

scroller.apply {
    if (!isFinished) {
        computeScrollOffset()
        setItemRotation(currX)
    }
}

Java

if (!scroller.isFinished()) {
    scroller.computeScrollOffset();
    setItemRotation(scroller.getCurrX());
}

Klasa Scroller oblicza za Ciebie pozycję przewijania, ale nie stosuje automatycznie tych pozycji w widoku. Zastosuj nowe współrzędne na tyle często, by przewijanie animacji wyglądało płynnie. Są 2 sposoby wykonaj następujące czynności:

  • Wymuszanie ponownego rysowania przez wywołanie postInvalidate() po rozmowie z: fling(). Ta technika wymaga oblicz odsunięcia przewijania w argumencie onDraw() i wywołuj funkcję postInvalidate() za każdym razem, gdy przesunięcie przewijania zmian.
  • Skonfiguruj ValueAnimator aby uruchomić animację na czas trwania przelotu i dodać detektor do procesu. aktualizacje animacji przez wywołanie addUpdateListener() Ta technika pozwala animować właściwości elementu View

Płynne przejścia

Użytkownicy oczekują, że nowoczesny interfejs będzie płynnie przechodzić między stanami: elementy interfejsu pojawianie się i znikanie, a nie pojawianie się i znikanie, a początek ruchów i kończy się płynnie, zamiast zaczynać i zatrzymywać. Android animacja właściwości ułatwia płynne przejścia.

Aby można było korzystać z systemu animacji, za każdym razem, gdy właściwość zmieni, co wpływa na wyglądu, nie zmieniaj bezpośrednio właściwości. Zamiast tego użyj ValueAnimator, aby wprowadzić zmianę. W poniższym przykładzie że modyfikacja wybranego komponentu podrzędnego w widoku spowoduje, że cały wyrenderowany widoku, obróć widok tak, aby wskaźnik wyboru był wyśrodkowany. ValueAnimator zmienia obrót w ciągu kilkuset milisekund, a nie od razu ustawić nową wartość rotacji.

Kotlin

autoCenterAnimator = ObjectAnimator.ofInt(this, "Rotation", 0).apply {
    setIntValues(targetAngle)
    duration = AUTOCENTER_ANIM_DURATION
    start()
}

Java

autoCenterAnimator = ObjectAnimator.ofInt(this, "Rotation", 0);
autoCenterAnimator.setIntValues(targetAngle);
autoCenterAnimator.setDuration(AUTOCENTER_ANIM_DURATION);
autoCenterAnimator.start();

Jeśli wartość, którą chcesz zmienić, jest jedną z podstawowych wartości View tworzenie animacji jest jeszcze łatwiejsze, ponieważ widoki mają wbudowany ViewPropertyAnimator zoptymalizowaną pod kątem jednoczesnej animacji wielu właściwości, na przykład następujący przykład:

Kotlin

animate()
    .rotation(targetAngle)
    .duration = ANIM_DURATION
    .start()

Java

animate().rotation(targetAngle).setDuration(ANIM_DURATION).start();