Udostępnij za pośrednictwem


Śledzenie palcem wielodotykowym

W tym temacie pokazano, jak śledzić zdarzenia dotykowe z wielu palców

Czasami aplikacja wielodotykowa musi śledzić poszczególne palce, gdy poruszają się jednocześnie na ekranie. Jedną z typowych aplikacji jest program malowania palcami. Chcesz, aby użytkownik mógł rysować za pomocą pojedynczego palca, ale także rysować z wieloma palcami jednocześnie. Ponieważ program przetwarza wiele zdarzeń dotykowych, musi odróżnić zdarzenia odpowiadające każdemu palcem. W tym celu system Android dostarcza kod identyfikatora, ale uzyskanie i obsługa tego kodu może być nieco trudne.

Dla wszystkich zdarzeń skojarzonych z określonym palcem kod identyfikatora pozostaje taki sam. Kod identyfikatora jest przypisywany, gdy palec po raz pierwszy dotyka ekranu i staje się nieprawidłowy po naciśnięciu palca z ekranu. Te kody identyfikatorów są zazwyczaj bardzo małymi liczbami całkowitymi, a system Android ponownie używa ich w przypadku późniejszych zdarzeń dotykowych.

Prawie zawsze, program, który śledzi poszczególne palce utrzymuje słownik do śledzenia dotyku. Klucz słownika to kod identyfikatora, który identyfikuje konkretny palec. Wartość słownika zależy od aplikacji. W przykładzie FingerPaint każdy pociągnięcie palca (od dotyku do zwolnienia) jest skojarzone z obiektem zawierającym wszystkie informacje niezbędne do renderowania linii narysowanej palcem. Program definiuje małą FingerPaintPolyline klasę w tym celu:

class FingerPaintPolyline
{
    public FingerPaintPolyline()
    {
        Path = new Path();
    }

    public Color Color { set; get; }

    public float StrokeWidth { set; get; }

    public Path Path { private set; get; }
}

Każda wielolinia ma kolor, szerokość pociągnięcia i obiekt graficzny systemu Path Android do gromadzenia i renderowania wielu punktów linii podczas rysowania.

Pozostała część kodu pokazanego poniżej znajduje się w pochodnej View nazwie FingerPaintCanvasView. Ta klasa utrzymuje słownik obiektów typu FingerPaintPolyline w czasie, gdy są aktywnie rysowane przez co najmniej jeden palec:

Dictionary<int, FingerPaintPolyline> inProgressPolylines = new Dictionary<int, FingerPaintPolyline>();

Ten słownik umożliwia szybkie uzyskanie FingerPaintPolyline informacji skojarzonych z konkretnym palcem.

Klasa FingerPaintCanvasView obsługuje List również obiekt dla linii polilinii, które zostały ukończone:

List<FingerPaintPolyline> completedPolylines = new List<FingerPaintPolyline>();

Obiekty w tej List kolejności znajdują się w tej samej kolejności, w której zostały narysowane.

FingerPaintCanvasView zastępuje dwie metody zdefiniowane przez View: OnDraw i OnTouchEvent. W jego OnDraw przesłonięć widok rysuje ukończone linie wieloliniowe, a następnie rysuje wielolinie w toku.

Zastąpienie OnTouchEvent metody rozpoczyna się od uzyskania pointerIndex wartości z ActionIndex właściwości . Ta ActionIndex wartość rozróżnia wiele palców, ale nie jest spójna w wielu zdarzeniach. Z tego powodu użyj polecenia , pointerIndex aby uzyskać wartość wskaźnika id z GetPointerId metody . Ten identyfikator jest spójny w wielu zdarzeniach:

public override bool OnTouchEvent(MotionEvent args)
{
    // Get the pointer index
    int pointerIndex = args.ActionIndex;

    // Get the id to identify a finger over the course of its progress
    int id = args.GetPointerId(pointerIndex);

    // Use ActionMasked here rather than Action to reduce the number of possibilities
    switch (args.ActionMasked)
    {
        // ...
    }

    // Invalidate to update the view
    Invalidate();

    // Request continued touch input
    return true;
}

Zwróć uwagę, że przesłonięcia używają ActionMasked właściwości w instrukcji switch , a nie Action właściwości . Poniżej przedstawiono przyczyny:

Gdy masz do czynienia z wieloma dotykami, Action właściwość ma wartość MotionEventsAction.Down dla pierwszego palca, aby dotknąć ekranu, a następnie wartości Pointer2Down i Pointer3Down jako drugie i trzecie palce również dotknąć ekranu. Jako czwarte i piąte palce nawiązać kontakt, Action właściwość ma wartości liczbowe, które nawet nie odpowiadają członkom MotionEventsAction wyliczenia! Należy zbadać wartości flag bitowych w wartościach, aby zinterpretować, co oznaczają.

