package com.mm.android.deviceaddmodule.mobilecommon.widget.linechart.touch; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.view.ViewParent; import android.widget.Scroller; import com.mm.android.deviceaddmodule.mobilecommon.widget.linechart.charts.LineChart; import com.mm.android.deviceaddmodule.mobilecommon.widget.linechart.listener.IDragListener; import com.mm.android.deviceaddmodule.mobilecommon.widget.linechart.manager.MappingManager; import com.mm.android.deviceaddmodule.mobilecommon.widget.linechart.utils.RectD; public class TouchListener implements View.OnTouchListener { ViewParent viewParent; GestureDetector _GestureDetector; Zoomer _Zoomer; Scroller _Scroller; LineChart _LineChart; MappingManager _MappingManager; VelocityTracker _VelocityTracker; TouchMode _TouchMode = TouchMode.NONE; /////////////////////////////////// 平滑的缩放 /////////////////////////////////// double _zoom_w, _zoom_h; float _zoom_cx, _zoom_cy; public TouchListener(LineChart lineChart) { this._LineChart = lineChart; _GestureDetector = new GestureDetector(_LineChart.getContext(), new GestureListener()); _MappingManager = lineChart.get_MappingManager(); _Zoomer = new Zoomer(); _Scroller = new Scroller(lineChart.getContext()); } float _lastX, _lastY; float _disX = 1, _disY = 1, _disXY = 1, _cX, _cY; @Override public boolean onTouch(View v, MotionEvent event) { viewParent = v.getParent(); // 速度跟踪 if (_VelocityTracker == null) { _VelocityTracker = VelocityTracker.obtain(); } _VelocityTracker.addMovement(event); boolean hit = _GestureDetector.onTouchEvent(event); if (hit) { _LineChart.invalidate(); return true; } float x = event.getX(); float y = event.getY(); switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: stopAll(); iNeedTouch(true); _lastX = x; _lastY = y; break; case MotionEvent.ACTION_POINTER_DOWN: if (event.getPointerCount() >= 2) { _disX = getXDist(event); _disY = getYDist(event); _disXY = getABSDist(event); float tmpX = event.getX(0) + event.getX(1); float tmpY = event.getY(0) + event.getY(1); _cX = (tmpX / 2f); _cY = (tmpY / 2f); _TouchMode = TouchMode.PINCH_ZOOM; } break; case MotionEvent.ACTION_MOVE: float dx = x - _lastX; float dy = y - _lastY; if (_TouchMode == TouchMode.DRAG) { _VelocityTracker.computeCurrentVelocity(1000); float vx = _VelocityTracker.getXVelocity();// 得到当前手指在这一点移动的速度 float vy = _VelocityTracker.getYVelocity();// 速度分为了x和y轴两个方向的速度 int direction = judgeDir(vx, vy); doDrag(dx, dy, direction); } else if (_TouchMode == TouchMode.PINCH_ZOOM) { doPinch(event); } else if (_TouchMode == TouchMode.NONE) { _TouchMode = TouchMode.DRAG; } _lastX = x; _lastY = y; break; case MotionEvent.ACTION_POINTER_UP: _TouchMode = TouchMode.NONE; break; case MotionEvent.ACTION_UP: if (_TouchMode == TouchMode.DRAG) { _TouchMode = TouchMode.FLING; _VelocityTracker.computeCurrentVelocity(1000, 5000); int vx = (int) _VelocityTracker.getXVelocity(); int vy = (int) _VelocityTracker.getYVelocity(); if (vx > 20 || vy > 20) { doFling(event.getX(), event.getY(), vx, vy); } IDragListener listener = _LineChart.get_dragListener(); if (listener != null) { listener.onDrag(_LineChart.getVisiableMinX(), _LineChart.getVisiableMaxX()); } } _TouchMode = TouchMode.NONE; break; case MotionEvent.ACTION_CANCEL: if (_VelocityTracker != null) { _VelocityTracker.recycle(); _VelocityTracker = null; } _TouchMode = TouchMode.NONE; break; } return true; } float _lastScrollX, _lastScrollY; public void computeScroll() { // 考虑滚动 if (_Scroller.computeScrollOffset()) { int currX = _Scroller.getCurrX(); int currY = _Scroller.getCurrY(); float dx = currX - _lastScrollX; float dy = currY - _lastScrollY; doDrag(dx, dy, -1); _lastScrollX = currX; _lastScrollY = currY; } // 考虑缩放 if (_Zoomer.computeZoom()) { float currentLevel = _Zoomer.getCurrentZoom(); zoom(currentLevel, _zoom_w, _zoom_h, _zoom_cx, _zoom_cy); } } boolean hitStart = false; int hitCount = 0; /** * lchart 需要触摸事件 */ private void iNeedTouch(boolean need) { if (need) { viewParent.requestDisallowInterceptTouchEvent(true); hitStart = false; } else { hitCount++; if (!hitStart) { hitCount = 0; hitStart = true; } else { if (hitCount > 3) { hitStart = false; viewParent.requestDisallowInterceptTouchEvent(false); } else { viewParent.requestDisallowInterceptTouchEvent(true); } } } } private void stopAll() { _Scroller.forceFinished(true); _Zoomer.stop(); } private int judgeDir(float vx, float vy) { float x = Math.abs(vx); float y = Math.abs(vy); if (x > y) { // 方向是水平 return 1; } else { // 方向是垂直 return 2; } } /** * 双手缩放 * * @param event */ private void doPinch(MotionEvent event) { if (!_LineChart.isScaleable()) { return; } iNeedTouch(true); float absDist = getABSDist(event); float scale = _disXY / absDist; if (zoom_alone) { // 考虑独立缩放 float xDis = getXDist(event); float yDis = getYDist(event); if (xDis > yDis) { zoom(scale, 1, _cX, _cY); } else { zoom(1, scale, _cX, _cY); } } else { zoom(scale, scale, _cX, _cY); } _disXY = absDist; } boolean canX_drag = true; boolean canY_drag = true; /** * 拖拽 * * @param dx * @param dy */ private void doDrag(float dx, float dy, int direction) { if (!_LineChart.isDragable()) { return; } if (direction != -1) { RectD current = _MappingManager.get_currentViewPort(); RectD maxx = _MappingManager.get_constrainViewPort(); boolean need = false; if (direction == 1) { // hor need = current.right < maxx.right; need &= current.left > maxx.left; } else { // ver need = current.bottom > maxx.bottom; need &= current.top < maxx.top; } iNeedTouch(need); } if (!canX_drag) { dx = 0; } if (!canY_drag) { dy = 0; } _MappingManager.translate(dx, dy); _LineChart.postInvalidate(); } /** * 抛掷 */ private void doFling(float x, float y, int velocityX, int velocityY) { if (!_LineChart.isDragable()) { return; } _Scroller.forceFinished(true); _lastScrollX = x; _lastScrollY = y; _Scroller.fling( (int) x, (int) y, velocityX, velocityY, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE); _LineChart.invalidate(); } boolean canX_zoom = true; boolean canY_zoom = true; boolean zoom_alone = false; /** * 应用于指定的缩放 * * @param scaleX * @param scaleY * @param cx * @param cy */ private void zoom(float scaleX, float scaleY, float cx, float cy) { if (!_LineChart.isScaleable()) { return; } if (!canX_zoom) { scaleX = 1; } if (!canY_zoom) { scaleY = 1; } _MappingManager.zoom(scaleX, scaleY, cx, cy); _LineChart.postInvalidate(); } /** * 应用于平滑的缩放 * * @param level * @param startW * @param startH * @param cx * @param cy */ private void zoom(float level, double startW, double startH, float cx, float cy) { if (!_LineChart.isScaleable()) { return; } _MappingManager.zoom(level, startW, startH, cx, cy, canX_zoom, canY_zoom); _LineChart.postInvalidate(); } private static float getABSDist(MotionEvent event) { float x = event.getX(0) - event.getX(1); float y = event.getY(0) - event.getY(1); return (float) Math.sqrt(x * x + y * y); } private static float getXDist(MotionEvent e) { float x = Math.abs(e.getX(0) - e.getX(1)); return x; } private static float getYDist(MotionEvent e) { float y = Math.abs(e.getY(0) - e.getY(1)); return y; } public boolean isCanX_drag() { return canX_drag; } public void setCanX_drag(boolean canX_drag) { this.canX_drag = canX_drag; } public boolean isCanY_drag() { return canY_drag; } public void setCanY_drag(boolean canY_drag) { this.canY_drag = canY_drag; } public boolean isCanX_zoom() { return canX_zoom; } public void setCanX_zoom(boolean canX_zoom) { this.canX_zoom = canX_zoom; } public boolean isZoom_alone() { return zoom_alone; } public void setZoom_alone(boolean zoom_alone) { this.zoom_alone = zoom_alone; } public boolean isCanY_zoom() { return canY_zoom; } public void setCanY_zoom(boolean canY_zoom) { this.canY_zoom = canY_zoom; } class GestureListener extends GestureDetector.SimpleOnGestureListener { @Override public boolean onDoubleTap(MotionEvent e) { //禁用双击放大,以后有需求可以在这里打开 if (true){ return false; } _Zoomer.stop(); _zoom_w = _MappingManager.get_currentViewPort().width(); _zoom_h = _MappingManager.get_currentViewPort().height(); _zoom_cx = e.getX(); _zoom_cy = e.getY(); _Zoomer.startZoom(0.6f); return true; } @Override public boolean onDown(MotionEvent e) { return super.onDown(e); } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { return super.onFling(e1, e2, velocityX, velocityY); } @Override public void onLongPress(MotionEvent e) { super.onLongPress(e); } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { return super.onScroll(e1, e2, distanceX, distanceY); } @Override public boolean onSingleTapConfirmed(MotionEvent e) { _LineChart.highLight_PixXY(e.getX(), e.getY()); return true; } } enum TouchMode { NONE, DRAG, X_ZOOM, Y_ZOOM, PINCH_ZOOM, ROTATE, SINGLE_TAP, DOUBLE_TAP, LONG_PRESS, FLING } }