安卓開發-----手勢動作
阿新 • • 發佈:2018-12-11
在某個point我是非常需要像百度地圖那些自定義手勢動作,能夠放大,縮小,移動,旋轉,但是在網上卻找不到一個立馬下載能使用的包,不說那麼多,我先上介面程式碼:
import android.view.MotionEvent; /** * Created by mr.Hao on 20**/**/**. */ public interface OnGestuerListner { public boolean onOneTouch(MotionEvent event);//點選一個點 public void onTranslate(float x, float y);//手指移動,有正負值,直接累加即可 public void onLarge(float px, float centerX, float centerY);//放大手勢 ,相差多少,中心點座標 public void onDecrease(float px, float centerX, float centerY);//縮小手勢,相差多少,中心點座標 public void onRotate(float degree, float centerX, float centerY);//旋轉手勢,中心點 public void onDoubleClick();//雙擊 }
看到介面怎麼樣?感覺流口水了吧?,那麼我們繼續上實現的程式碼:
import android.util.Log; import android.view.MotionEvent; /** * Created by mr.Hao on 20**/**/**. */ public class GestuerControl { private OnGestuerListner listener; public GestuerControl(OnGestuerListner listener) { this.listener = listener; } //移動 float mX;//移動的x軸 float mY;//移動的y軸 //是否在放大手勢 private boolean isScale = false; //放大的距離 float distance = 0; public boolean OnEvent(MotionEvent event) { if (this.listener == null) { return false; } //現獲得觸控點數量 int pointerCount = event.getPointerCount(); if (pointerCount == 1) { //判斷是否在放大手勢,因為放大手勢最後會remove switch (event.getAction()) { case MotionEvent.ACTION_DOWN: isScale = false; mX = event.getX(); mY = event.getY(); return this.listener.onOneTouch(event); case MotionEvent.ACTION_MOVE: if (!isScale) { //移動了多少個畫素 float difX = Math.abs(mX - event.getX()); float difY = Math.abs(mY - event.getY()); if (mX < event.getX() && mY == event.getY()) { //向右 listener.onTranslate(difX, 0); } else if (mX > event.getX() && mY == event.getY()) { //向左 listener.onTranslate(-difX, 0); } else if (mY < event.getY() && mX == event.getX()) { //向下 listener.onTranslate(0, difY); } else if (mY > event.getY() && mX == event.getX()) { //向上 listener.onTranslate(0, -difY); //共同移動 } else if (mX < event.getX() && mY < event.getY()) { //向右,向下 listener.onTranslate(difX, difY); } else if (mX < event.getX() && mY > event.getY()) { //向右,向上 listener.onTranslate(difX, -difY); } else if (mX > event.getX() && mY < event.getY()) { //向左,向下 listener.onTranslate(-difX, difY); } else if (mX > event.getX() && mY > event.getY()) { //向左,向下 listener.onTranslate(-difX, -difY); } mX = event.getX(); mY = event.getY(); return this.listener.onOneTouch(event); } break; case MotionEvent.ACTION_UP: distance = 0; lastX1 = -1; lastY1 = -1; lastX2 = -1; lastY2 = -1; //開始判斷是否是雙擊 long clickTime = System.currentTimeMillis(); //判斷範圍 float clickX = event.getX(); float clickY = event.getY(); //先判斷點選範圍 if (Math.abs(clickX - lastClickX) < ENABLE_CLICK_DIS && Math.abs(clickY - lastClickY) < ENABLE_CLICK_DIS) { //判斷點選時間 if ((clickTime - lastClickTime) < DOUBLE_CLICK_TIME) { listener.onDoubleClick(); //雙擊後復原,防止多重點選 lastClickTime = 0; lastClickX = 0f; lastClickY = 0f; } else { //如果是第一次點選就賦值 lastClickTime = clickTime; } } else { lastClickX = clickX; lastClickY = clickY; lastClickTime = clickTime; } break; } } else if (pointerCount > 1) { onTwoTouch(event); } return true; } //最後點選的位置 private float lastClickX = 0f; private float lastClickY = 0f; //在這個點選返回內判斷是點選有效 private final static float ENABLE_CLICK_DIS = 50; //最後點選的時間 private long lastClickTime = 0; //當兩次點選小於這個時間被認為是雙擊 private final static long DOUBLE_CLICK_TIME = 500; private float lastX1 = -1; private float lastY1 = -1; private float lastX2 = -1; private float lastY2 = -1; private void onTwoTouch(MotionEvent event) { float x1 = event.getX(0); float y1 = event.getY(0); float x2 = event.getX(1); float y2 = event.getY(1); if (event.getAction() == MotionEvent.ACTION_POINTER_DOWN) { isScale = true; distance = (float) Math.abs(Math.sqrt(Math.pow((x1 - x2), 2) + Math.pow((y1 - y2), 2))); } else if (event.getAction() == MotionEvent.ACTION_MOVE) { //縮放手勢 if (distance == 0) { distance = (float) Math.abs(Math.sqrt(Math.pow((x1 - x2), 2) + Math.pow((y1 - y2), 2))); } else { float tempD = (float) Math.abs(Math.sqrt(Math.pow((x1 - x2), 2) + Math.pow((y1 - y2), 2))); float d = tempD - distance; if (Math.abs(d) > 50) { //第一次是否是錯誤的點,可能是上次的 distance = (float) Math.abs(Math.sqrt(Math.pow((x1 - x2), 2) + Math.pow((y1 - y2), 2))); } else { isScale = true; if (d > 0) { this.listener.onLarge(d, getCenter(x1, x2), getCenter(y1, y2)); } else if (d < 0) { this.listener.onDecrease(Math.abs(d), getCenter(x1, x2), getCenter(y1, y2)); } distance = (float) Math.abs(Math.sqrt(Math.pow((x1 - x2), 2) + Math.pow((y1 - y2), 2))); } } } else if (event.getAction() == MotionEvent.ACTION_POINTER_UP) { distance = 0; } //旋轉手勢,觸碰獲得座標點 if (event.getAction() == MotionEvent.ACTION_POINTER_DOWN) { lastX1 = x1; lastY1 = y1; lastX2 = x2; lastY2 = y2; } else if (event.getAction() == MotionEvent.ACTION_MOVE) { if (lastX1 == -1 || lastY1 == -1 || lastX2 == -1 || lastY2 == -1) { lastX1 = x1; lastY1 = y1; lastX2 = x2; lastY2 = y2; } else { //計算兩條直線的方程 //y=kx+b //第一條直線 double k1; double b1; //先求出斜率 k1 = (lastY1 - lastY2) / (lastX1 - lastX2); //求出b b1 = (lastY2 * lastX1 - lastY1 * lastX2) / (lastX1 - lastX2); //第二條直線 //先求出斜率 double k2 = (y1 - y2) / (x1 - x2); //求出b double b2 = (y2 * x1 - y1 * x2) / (x1 - x2); //斜率不相等才有交點 if (k1 != k2) { //計算交點座標 double centerX = (b2 - b1) / (k1 - k2); double centerY = (b2 * k1 - b1 * k2) / (k1 - k2); //計算交點是否在兩條直線上 double lastMaxX = lastX1 > lastX2 ? lastX1 : lastX2; double lastMinX = lastX1 < lastX2 ? lastX1 : lastX2; double lastMaxY = lastY1 > lastY2 ? lastY1 : lastY2; double lastMinY = lastY1 < lastY2 ? lastY1 : lastY2; double maxX = x1 > x2 ? x1 : x2; double minX = x1 < x2 ? x1 : x2; double maxY = y1 > y2 ? y1 : y2; double minY = y1 < y2 ? y1 : y2; if (centerX < lastMaxX && centerX > lastMinX && centerY < lastMaxY && centerY > lastMinY && centerX < maxX && centerX > minX && centerY < maxY && centerY > minY) { //移動計算角度 //分別計算第一次的觸碰的半徑的平方,即兩點距離公式的平方,兩個手指一動分別形成兩個三個型A,B //A半徑^2 double A_R1 = (Math.pow(lastX1 - centerX, 2) + Math.pow(lastY1 - centerY, 2)); //B半徑^2 double B_R1 = (Math.pow(lastX2 - centerX, 2) + Math.pow(lastY2 - centerY, 2)); //然後計算一動後產生的第二次的半徑 //A半徑^2 double A_R2 = (Math.pow(x1 - centerX, 2) + Math.pow(y1 - centerY, 2)); //B半徑^2 double B_R2 = (Math.pow(x2 - centerX, 2) + Math.pow(y2 - centerY, 2)); //然後計算兩端的第三段長度,形成一個三角形 //A形成的第三條邊 double A_A3 = (Math.pow(lastX1 - x1, 2) + Math.pow(lastY1 - y1, 2)); //B形成的第三條邊 double B_A3 = (Math.pow(lastX2 - x2, 2) + Math.pow(lastY2 - y2, 2)); //然後用餘弦定理分別把角求出 // <c=(a^2+b^2-c^2)/(2ab),a,b,c分別為三角形的三條邊長 //A三角形的角度餘弦值 double A_AngleCos = (A_R1 + A_R2 - A_A3) / (2 * Math.sqrt(A_R1) * Math.sqrt(A_R2)); //是否超出定義範圍,餘弦值定義為[-1,1] A_AngleCos = A_AngleCos > 1 ? 1 : A_AngleCos; A_AngleCos = A_AngleCos < -1 ? -1 : A_AngleCos; //B三角形的角度餘弦值 double B_AngleCos = (B_R1 + B_R2 - B_A3) / (2 * Math.sqrt(B_R1) * Math.sqrt(B_R2)); //是否超出定義範圍,餘弦值定義為[-1,1] B_AngleCos = B_AngleCos > 1 ? 1 : B_AngleCos; B_AngleCos = B_AngleCos < -1 ? -1 : B_AngleCos; //判斷向量方向 boolean isClockwiseA = ((lastX1 - centerX) * (y1 - centerY) - (lastY1 - centerY) * (x1 - centerX)) > 0; boolean isClockwiseB = ((lastX2 - centerX) * (y2 - centerY) - (lastY2 - centerY) * (x2 - centerX)) > 0; //A角大小,Math.acos弧度轉換成角度 double A_Angle = Math.toDegrees(Math.acos(A_AngleCos)); //B角大小 double B_Angle = Math.toDegrees(Math.acos(B_AngleCos)); //計算得出的餘弦值轉換角度 double angle = A_Angle > B_Angle ? A_Angle : B_Angle; boolean isClockwise = A_Angle > B_Angle ? isClockwiseA : isClockwiseB; if (angle > 10 || angle < -10 || angle == Double.NaN || angle == Double.NEGATIVE_INFINITY || angle == Double.POSITIVE_INFINITY) { Log.e("NAN", "lastX1=" + lastX1 + ",lastY1=" + lastY1 + ",x1=" + x1 + ",y1=" + y1 + ",centerX=" + centerX + ",centerY=" + centerY + ",angle=" + angle); lastX1 = x1; lastY1 = y1; lastX2 = x2; lastY2 = y2; } else { this.listener.onRotate((float) (isClockwise ? -angle : angle), (float) centerX, (float) centerY); } } else { lastX1 = -1; lastY1 = -1; lastX2 = -1; lastY2 = -1; } } lastX1 = x1; lastY1 = y1; lastX2 = x2; lastY2 = y2; } } else if (event.getAction() == MotionEvent.ACTION_POINTER_UP) { lastX1 = -1; lastY1 = -1; lastX2 = -1; lastY2 = -1; } } //計算兩點的中心點 private float getCenter(float v1, float v2) { return (v1 + v2) / 2; } }
這就是這幾個手勢的全部程式碼,使用更簡單方便,上上上上程式碼:
import android.view.MotionEvent; import android.view.View; /** * Created by mr.Hao on 20**/**/**. */ public class MyView extends View implements OnGestuerListner { //獲得手勢例項 private GestuerControl gestuerControl = new GestuerControl(this); @Override public boolean onTouchEvent(MotionEvent event) { boolean flag = gestuerControl.OnEvent(event); if (!flag) { return super.onTouchEvent(event); } return true; } @Override public boolean onOneTouch(MotionEvent event) { return true; } @Override public void onTranslate(float x, float y) { } @Override public void onLarge(float px, float centerX, float centerY) { } @Override public void onDecrease(float px, float centerX, float centerY) { } @Override public void onRotate(float degree, float centerX, float centerY) { } @Override public void onDoubleClick() { } }
到這裡的話,操作已經全部完成了,手勢動作就是:
單個手指移動:觸發onTranslate 移動
兩個手指同時向一個方向旋轉:觸發onRotate 旋轉
兩個手指同一直線上反方向移動:觸發 onDecrease 縮小 和 onLarge 放大
單個手指雙擊螢幕:觸發 onDoubleClick 雙擊
okok!!結束了!!!很簡單!!!需要的趕緊cv大法。
轉載請註明!