@Override public boolean onTouchEvent(MotionEvent event) { boolean handled = true; switch (event.getAction() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: { // 删除按钮接收事件 if (mShowDashBorder && mDelDrawRectF.contains(event.getX(), event.getY())) { if (getParent() instanceof ViewGroup) { ((ViewGroup) getParent()).removeView(DashFrameLayout.this); } break; } // 拖放按钮接收事件 if (mShowDashBorder && mDragDrawRectF.contains(event.getX(), event.getY())) { mode = SINGLE_ZOOM; showDashBorder(true); mPreDistance = Utils.calcDistance(mCenterPoint[0], mCenterPoint[1], event.getX(), event.getY()); mPreRotation = Utils.calcRotation(mCenterPoint[0], mCenterPoint[1], event.getX(), event.getY()); break; } /** 判断虚线框是否接收事件; 因旋转后无法直接判断事件是否在rect内, 可将坐标点反向旋转后再与未进行旋转的rect进行比较判断 */ mDashDrawRectF.set(mDashRectF); mViewNoRotateMatrix.mapRect(mDashDrawRectF); Matrix inverseMatrix = new Matrix(); // 获取反向旋转矩阵 inverseMatrix.postRotate(360 - mCurRotation, mCenterPoint[0], mCenterPoint[1]); // 将当前坐标反向旋转,再进行判断 float[] eventPoint = {event.getX(), event.getY()}; inverseMatrix.mapPoints(eventPoint); if (mDashDrawRectF.contains(eventPoint[0], eventPoint[1])) { mode = DRAG; mInitTouchX = event.getX(); mInitTouchY = event.getY(); mLastTouchX = event.getX(); mLastTouchY = event.getY(); } else { handled = false; } showDashBorder(handled); if (handled && mListener != null) { mListener.onFocus(); mAlwaysInTapRegion = true; } break; } case MotionEvent.ACTION_MOVE: { if (mAlwaysInTapRegion) { float deltaX = event.getX() - mInitTouchX; float deltaY = event.getY() - mInitTouchY; // 判断是否在点击区域内 if ((deltaX * deltaX) + (deltaY * deltaY) > mTouchSlopSquare) { mAlwaysInTapRegion = false; } } if (mode == SINGLE_ZOOM || mode == DOUBLE_ZOOM) { float curDistance, endRotation; if (mode == SINGLE_ZOOM) { // 单指缩放、旋转 curDistance = Utils.calcDistance(mCenterPoint[0], mCenterPoint[1], event.getX(), event.getY()); endRotation = Utils.calcRotation(mCenterPoint[0], mCenterPoint[1], event.getX(), event.getY()); } else { // 双指缩放、旋转 curDistance = Utils.calcDistance(event); endRotation = Utils.calcRotation(event); } scale(curDistance / mPreDistance); mPreDistance = curDistance; rotate(endRotation - mPreRotation); mPreRotation = endRotation; } else if (mode == DRAG) { translate(event.getX() - mLastTouchX, event.getY() - mLastTouchY); mLastTouchX = event.getX(); mLastTouchY = event.getY(); } invalidate(); break; } case MotionEvent.ACTION_POINTER_DOWN: { mAlwaysInTapRegion = false; if (mode != SINGLE_ZOOM) { mode = DOUBLE_ZOOM; mPreDistance = Utils.calcDistance(event); mPreRotation = Utils.calcRotation(event); } break; } case MotionEvent.ACTION_UP: { mode = NONE; mPreRotation = 0.f; if (mAlwaysInTapRegion && mPreShowDashBorder && mListener != null) { mListener.onClick(); } mAlwaysInTapRegion = false; break; } case MotionEvent.ACTION_POINTER_UP: { if (mode != SINGLE_ZOOM) { mode = NONE; mPreRotation = 0.f; } break; } } return handled; }
public DashFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); setWillNotDraw(false); final int margin = Utils.dip2px(context, 5); mDelBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_stamp_close); mDragBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_stamp_drag); setLayoutParams( new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); mDashPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mDashPaint.setColor(Color.GRAY); mDashPaint.setStyle(Paint.Style.STROKE); mDashPaint.setStrokeWidth(3); mDashPath = new Path(); mRefreshHandler = new RefreshHandler(this); mDashFloats = new float[] {DASH_ON_WIDTH, DASH_OFF_WIDTH}; mPathEffects = new PathEffect[mDashMinSize]; showDashBorder(true); mTouchSlopSquare = ViewConfiguration.get(context).getScaledTouchSlop() * ViewConfiguration.get(context).getScaledTouchSlop(); getViewTreeObserver() .addOnPreDrawListener( new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { if (!isRunPreDrawListener) { return true; } isRunPreDrawListener = false; int childCount = getChildCount(); if (childCount < 1) { return false; } Rect rect = new Rect(); mChildView.getLocalVisibleRect(rect); // 计算虚线框绘制位置,注意虚线框与label之间存在margin mDashRectF.left = rect.left - margin; mDashRectF.top = rect.top - margin; mDashRectF.right = rect.right + margin; mDashRectF.bottom = rect.bottom + margin; // 计算删除按钮初始中心坐标 mDelPoint[0] = mDashRectF.left; mDelPoint[1] = mDashRectF.top; // 计算拖放按钮初始中心坐标 mDragPoint[0] = mDashRectF.right; mDragPoint[1] = mDashRectF.bottom; if (mViewMatrix.isIdentity()) { mCenterPoint[0] = rect.width() >> 1; mCenterPoint[1] = rect.height() >> 1; int halfWidth = getWidth() >> 1; int halfHeight = getHeight() >> 1; // 移动到中心 translate(halfWidth - mCenterPoint[0], halfHeight - mCenterPoint[1]); } else { int newCenterX = rect.width() >> 1; int newCenterY = rect.height() >> 1; // 根据新中心调整矩阵 mViewMatrix.reset(); mViewMatrix.postTranslate( mCenterPoint[0] - newCenterX, mCenterPoint[1] - newCenterY); mViewMatrix.postRotate(mCurRotation, mCenterPoint[0], mCenterPoint[1]); mViewMatrix.postScale( mCurScaleFactor, mCurScaleFactor, mCenterPoint[0], mCenterPoint[1]); mViewNoRotateMatrix.reset(); mViewNoRotateMatrix.postTranslate( mCenterPoint[0] - newCenterX, mCenterPoint[1] - newCenterY); mViewNoRotateMatrix.postScale( mCurScaleFactor, mCurScaleFactor, mCenterPoint[0], mCenterPoint[1]); updateBtnDrawRect(); } // 将view转换为bitmap,便于旋转、缩放时提高性能 mLabelBitmap = ImageUtils.captureScreenByDraw(mChildView); mDashPath.reset(); mDashPath.addRect(mDashRectF, Path.Direction.CCW); removeAllViews(); return true; } }); }