Condividi tramite


Il presente articolo è stato tradotto automaticamente.

Nuove frontiere per l'interfaccia utente

Movimenti di tocco in Windows Phone

Charles Petzold

Scaricare il codice di esempio

image: Charles PetzoldCome un utente dedica gran parte della sua vita professionale osservando l'evoluzione delle API, ho stato abbastanza divertimento dai piccolo angolo dell'universo API occupata da multi-touch.  Non si è sicuri anche potrebbe voler contare il numero di multi-touch diverse che API distribuite nel Windows Presentation Foundation (WPF), superficie Microsoft, Silverlight, XNA e telefonica di Windows, ma più evidente è che una teoria"unificata" di multi-touch è ancora più elusive.

Naturalmente, questo insieme di API tocco non dovrebbe essere sorprendente di una tecnologia che è ancora relativamente giovane.Inoltre, multitocco è più complesso il mouse.Che è parzialmente a causa della potenziale interazione di più dita, ma anche riflette la differenza tra un dispositivo puramente artificiale, ad esempio il mouse e le dita del tutto naturale.Abbiamo gli esseri umani hanno una durata dell'esperienza acquisita con le dita e prevediamo loro di interagire con il mondo in modi noti, anche se ci stiamo tocca la superficie lucida di uno schermo.

Per i programmatori di applicazioni Windows 7 telefono definisce quattro — Sì, quattro, ovvero touch diverse interfacce.

Le applicazioni Silverlight scritte per Windows 7 telefono hanno la possibilità di ottenere l'input tocco a basso livello tramite l'evento Touch.FrameReported statica o livello superiore dell'input tramite la manipolazione dei vari eventi indirizzati.Questi eventi di modifica sono principalmente un sottoinsieme di eventi simili in WPF, ma sono diversi per causare problemi principali.

Le applicazioni di XNA per Windows 7 telefono utilizzano la classe TouchPanel statica per ottenere l'input tocco, ma l'unica classe incorpora effettivamente due interfacce touch: GetState il metodo ottiene l'attività di basso livello finger e il metodo ReadGesture Ottiene i movimenti di livello superiore.I movimenti supportati dal metodo ReadGesture non sono i movimenti di simile a quello dello stilo, ad esempio segni di spunta e cerchi.È molto più semplici dei movimenti descritti da nomi quali Tap, trascinamento e alternativa.In linea con l'architettura di XNA input tocco viene eseguito il polling per l'applicazione non vengono recapitati tramite gli eventi.

Movimenti provengono da Silverlight

Presuppone ovviamente che Silverlight per Windows telefono 7 aveva già un numero sufficiente di API multitocco, in modo che sono stato sorpreso a vedere un terzo uno aggiunto alla combinazione, sebbene in un toolkit che proviene un po' troppo tardi per me descrivere nel mio libro, "Telefono di programmazione Windows 7" (Microsoft Press, 2010).

Come è noto, sono state aggiunte diverse versioni di WPF e Silverlight negli ultimi anni dal toolkit rilasciati tramite CodePlex.Questi strumenti consentono a Microsoft ottenere nuove classi per gli sviluppatori di fuori del ciclo di spedizione usuali e spesso consentono di noi una "Occhiatina" a miglioramenti per le strutture che possono essere incorporate in futuro versioni.Un ulteriore vantaggio è il codice sorgente completo.

