多點觸控手指追蹤
本主題示範如何追蹤多指的觸控事件
有時,多觸控應用程式需要追蹤個別手指,因為它們同時在螢幕上移動。 一個典型的應用程式是手指繪製程式。 您希望用戶能夠使用單指繪製,但也想要一次使用多根手指繪製。 當您的程式處理多個觸控事件時,它必須區分哪些事件對應至每個手指。 Android 會為此提供標識碼,但取得和處理該程式代碼可能有點棘手。
對於與特定手指相關聯的所有事件,標識碼會維持不變。 當手指第一次觸碰螢幕時,會指派標識碼,並在手指從螢幕抬起之後變成無效。 這些標識碼通常是非常小的整數,而Android會針對稍後的觸控事件重複使用這些標識碼。
幾乎一律是追蹤個別手指的程式會維護觸控追蹤的字典。 字典索引鍵是識別特定手指的標識碼。 字典值取決於應用程式。 在 Finger 小畫家 範例中,每個手指筆劃(從觸控到釋放)都會與對象相關聯,其中包含使用該手指繪製線條所需的所有資訊。 程式會為此目的定義小型 FingerPaintPolyline
類別:
class FingerPaintPolyline
{
public FingerPaintPolyline()
{
Path = new Path();
}
public Color Color { set; get; }
public float StrokeWidth { set; get; }
public Path Path { private set; get; }
}
每個聚合線條都有色彩、筆劃寬度和Android圖形 Path
物件,以在繪製線條時累積和轉譯多點。
下列程式代碼的其餘部分包含在名為FingerPaintCanvasView
的衍生工具中View
。 該類別會在一或多個手指主動繪製物件時,維護型 FingerPaintPolyline
別物件的字典:
Dictionary<int, FingerPaintPolyline> inProgressPolylines = new Dictionary<int, FingerPaintPolyline>();
此字典可讓檢視快速取得 FingerPaintPolyline
與特定手指相關聯的資訊。
類別 FingerPaintCanvasView
也會針對已完成的聚合線條維護 List
物件:
List<FingerPaintPolyline> completedPolylines = new List<FingerPaintPolyline>();
這個 List
中的物件順序與繪製的順序相同。
FingerPaintCanvasView
會覆寫 由 定義的 View
兩個方法:OnDraw
和 OnTouchEvent
。
在覆 OnDraw
寫中,檢視會繪製已完成的聚合線條,然後繪製進行中的聚合線條。
方法的覆寫會 OnTouchEvent
從 屬性取得 pointerIndex
值 ActionIndex
開始。 這個 ActionIndex
值區分多個手指,但它在多個事件之間並不一致。 基於這個理由,您可以使用 pointerIndex
來從 GetPointerId
方法取得指標id
值。 此識別碼 在多個事件之間是 一致的:
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;
}
請注意,覆寫會使用 ActionMasked
語句中的 switch
屬性,而不是 Action
屬性。 原因如下:
當您處理多點觸控時, Action
屬性的值會讓第一個 MotionEventsAction.Down
手指觸碰螢幕,然後將 Pointer2Down
Pointer3Down
的值設定為 和 ,做為第二和第三個手指也會觸碰螢幕。 當第四根和第五根手指接觸時, Action
屬性具有數值,甚至不會對應至列舉的成員 MotionEventsAction
! 您必須檢查值中的位旗標值,以解譯它們的意義。
同樣地,當手指與螢幕保持接觸時, Action
屬性具有 Pointer2Up
Pointer3Up
的值,以及第二根和第三根手指的值,以及 Up
第一根手指。
屬性 ActionMasked
會佔用較少的值數目,因為它的目的是要與 屬性搭配 ActionIndex
使用,以區分多指。 當手指觸碰螢幕時,屬性只能等於 MotionEventActions.Down
第一根手指和 PointerDown
後續手指。 當手指離開螢幕時,ActionMasked
針對後續手指和Up
第一根手指具有的值Pointer1Up
。
使用 ActionMasked
時,會 ActionIndex
區分後續手指來觸控和離開螢幕,但您通常不需要使用該值,但除了做為物件中其他方法的 MotionEvent
自變數。 針對多點觸控,上述程式代碼會呼叫其中一個最重要的方法 GetPointerId
。 該方法會傳回值,您可以用於字典索引鍵,將特定事件與手指產生關聯。
OnTouchEvent
範例中的覆寫會藉由建立新的 FingerPaintPolyline
物件並將它新增至字典,以相同方式處理 MotionEventActions.Down
和 PointerDown
事件:
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;
// ...
}
// ...
}
請注意, pointerIndex
也用來取得檢視中手指的位置。 所有觸控資訊都與 pointerIndex
值相關聯。 可 id
唯一識別多個訊息的手指,以便用來建立字典專案。
同樣地,覆OnTouchEvent
寫也會藉MotionEventActions.Up
由將完成的聚合線條傳送至completedPolylines
集合來處理 和 Pointer1Up
相同,以便在覆寫期間OnDraw
繪製它們。 程式代碼也會從字典中移除 id
專案:
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;
}
// ...
}
現在,對於棘手的部分。
在向下和向上事件之間,通常有許多 MotionEventActions.Move
事件。 這些會組合在 對的單一呼叫 OnTouchEvent
中,而且它們必須以 Down
不同於 和 Up
事件的方式處理。 pointerIndex
必須忽略先前從 ActionIndex
屬性取得的值。 相反地,方法必須藉由迴圈介於 0 和 PointerCount
屬性之間取得多個pointerIndex
值,然後取得id
每個值pointerIndex
的 :
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;
// ...
}
// ...
}
這種類型的處理可讓範例追蹤個別手指,並在畫面上繪製結果:
您現在已瞭解如何追蹤螢幕上的個別手指,並區分它們。