Podobnie, jak palce pozostawiają kontakt z ekranem, Action właściwość ma wartości Pointer2Up i Pointer3Up dla drugiego i trzeciego palca, a Up dla pierwszego palca.

Właściwość ActionMasked przyjmuje mniejszą liczbę wartości, ponieważ ma być używana w połączeniu z właściwością ActionIndex , aby odróżnić wiele palców. Gdy palce dotykają ekranu, właściwość może być równa MotionEventActions.Down tylko dla pierwszego palca i PointerDown kolejnych palców. Gdy palce opuszczają ekran, ActionMasked ma wartości Pointer1Up dla kolejnych palców i Up pierwszego palca.

W przypadku używania ActionMaskedparametru ActionIndex wyróżnia się między kolejnymi palcami, aby dotknąć i opuścić ekran, ale zwykle nie trzeba używać tej wartości, z wyjątkiem argumentu dla innych metod w MotionEvent obiekcie. W przypadku wielodotyku jedna z najważniejszych z tych metod jest GetPointerId wywoływana w powyższym kodzie. Ta metoda zwraca wartość, której można użyć dla klucza słownika, aby skojarzyć określone zdarzenia z palcami.

Przesłonięcia OnTouchEvent w przykładzie przetwarza zdarzenia MotionEventActions.Down i PointerDown identycznie, tworząc nowy FingerPaintPolyline obiekt i dodając go do słownika:

public override bool OnTouchEvent(MotionEvent args)
{
    // Get the pointer index
    int pointerIndex = args.ActionIndex;

    // Get the id to identify a finger over the course of its progress
    int id = args.GetPointerId(pointerIndex);

    // Use ActionMasked here rather than Action to reduce the number of possibilities
    switch (args.ActionMasked)
    {
        case MotionEventActions.Down:
        case MotionEventActions.PointerDown:

            // Create a Polyline, set the initial point, and store it
            FingerPaintPolyline polyline = new FingerPaintPolyline
            {
                Color = StrokeColor,
                StrokeWidth = StrokeWidth
            };

            polyline.Path.MoveTo(args.GetX(pointerIndex),
                                 args.GetY(pointerIndex));

            inProgressPolylines.Add(id, polyline);
            break;
        // ...
    }
    // ...        
}

Zwróć uwagę, że pointerIndex element jest również używany do uzyskania położenia palca w widoku. Wszystkie informacje dotykowe są skojarzone z wartością pointerIndex . Element id jednoznacznie identyfikuje palce w wielu komunikatach, dzięki czemu jest używany do tworzenia wpisu słownika.

OnTouchEvent Podobnie przesłonięcia obsługują również metodę MotionEventActions.Up i Pointer1Up identycznie, przenosząc ukończoną wielolinię do completedPolylines kolekcji, aby można je było narysować podczas OnDraw przesłonięcia. Kod usuwa id również wpis ze słownika:

public override bool OnTouchEvent(MotionEvent args)
{
    // ...
    switch (args.ActionMasked)
    {
        // ...
        case MotionEventActions.Up:
        case MotionEventActions.Pointer1Up:

            inProgressPolylines[id].Path.LineTo(args.GetX(pointerIndex),
                                                args.GetY(pointerIndex));

            // Transfer the in-progress polyline to a completed polyline
            completedPolylines.Add(inProgressPolylines[id]);
            inProgressPolylines.Remove(id);
            break;

        case MotionEventActions.Cancel:
            inProgressPolylines.Remove(id);
            break;
    }
    // ...        
}

Teraz na trudną część.

Między zdarzeniami w dół i w górę zazwyczaj istnieje wiele MotionEventActions.Move zdarzeń. Są one umieszczane w jednym wywołaniu metody OnTouchEventi muszą być obsługiwane inaczej niż zdarzenia Down i Up . Wartość pointerIndex uzyskana wcześniej z ActionIndex właściwości musi być ignorowana. Zamiast tego metoda musi uzyskać wiele pointerIndex wartości przez pętlę z zakresu od 0 do PointerCount właściwości, a następnie uzyskać dla id każdej z tych pointerIndex wartości:

public override bool OnTouchEvent(MotionEvent args)
{
    // ...
    switch (args.ActionMasked)
    {
        // ...
        case MotionEventActions.Move:

            // Multiple Move events are bundled, so handle them differently
            for (pointerIndex = 0; pointerIndex < args.PointerCount; pointerIndex++)
            {
                id = args.GetPointerId(pointerIndex);

                inProgressPolylines[id].Path.LineTo(args.GetX(pointerIndex),
                                                    args.GetY(pointerIndex));
            }
            break;
        // ...
    }
    // ...        
}

Ten typ przetwarzania umożliwia przykładowi śledzenie poszczególnych palców i rysowanie wyników na ekranie:

Przykładowy zrzut ekranu z przykładu FingerPaint

Wiesz już, jak można śledzić poszczególne palce na ekranie i rozróżniać je.