7 Telefono Windows ora anche vantaggi questo personalizzato.Silverlight per telefono Toolkit per Windows (disponibile all'indirizzo silverlight.codeplex.com) contiene DatePicker, TimePicker e ToggleSwitch i controlli già familiari agli utenti di Windows 7 telefono;WrapPanel (utile per la gestione delle modifiche di orientamento del telefono);e il supporto di movimento multitocco.

Questo nuovo supporto del movimento di Silverlight nel toolkit è destinato a essere simile al metodo XNA TouchPanel.ReadGesture, con l'eccezione viene recapitato tramite gli eventi indirizzati invece di polling.

Quanto il processo è?Molto più così minore del previsto.Esaminare il codice sorgente, è stato sorpreso nello scoprire che questi nuovi eventi di movimento di Silverlight interamente derivati da una chiamata al metodo TouchPanel.ReadGesture XNA.Non sarebbe stato credevo che un'applicazione Silverlight in Windows telefono è stata consentita di chiamare questo metodo XNA, ma è presente.

Anche se i movimenti di Silverlight e XNA sono piuttosto simili tra loro, le proprietà associate con i movimenti non lo sono.Ad esempio, le proprietà XNA utilizzano vettori, e poiché Silverlight non include una struttura di vettore (un'omissione che mi sento è ridicolo), le proprietà dovevano essere ridefinita per Silverlight in determinati modi semplici.

Come ho lavorato con questi eventi di movimento, sono venuti dal mio preferito API multitocco per telefono di Silverlight per Windows.Ho trovato loro di essere anche abbastanza facile da usare e completa per la maggior parte delle quali è necessario eseguire.

Consenti all'utente di dimostrare assegnando questi movimenti effettivi operazioni da eseguire.

Servizio di movimento e listener

Tutto il codice sorgente per questa colonna è in una soluzione Visual Studio scaricabile denominata GestureDemos che contiene tre progetti.È necessario disporre di strumenti di sviluppo di Windows 7 telefono installati, naturalmente e anche Silverlight per telefono Toolkit per Windows.

Dopo l'installazione del toolkit, è possibile utilizzare tale nei propri progetti telefono Windows mediante l'aggiunta di un riferimento all'assembly Microsoft.Phone.Controls.Toolkit.Nella finestra di dialogo Aggiungi riferimento devono essere elencato nella sezione i.Scheda di rete.

In un file XAML, una dichiarazione di spazio dei nomi XML come questo, ma nella stessa riga, quindi sarà necessario:

xmlns:Toolkit = "clr-namespace:Microsoft.Phone.Controls; assembly=Microsoft.Phone.Controls.Toolkit"

Di seguito sono all'incirca gli eventi di movimento disponibile 12, in base all'ordine che tratterò (gli eventi che ho raggruppati in una singola riga sono correlati e si verificano in una sequenza):

GestureBegin, GestureCompleted Tap Hold DoubleTap DragStarted, DragDelta, gesto rapido DragCompleted PinchStarted, PinchDelta, PinchCompleted

Si supponga che si desidera gestire toccare e tenere premuto per gli eventi che si verificano su una griglia o qualsiasi elemento figlio della griglia. È possibile specificare che nel codice XAML file in questo modo:

< griglia...
> <toolkit:GestureService.GestureListener>< toolkit:GestureListener Tap = "ongesturelistenertap"Tenere = "ongesturelistenerhold"/ ></toolkit:GestureService.GestureListener>...
</Grid>

È possibile indicare gli eventi e gestori in un tag di GestureListener è un elemento figlio della proprietà della classe GestureService GestureListener collegato.

In alternativa, nel codice, è necessario disporre una direttiva spazio dei nomi per lo spazio dei nomi Microsoft.Phone.Controls e il codice riportato di seguito:

GestureListener gestureListener = GestureService.GetGestureListener(element);

gestureListener.Tap + = OnGestureListenerTap;gestureListener.Hold + = OnGestureListenerHold;

In entrambi i casi, se si imposta questo listener di movimento su un pannello, assicurarsi che la proprietà Background è impostata almeno su trasparente! Eventi passa semplicemente un pannello con il colore di sfondo null.

Toccare e tenere premuto

Tutti gli eventi di movimento sono accompagnati da un tipo che deriva da GestureEventArgs o argomenti di tipo GestureEventArgs. La proprietà OriginalSource indica l'elemento di primo piano richiamata dall'indice che soddisfi sullo schermo.il metodo GetPosition fornisce le coordinate correnti di tale finger rispetto a qualsiasi elemento.

Gli eventi di movimento vengono instradati, ovvero può risale la struttura ad albero visuale e gestione per qualsiasi elemento che dispone di un GestureListener installato. Come di consueto, un gestore eventi può impostare la proprietà Handled di GestureEventArgs su true per impedire a un evento di viaggio up ulteriormente la struttura ad albero visuale. Tuttavia, ciò riguarda solo gli altri elementi che utilizzano questi eventi di movimento. Impostando Handled su true impedisce gli elementi di livello superiori nella struttura visiva di ottenere l'input tocco tramite altre interfacce.

L'evento GestureBegin indica che un dito ha toccato una schermata in precedenza a mezze dita;GestureCompleted segnali quando tutte le dita hanno abbandonato la schermata. Questi eventi possono essere utili per l'inizializzazione o di pulizia, ma verrà in genere più concentrato su eventi di movimento che si verificano tra questi due eventi.

Non desidero spendere molto tempo i movimenti più semplici. Un tocco si verifica quando un dito tocca lo schermo e quindi si alza all'interno di circa 1. 1 secondi, senza spostare troppo distante dalla posizione originale. In caso di chiusura in successione due tocchi, secondo arriva come un DoubleTap. Un'esenzione si verifica quando viene premuto sullo schermo di un dito e rimane in circa del campione stesso per circa 1. 1 secondi. Alla fine di questa fase viene generato l'evento di blocco senza attendere il dito sollevare.

Trascinare e spostare l?

Una sequenza di trascinamento, costituito da un evento DragStarted, zero o più eventi DragDelta e un evento DragCompleted, si verifica quando un dito tocca lo schermo, viene spostata e gli ascensori. Poiché esso non è noto che trascinando si verifica quando un dito prima tocca lo schermo, l'evento DragStarted viene ritardata fino a quando il dito ha effettivamente inizio lo spostamento di là della soglia di Tap. L'evento DragStarted può essere preceduto da un evento di blocco se non è stato il dito sullo schermo senza spostare per circa un secondo.

Poiché il dito è già stato avviato lo spostamento quando viene generato l'evento DragStarted, l'oggetto DragStartedEventArgs può includere una proprietà di direzione di tipo orientamento (orizzontale o verticale). Il DragDeltaEventArgs di oggetti che accompagna l'evento DragDelta include ulteriori informazioni: proprietà associate le proprietà HorizontalChange e VerticalChange che sono utili per l'aggiunta alle proprietà x e y di TranslateTransform, o Canvas. Left e Canvas. Top.

Quando si sposta un dito sullo schermo come è fermata, suggerisce che l'utente desidera inerzia si verifichi, si verifica l'evento gesto rapido. Gli argomenti dell'evento includono un angolo (misurato in senso orario dal valore positivo asse X) e i valori HorizontalVelocity e VerticalVelocity, in pixel al secondo.

L'evento gesto rapido può verificarsi in isolamento;oppure può essere tra gli eventi DragStarted e DragCompleted senza alcun evento DragDelta.oppure può seguire una serie di eventi DragDelta prima di DragCompleted. In genere è necessario gestire gli eventi di trascinamento e spostare gli eventi in combinazione, quasi come se il gesto rapido è la continuazione dell'operazione di trascinamento. Tuttavia, è necessario aggiungere la logica di inerzia.

Come illustrato nel progetto DragAndFlick. La visualizzazione contiene un'ellisse che l'utente trascina intorno semplicemente con un dito. Se il dito esce dallo schermo con un movimento di flicking, quindi si verifica un evento gesto rapido e il gestore gesto rapido consente di salvare alcune informazioni e consente di installare un gestore per l'evento CompositionTarget.Rendering. Questo evento, che si verifica in sincronizzazione con l'aggiornamento per la visualizzazione del video, mantiene l'ellisse lo spostamento durante l'applicazione di una decelerazione alla velocità.

Rimbalzo su lati viene gestito un po' insolitamente: il programma consente di mantenere una posizione come se l'ellisse tiene semplicemente lo spostamento nella stessa direzione fino a quando non si ferma;tale posizione viene piegata nell'area in cui può rimbalzo.

Deforma, Me, è necessario essere un auspicio

La sequenza alternativa si verifica quando due dita sono adiacenti sullo schermo.in genere viene interpretato per espandere o contrarre un oggetto sullo schermo, probabilmente la rotazione anche.

Qualsiasi problema che l'operazione pinching costituisce uno dei settori più treacherous di multi-touch l'elaborazione e non è raro vedere interfacce di alto livello di non riuscire a fornire informazioni adeguate. L'evento ManipulationDelta 7 telefono di Windows è più notoriamente particolarmente complessa da utilizzare.

Quando si gestiscono i movimenti, le sequenze di trascinamento e alternativa si escludono a vicenda. Non si sovrappongano ma possono verificarsi back to back. Ad esempio, premere un dito sullo schermo e trascinarlo. Che genera un DragStarted e DragDelta di più eventi. A questo punto premere un secondo dito sullo schermo. Si otterrà un DragCompleted per completare la sequenza di trascinamento seguita da un PinchStarted e PinchDelta di più eventi. A questo punto sollevare il dito secondo mentre l'indice consente di mantenere lo spostamento. Che è un PinchCompleted per completare la sequenza alternativa, seguita da DragStarted e DragDelta. A seconda del numero di dita tocca lo schermo, fondamentalmente sono alternati tra le sequenze di trascinamento e alternativa.

Una caratteristica utile di questo movimento alternativa è che esso non scarterà le informazioni. Per ricostruire interamente le posizioni dei due dita, pertanto è sempre possibile tornare ai principi primo se è necessario, è possibile utilizzare le proprietà degli argomenti dell'evento.

Durante una sequenza alternativa, la posizione corrente di un dito, che chiameremo il dito primario, è sempre disponibile con il metodo GetPosition. Per questa discussione, chiamare pt1 tale valore restituito. Per l'evento PinchStarted, la classe PinchStartedGestureEventArgs dispone di due proprietà aggiuntive denominato distanza e l'angolo che indica la posizione del frutto secondo rispetto al primo. È possibile calcolare facilmente il percorso effettivo utilizzando la seguente istruzione:

Punto pt2 = nuovo punto (pt1.X + args.Distanza * Cos(args.Angolo), pt1.Y + args.Distanza * Sin (args.Angolo));

La proprietà angolo è espresso in gradi, sarà necessario Cos e Sin metodi per convertire in radianti, prima di chiamare Math. cos e Math. sin. Prima che il gestore di PinchStarted è stata completata, verrà inoltre desiderate salvare le proprietà di distanza e l'angolo nei campi, ad esempio denominati pinchStartDistance e pinchStartAngle.

L'evento PinchDelta è accompagnato da un oggetto PinchGestureEventArgs. Ancora una volta, il metodo GetPosition viene indicato il percorso del frutto primario, che forse è stato spostato dalla posizione originale. Per il dito secondo gli argomenti dell'evento forniscono le proprietà DistanceRatio e TotalAngleDelta.

Il DistanceRatio è il rapporto tra la distanza tra le dita per la distanza originale, vale a dire che è possibile calcolare la distanza corrente come segue:

distanza Double = args.DistanceRatio * pinchStartDistance;

Il TotalAngleDelta è una differenza tra l'angolo corrente tra le dita e l'angolo originale. È possibile calcolare l'angolo corrente simile al seguente:

angolo doppio = args.TotalAngleDelta + pinchStartAngle;

A questo punto è possibile calcolare la posizione del secondo frutto come prima:

Punto pt2 = nuovo punto (pt1.X + distanza * Cos(angle), pt1.Y + distanza * Sin(angle));

Non è necessario salvare eventuali informazioni aggiuntive per i campi durante la gestione per elaborare ulteriormente gli eventi di PinchDelta PinchDelta.

Il progetto TwoFingerTracking viene illustrata questa logica visualizzando ellissi blu e verde che tengono traccia di uno o due dita sullo schermo.

Modificare la scala e rotazione

L'evento PinchDelta fornisce anche informazioni sufficienti per eseguire il ridimensionamento e rotazione su oggetti. È stato necessario fornire il proprio metodo moltiplicazione di matrice, ma che era sulle dimensioni degli inconvenienti.

Per illustrare che il progetto ScaleAndRotate implementa ciò che è ora un tipo "tradizionale" di dimostrazione che consente di trascinare, scalare e ruotare facoltativamente una fotografia. Per eseguire queste trasformazioni, ho definito l'elemento immagine con un RenderTransform double-barreled come illustrato nella nella figura 1.

Figura 1, l'elemento immagine in ScaleAndRotate

< nome immagine = "image"Source="PetzoldTattoo.jpg"Dilatazione = "None"HorizontalAlignment = "Left"VerticalAlignment = "Top" ><Image.RenderTransform><TransformGroup>< MatrixTransform. x: Name = "previousTransform"/ >

        < TransformGroup X:Name = "currentTransform" >< ScaleTransform X:Name = "scaleTransform"/ >< RotateTransform X:Name = "rotateTransform"/ >< TranslateTransform X:Name = "translateTransform"/ ></TransformGroup></TransformGroup></Image.RenderTransform></Image>

Quando un'operazione di trascinamento o alternativa è in corso, tre trasformazioni TransformGroup nidificati sono manipolate per spostare l'immagine sullo schermo, ridimensionarlo e ruotarlo. Quando si verifica un evento DragCompleted o PinchCompleted, la matrice in MatrixTransform denominata previousTransform viene moltiplicata per la trasformazione composita disponibile come proprietà Value del TransformGroup. Le tre trasformazioni in questo TransformGroup quindi vengono reimpostate ai valori predefiniti.

Scala e rotazione sono sempre rispetto a un punto centrale, ovvero il punto rimane nella stessa posizione quando si verifica la trasformazione. Una fotografia, ridimensionati o ruotati rispetto alle estremità di angolo superiore sinistro in un percorso diverso da una fotografia, ridimensionati o ruotati relativa all'angolo in basso a destra.

Viene riportato il codice di ScaleAndRotate in nella figura 2. Utilizzare il dito primario come centro di ridimensionamento e rotazione;questi punti centrali sono impostati in trasformazioni durante la gestione di PinchStarted e non cambiano per tutta la durata della sequenza alternativa. Durante gli eventi PinchDelta, le proprietà DistanceRatio e TotalAngleDelta forniscono scala e rotazione informazioni relative a tale area. Qualsiasi modifica in circolazione del frutto principale (che deve essere rilevato con un campo salvato) quindi diventa un fattore di conversione generale.

Figura 2 il codice ScaleAndRotate

public partial class MainPage: PhoneApplicationPage {bool isDragging;BOOL isPinching;Punto ptPinchPositionStart;

    MainPage() pubblica {InitializeComponent ();}

    void OnGestureListenerDragStarted (object sender, DragStartedGestureEventArgs args) {isDragging = args.OriginalSource = = immagine;}

    void OnGestureListenerDragDelta (object sender, DragDeltaGestureEventArgs args) {se (isDragging) {args + = translateTransform.X.HorizontalChange;translateTransform.Y + = args.VerticalChange;} }

    void OnGestureListenerDragCompleted (object sender, DragCompletedGestureEventArgs args) {se (isDragging) {TransferTransforms();isDragging = false;} }

    void OnGestureListenerPinchStarted (object sender, PinchStartedGestureEventArgs args) {isPinching = args.OriginalSource = = immagine;

        Se (isPinching) {/ / Set di trasformazione Centra il punto ptPinchCenter = args.GetPosition(image);ptPinchCenter = previousTransform.Transform(ptPinchCenter);

            scaleTransform.CenterX = ptPinchCenter.X;scaleTransform.CenterY = ptPinchCenter.Y;

            rotateTransform.CenterX = ptPinchCenter.X;rotateTransform.CenterY = ptPinchCenter.Y;

            ptPinchPositionStart = args.GetPosition(this);}}} void OnGestureListenerPinchDelta (object sender, PinchGestureEventArgs args) {se (isPinching) {/ / imposta la scala scaleTransform.ScaleX = args.DistanceRatio;scaleTransform.ScaleY = args.DistanceRatio;

            / / Imposta eventualmente rotazione se rotateTransform.Angle (allowRotateCheckBox.IsChecked.Value) = args.TotalAngleDelta;

            / / Imposta la traduzione punto ptPinchPosition = args.GetPosition(this);translateTransform.X = ptPinchPosition.X - ptPinchPositionStart.X;translateTransform.Y = ptPinchPosition.Y - ptPinchPositionStart.Y;} }

    void OnGestureListenerPinchCompleted (object sender, PinchGestureEventArgs args) {se (isPinching) {TransferTransforms();isPinching = false;} }

    void TransferTransforms() {previousTransform.Matrix = Multiply (previousTransform.Matrix, currentTransform.Value);

        / / Imposta le trasformazioni correnti per impostazione predefinita i valori scaleTransform.ScaleX = scaleTransform.ScaleY = 1;scaleTransform.CenterX = scaleTransform.CenterY = 0;

        rotateTransform.Angle = 0;rotateTransform.CenterX = rotateTransform.CenterY = 0;

        translateTransform.X = translateTransform.Y = 0;}

    Matrice di moltiplicazione (matrice A, B Matrix) {restituire nuova matrice (A.M11 * B.M11 + A.M12 * B.M21, A.M11 * B.M12 + A.M12 * B.M22, A.M21 * B.M11 + A.M22 * B.M21, A.M21 * B.M12 + A.M22 * B.M22A.OffsetX * B.M11 + A.OffsetY * B.M21 + B.OffsetX, A.OffsetX * B.M12 + A.OffsetY * B.M22 + B.OffsetY);} }

Che è sicuramente il codice alternativa più semplice che mai ho scritto e fatto è forse il migliore visto che sia possibile fornire per questa nuova interfaccia di movimento.

Non è possibile che una teoria unificata di multi-touch è fuori dopo che tutti i.

Charles Petzold è un redattore esperti di MSDN Magazine*.*Il suo nuovo libro, "Programmazione telefono Windows 7" (Microsoft Press, 2010), è disponibile come download gratuito in bit.LY/cpebookpdf.

Grazie all'esperto tecnico riportato di seguito per la revisione di questo articolo: Richard Bailey