android 拖拽與縮放
編寫:Andrwyw - 原文:http://developer.android.com/training/gestures/scale.html
本節課程講述,使用onTouchEvent()截獲觸控事件後,如何使用觸控手勢拖拽、縮放螢幕上的物件。
拖拽一個物件
如果我們的目標版本為3.0或以上,我們可以使用View.OnDragListener監聽內建的拖放(drag-and-drop)事件,拖拽與釋放中有更多相關描述。
對於觸控手勢來說,一個很常見的操作是在螢幕上拖拽一個物件。接下來的程式碼段讓使用者可以拖拽螢幕上的圖片。需要注意以下幾點:
- 拖拽操作時,即使有額外的手指放置到螢幕上了,app也必須保持對最初的點(手指)的追蹤。比如,想象在拖拽圖片時,使用者放置了第二根手指在螢幕上,並且抬起了第一根手指。如果我們的app只是單獨地追蹤每個點,它會把第二個點當做預設的點,並且把圖片移到該點的位置。
- 為了防止這種情況發生,我們的app需要區分初始點以及隨後任意的觸控點。要做到這一點,它需要追蹤處理多觸控手勢章節中提到過的 ACTION_POINTER_DOWN 和 ACTION_POINTER_UP 事件。每當第二根手指按下或拿起時,ACTION_POINTER_DOWN 和 ACTION_POINTER_UP 事件就會傳遞給
onTouchEvent()
回撥函式。 - 當ACTION_POINTER_UP事件發生時,示例程式會移除對該點的索引值的引用,確保操作中的點的ID(the active pointer ID)不會引用已經不在觸控式螢幕上的觸控點。這種情況下,app會選擇另一個觸控點來作為操作中(active)的點,並儲存它當前的x、y值。由於在ACTION_MOVE事件時,這個儲存的位置會被用來計算螢幕上的物件將要移動的距離,所以app會始終根據正確的觸控點來計算移動的距離。
下面的程式碼段允許使用者拖拽螢幕上的物件。它會記錄操作中的點(active pointer)的初始位置,計算觸控點移動過的距離,再把物件移動到新的位置。如上所述,它也正確地處理了額外觸控點的可能。
需要注意的是,程式碼段中使用了getActionMasked()函式。我們應該始終使用這個函式(或者最好用MotionEventCompat.getActionMasked()這個相容版本)來獲得MotionEvent對應的動作(action)。不像舊的getAction()函式,getActionMasked()
就是設計用來處理多點觸控的。它會返回執行過的動作的掩碼值,不包括該點的索引位。
// The ‘active pointer’ is the one currently moving our object.
private int mActivePointerId = INVALID_POINTER_ID;
@Override
public boolean onTouchEvent(MotionEvent ev) {
// Let the ScaleGestureDetector inspect all events.
mScaleDetector.onTouchEvent(ev);
final int action = MotionEventCompat.getActionMasked(ev);
switch (action) {
case MotionEvent.ACTION_DOWN: {
final int pointerIndex = MotionEventCompat.getActionIndex(ev);
final float x = MotionEventCompat.getX(ev, pointerIndex);
final float y = MotionEventCompat.getY(ev, pointerIndex);
// Remember where we started (for dragging)
mLastTouchX = x;
mLastTouchY = y;
// Save the ID of this pointer (for dragging)
mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
break;
}
case MotionEvent.ACTION_MOVE: {
// Find the index of the active pointer and fetch its position
final int pointerIndex =
MotionEventCompat.findPointerIndex(ev, mActivePointerId);
final float x = MotionEventCompat.getX(ev, pointerIndex);
final float y = MotionEventCompat.getY(ev, pointerIndex);
// Calculate the distance moved
final float dx = x - mLastTouchX;
final float dy = y - mLastTouchY;
mPosX += dx;
mPosY += dy;
invalidate();
// Remember this touch position for the next move event
mLastTouchX = x;
mLastTouchY = y;
break;
}
case MotionEvent.ACTION_UP: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_CANCEL: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_POINTER_UP: {
final int pointerIndex = MotionEventCompat.getActionIndex(ev);
final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
if (pointerId == mActivePointerId) {
// This was our active pointer going up. Choose a new
// active pointer and adjust accordingly.
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mLastTouchX = MotionEventCompat.getX(ev, newPointerIndex);
mLastTouchY = MotionEventCompat.getY(ev, newPointerIndex);
mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);
}
break;
}
}
return true;
}
通過拖拽平移
前一節展示了一個,在螢幕上拖拽物件的例子。另一個常見的場景是平移(panning),平移是指使用者通過拖拽移動引起x、y軸方向發生滾動(scrolling)。上面的程式碼段直接截獲了MotionEvent動作來實現拖拽。這一部分的程式碼段,利用了平臺對常用手勢的內建支援。它重寫了GestureDetector.SimpleOnGestureListener的onScroll()函式。
更詳細地說,當使用者拖拽手指來平移內容時,onScroll()
函式就會被呼叫。onScroll()
函式只會在手指按下的情況下被呼叫,一旦手指離開螢幕了,要麼手勢終止,要麼快速滑動(fling)手勢開始(如果手指在離開螢幕前快速移動了一段距離)。關於滾動與快速滑動的更多討論,可以檢視滾動手勢動畫章節。
這裡是onScroll()
的相關程式碼段:
// The current viewport. This rectangle represents the currently visible
// chart domain and range.
private RectF mCurrentViewport =
new RectF(AXIS_X_MIN, AXIS_Y_MIN, AXIS_X_MAX, AXIS_Y_MAX);
// The current destination rectangle (in pixel coordinates) into which the
// chart data should be drawn.
private Rect mContentRect;
private final GestureDetector.SimpleOnGestureListener mGestureListener
= new GestureDetector.SimpleOnGestureListener() {
...
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
// Scrolling uses math based on the viewport (as opposed to math using pixels).
// Pixel offset is the offset in screen pixels, while viewport offset is the
// offset within the current viewport.
float viewportOffsetX = distanceX * mCurrentViewport.width()
/ mContentRect.width();
float viewportOffsetY = -distanceY * mCurrentViewport.height()
/ mContentRect.height();
...
// Updates the viewport, refreshes the display.
setViewportBottomLeft(
mCurrentViewport.left + viewportOffsetX,
mCurrentViewport.bottom + viewportOffsetY);
...
return true;
}
onScroll()
函式中滑動視窗(viewport)來響應觸控手勢的實現:
/**
* Sets the current viewport (defined by mCurrentViewport) to the given
* X and Y positions. Note that the Y value represents the topmost pixel position,
* and thus the bottom of the mCurrentViewport rectangle.
*/
private void setViewportBottomLeft(float x, float y) {
/*
* Constrains within the scroll range. The scroll range is simply the viewport
* extremes (AXIS_X_MAX, etc.) minus the viewport size. For example, if the
* extremes were 0 and 10, and the viewport size was 2, the scroll range would
* be 0 to 8.
*/
float curWidth = mCurrentViewport.width();
float curHeight = mCurrentViewport.height();
x = Math.max(AXIS_X_MIN, Math.min(x, AXIS_X_MAX - curWidth));
y = Math.max(AXIS_Y_MIN + curHeight, Math.min(y, AXIS_Y_MAX));
mCurrentViewport.set(x, y - curHeight, x + curWidth, y);
// Invalidates the View to update the display.
ViewCompat.postInvalidateOnAnimation(this);
}
使用觸控手勢進行縮放
如同檢測常用手勢章節中提到的,GestureDetector可以幫助我們檢測Android中的常見手勢,例如滾動,快速滾動以及長按。對於縮放,Android也提供了ScaleGestureDetector類。當我們想讓view能識別額外的手勢時,我們可以同時使用GestureDetector和ScaleGestureDetector類。
為了報告檢測到的手勢事件,手勢檢測需要一個作為建構函式引數的listener物件。ScaleGestureDetector使用ScaleGestureDetector.OnScaleGestureListener。Android提供了ScaleGestureDetector.SimpleOnScaleGestureListener類作為幫助類,如果我們不是關注所有的手勢事件,我們可以繼承(extend)它。
基本的縮放示例
下面的程式碼段展示了縮放功能中的基本部分。
private ScaleGestureDetector mScaleDetector;
private float mScaleFactor = 1.f;
public MyCustomView(Context mContext){
...
// View code goes here
...
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
// Let the ScaleGestureDetector inspect all events.
mScaleDetector.onTouchEvent(ev);
return true;
}
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
canvas.scale(mScaleFactor, mScaleFactor);
...
// onDraw() code goes here
...
canvas.restore();
}
private class ScaleListener
extends ScaleGestureDetector.SimpleOnScaleGestureListener {
@Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
// Don't let the object get too small or too large.
mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f));
invalidate();
return true;
}
}
更加複雜的縮放示例
這是本章節提供的InteractiveChart
示例中一個更復雜的示範。通過使用ScaleGestureDetector中的"span"(getCurrentSpanX/Y)和"focus"(getFocusX/Y)功能,InteractiveChart
示例同時支援滾動(平移)以及多指縮放。
@Override
private RectF mCurrentViewport =
new RectF(AXIS_X_MIN, AXIS_Y_MIN, AXIS_X_MAX, AXIS_Y_MAX);
private Rect mContentRect;
private ScaleGestureDetector mScaleGestureDetector;
...
public boolean onTouchEvent(MotionEvent event) {
boolean retVal = mScaleGestureDetector.onTouchEvent(event);
retVal = mGestureDetector.onTouchEvent(event) || retVal;
return retVal || super.onTouchEvent(event);
}
/**
* The scale listener, used for handling multi-finger scale gestures.
*/
private final ScaleGestureDetector.OnScaleGestureListener mScaleGestureListener
= new ScaleGestureDetector.SimpleOnScaleGestureListener() {
/**
* This is the active focal point in terms of the viewport. Could be a local
* variable but kept here to minimize per-frame allocations.
*/
private PointF viewportFocus = new PointF();
private float lastSpanX;
private float lastSpanY;
// Detects that new pointers are going down.
@Override
public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {
lastSpanX = ScaleGestureDetectorCompat.
getCurrentSpanX(scaleGestureDetector);
lastSpanY = ScaleGestureDetectorCompat.
getCurrentSpanY(scaleGestureDetector);
return true;
}
@Override
public boolean onScale(ScaleGestureDetector scaleGestureDetector) {
float spanX = ScaleGestureDetectorCompat.
getCurrentSpanX(scaleGestureDetector);
float spanY = ScaleGestureDetectorCompat.
getCurrentSpanY(scaleGestureDetector);
float newWidth = lastSpanX / spanX * mCurrentViewport.width();
float newHeight = lastSpanY / spanY * mCurrentViewport.height();
float focusX = scaleGestureDetector.getFocusX();
float focusY = scaleGestureDetector.getFocusY();
// Makes sure that the chart point is within the chart region.
// See the sample for the implementation of hitTest().
hitTest(scaleGestureDetector.getFocusX(),
scaleGestureDetector.getFocusY(),
viewportFocus);
mCurrentViewport.set(
viewportFocus.x
- newWidth * (focusX - mContentRect.left)
/ mContentRect.width(),
viewportFocus.y
- newHeight * (mContentRect.bottom - focusY)
/ mContentRect.height(),
0,
0);
mCurrentViewport.right = mCurrentViewport.left + newWidth;
mCurrentViewport.bottom = mCurrentViewport.top + newHeight;
...
// Invalidates the View to update the display.
ViewCompat.postInvalidateOnAnimation(InteractiveLineGraphView.this);
lastSpanX = spanX;
lastSpanY = spanY;
return true;
}
};
轉自:http://hukai.me/android-training-course-in-chinese/input/gestures/scale.html
相關文章
- vue-drag-resize 全解,vue拖拽縮放元件Vue元件
- Android 圖片縮放Android
- C#影象顯示實現拖拽、錨點縮放功能【轉】C#
- Android:ImageView圖片縮放、居中AndroidView
- flutter實現可縮放可拖拽雙擊放大的圖片功能Flutter
- android imageview 縮放檢視圖片AndroidView
- Android動態UI適配(等比縮放)AndroidUI
- Android 可平移,縮放,旋轉的ImageViewAndroidView
- 縮放比
- 通過手勢實現Android ImageView 縮放AndroidView
- html-拖拽釋放(Drag and drop) APIHTMLAPI
- 如何縮放SVGSVG
- UWPWebView禁用縮放WebView
- Android拖拽詳解Android
- 【Android動畫】之Tween動畫 (漸變、縮放、位移、旋轉)Android動畫
- Android開發筆記——點選檢視大圖過渡動畫與圖片縮放與移動Android筆記動畫
- C++影象縮放C++
- 快速開啟縮放資料夾與Safari應用
- Android根據螢幕寬度,按比例縮放圖片Android
- cad縮放快捷鍵命令 cad縮放快捷鍵使用的方法
- 實現圖片縮放
- 直播電商平臺開發,Android | 圖片縮放、自動居中Android
- Android 中實現圖片平移、縮放、旋轉同步進行Android
- VAR:自迴歸建模與縮放的視覺大模型視覺大模型
- win10游標怎麼縮放_win10游標縮放方法Win10
- 線性代數在前端中的應用(一):實現滑鼠滾輪縮放元素、Canvas圖片和拖拽前端Canvas
- CAD如何使用縮放命令
- OpenCV(iOS)影象尺寸縮放(14)OpenCViOS
- html 圖片按比例縮放HTML
- 縮放控制ZoomControlsOOM
- Java中縮放緩衝影像Java
- Android 呼叫相簿 拍照 實現系統控制元件縮放 切割圖片Android控制元件
- .NetCore實現圖片縮放與裁剪 - 基於ImageSharpNetCore
- win10怎麼開啟自定義縮放 win10怎麼自定義縮放Win10
- Android 圖片壓縮方法分析與學習Android
- Android學習歷程--Launcher拖拽流程Android
- Flutter 自定義縮放控制元件Flutter控制元件
- Canvas 縮放圖片中細節消失Canvas