package com.lechange.demo.view; import android.annotation.SuppressLint; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.PaintFlagsDrawFilter; import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import com.lechange.demo.R; /** * 云台控制面板 */ @SuppressLint("DrawAllocation") public class LcCloudRudderView extends View { private Point mRockerPosition; private final Point mCtrlPoint = new Point(100, 100); private float mSolidRudderRadius = 60; private float mChangeRudderRadius = 60; private int mWheelRadius = 100; private RudderListener listener = null; private int mDownState; private int firstX, firstY; private int dCtrlPointx, dCtrlPointy; private static Bitmap mBigCircleBg; private static Bitmap mBigCircleBg_h; private static Bitmap mSmallCircleBg; private static Bitmap mBigCircleLandScapeBg; private static Bitmap mBigCircleLandScapeBg_h; private static Bitmap mSmallCircleLandScapeBg; private float mAngle; private boolean mShowDirectPic; private boolean mCanTouch = true; private boolean mLandScapeOnly = false; //支持四方向 private boolean mSupportFourDirection=true; public LcCloudRudderView(Context context) { super(context); init(); } public LcCloudRudderView(Context context, AttributeSet attrs) { super(context, attrs); setAttribute(context, attrs); init(); } private void setAttribute(Context context, AttributeSet attrs) { if (attrs != null) { TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.LcCloudRudderView); mLandScapeOnly = array.getBoolean(R.styleable.LcCloudRudderView_landscape_only, false); array.recycle(); } } private void init() { setBackgroundColor(Color.TRANSPARENT); this.setKeepScreenOn(true); mCanTouch = true; Paint mPaint = new Paint(); mPaint.setColor(Color.GREEN); mPaint.setAntiAlias(true); mRockerPosition = new Point(mCtrlPoint); setFocusable(true); setFocusableInTouchMode(true); if (mBigCircleBg == null) { mBigCircleBg = BitmapFactory.decodeResource(getResources(), R.mipmap.play_module_cloudstage_direction_default); } if (mBigCircleBg_h == null) { mBigCircleBg_h = BitmapFactory.decodeResource(getResources(), R.mipmap.play_module_cloudstage_direction_up); } if (mSmallCircleBg == null) { mSmallCircleBg = BitmapFactory.decodeResource(getResources(), R.mipmap.play_module_cloudstage_direction_button); } if (mBigCircleLandScapeBg == null) { mBigCircleLandScapeBg = BitmapFactory.decodeResource(getResources(), R.mipmap.play_module_video_cloudstage_direction_default); } if (mBigCircleLandScapeBg_h == null) { mBigCircleLandScapeBg_h = BitmapFactory.decodeResource(getResources(), R.mipmap.play_module_video_cloudstage_direction_up); } if (mSmallCircleLandScapeBg == null) { mSmallCircleLandScapeBg = BitmapFactory.decodeResource(getResources(), R.mipmap.play_module_video_cloudstage_direction_button); } mSolidRudderRadius = (float) (mSmallCircleBg.getHeight() / 2.0); } /** * 重置云台背景图,为了区分4方向和8方向 */ public void resetCircleBg() { if (mSupportFourDirection) { if (mBigCircleBg != null) { mBigCircleBg.recycle(); mBigCircleBg = BitmapFactory.decodeResource(getResources(), R.mipmap.play_module_cloudstage_direction_default_four); } if (mBigCircleLandScapeBg != null) { mBigCircleLandScapeBg.recycle(); mBigCircleLandScapeBg = BitmapFactory.decodeResource(getResources(), R.mipmap.play_module_video_cloudstage_direction_default_four); } } else { if (mBigCircleBg != null) { mBigCircleBg.recycle(); mBigCircleBg = BitmapFactory.decodeResource(getResources(), R.mipmap.play_module_cloudstage_direction_default); } if (mBigCircleLandScapeBg != null) { mBigCircleLandScapeBg.recycle(); mBigCircleLandScapeBg = BitmapFactory.decodeResource(getResources(), R.mipmap.play_module_video_cloudstage_direction_default); } } invalidate(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Bitmap bigCirclebg = mLandScapeOnly ? mBigCircleLandScapeBg : mBigCircleBg; if (bigCirclebg == null || bigCirclebg.isRecycled()) { return; } try { canvas.save(); canvas.translate(mCtrlPoint.x, mCtrlPoint.y); canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG)); Paint paint = new Paint(); paint.setAntiAlias(true); Rect srcRect = new Rect(0, 0, bigCirclebg.getWidth(), bigCirclebg.getHeight()); RectF dstRcctF = new RectF(-mWheelRadius, -mWheelRadius, mWheelRadius, mWheelRadius); canvas.drawBitmap(bigCirclebg, srcRect, dstRcctF, paint); canvas.restore(); if (mShowDirectPic) { Bitmap bigCircleBg_h = mLandScapeOnly ? mBigCircleLandScapeBg_h : mBigCircleBg_h; canvas.save(); canvas.translate(mCtrlPoint.x, mCtrlPoint.y); canvas.rotate(mAngle); Rect srcRect2 = new Rect(0, 0, bigCircleBg_h.getWidth(), bigCircleBg_h.getHeight()); RectF dstRcctF2 = new RectF(-mWheelRadius, -mWheelRadius, mWheelRadius, mWheelRadius); canvas.drawBitmap(bigCircleBg_h, srcRect2, dstRcctF2, paint); canvas.restore(); } Bitmap smallCircleBg = mLandScapeOnly ? mSmallCircleLandScapeBg : mSmallCircleBg; Rect srcRect3 = new Rect(0, 0, smallCircleBg.getWidth(), smallCircleBg.getHeight()); RectF dstRcctF3 = new RectF(mRockerPosition.x - mChangeRudderRadius, mRockerPosition.y - mChangeRudderRadius, mRockerPosition.x + mChangeRudderRadius, mRockerPosition.y + mChangeRudderRadius); canvas.drawBitmap(smallCircleBg, srcRect3, dstRcctF3, paint); } catch (Exception e) { e.printStackTrace(); } finally { } } public void setRudderListener(RudderListener rockerListener) { listener = rockerListener; } public boolean isSupportFourDirection() { return mSupportFourDirection; } /** * 设置是否支持四方向云台 * * @param supportFourDirection */ public void setSupportFourDirection(boolean supportFourDirection) { this.mSupportFourDirection = supportFourDirection; resetCircleBg(); } @Override public boolean onTouchEvent(MotionEvent event) { if (!mCanTouch) { return true; } int len = (int) Math.sqrt(Math.pow(mCtrlPoint.x - event.getX(), 2) + Math.pow(mCtrlPoint.y - event.getY(), 2)); switch (event.getAction() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: if (len < mWheelRadius / 2) { if (listener != null) { listener.onSteeringWheelChangedBegin(); } mDownState = 1; firstX = (int) event.getX(); firstY = (int) event.getY(); dCtrlPointx = firstX - mCtrlPoint.x; dCtrlPointy = firstY - mCtrlPoint.y; } else if (len >= (mWheelRadius / 2) && len <= mWheelRadius) { if (listener != null) { listener.onSteeringWheelChangedBegin(); } mDownState = 2; mShowDirectPic = true; firstX = (int) event.getX(); firstY = (int) event.getY(); dCtrlPointx = firstX - mCtrlPoint.x; dCtrlPointy = firstY - mCtrlPoint.y; float radian = getRadian(mCtrlPoint, new Point((int) event.getX(), (int) event.getY())); int angle = LcCloudRudderView.this.getAngleConvert(radian); Direction direction = directConvert(angle); if (listener != null) { listener.onSteeringWheelChangedSingle(direction); } } invalidate(); break; case MotionEvent.ACTION_MOVE: if (mDownState == 1) { int dragLen = (int) Math.sqrt(Math.pow(firstX - event.getX(), 2) + Math.pow(firstY - event.getY(), 2)); if (dragLen <= mWheelRadius - mChangeRudderRadius / 2) { mRockerPosition.set((int) event.getX() - dCtrlPointx, (int) event.getY() - dCtrlPointy); } else { int cutRadius = (int) (mWheelRadius - mChangeRudderRadius / 2); float radian = getRadian(mCtrlPoint, new Point((int) event.getX() - (firstX - mCtrlPoint.x), (int) event.getY() - (firstY - mCtrlPoint.y))); mRockerPosition = new Point(mCtrlPoint.x + (int) (cutRadius * Math.cos(radian)), mCtrlPoint.y + (int) (cutRadius * Math.sin(radian))); } float radian = getRadian(mCtrlPoint, new Point((int) event.getX(), (int) event.getY())); int angle = LcCloudRudderView.this.getAngleConvert(radian); Direction direction = directConvert(angle); if (dragLen >= (mWheelRadius / 2)) {//超过 边界再发送命令 mShowDirectPic = true; if (listener != null) { listener.onSteeringWheelChangedContinue(direction); } } else {//未超过边界,发送停止命令,图标不显示按下 mShowDirectPic = false; if (listener != null) { listener.onSteeringWheelChangedContinue(null); } } } else if (mDownState == 2) { if (listener != null) { if (len >= (mWheelRadius / 2) && len <= mWheelRadius) { mShowDirectPic = true; float radian = getRadian(mCtrlPoint, new Point((int) event.getX(), (int) event.getY())); int angle = LcCloudRudderView.this.getAngleConvert(radian); directConvert(angle); } else { mShowDirectPic = false; } } } invalidate(); break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: if (mDownState == 1) { mShowDirectPic = false; mRockerPosition = new Point(mCtrlPoint); if (listener != null) { listener.onSteeringWheelChangedContinue(null); listener.onSteeringWheelChangedEnd(); } } else if (mDownState == 2) { mShowDirectPic = false; float radian = getRadian(mCtrlPoint, new Point((int) event.getX(), (int) event.getY())); int angle = LcCloudRudderView.this.getAngleConvert(radian); Direction direction = directConvert(angle); mRockerPosition = new Point(mCtrlPoint); if (listener != null) { //listener.onSteeringWheelChangedSingle(direction); listener.onSteeringWheelChangedEnd(); } } firstX = 0; firstY = 0; mDownState = 0; invalidate(); break; default: break; } return true; } private static float getRadian(Point a, Point b) { float lenA = b.x - a.x; float lenB = b.y - a.y; float lenC = (float) Math.sqrt(lenA * lenA + lenB * lenB); float ang = (float) Math.acos(lenA / lenC); ang = ang * (b.y < a.y ? -1 : 1); return ang; } private Direction directConvert(int angle) { Direction direction = Direction.Left; if (mSupportFourDirection) { if (angle <= 45 || angle > 315) { direction = Direction.Right; mAngle = 90; } else if (angle > 45 && angle <= 135) { direction = Direction.Up; mAngle = 0; } else if (angle > 135 && angle <= 225) { direction = Direction.Left; mAngle = 270; } else if (angle > 225 && angle <= 315) { direction = Direction.Down; mAngle = 180; } } else { if (angle <= 22.5 || angle > 337.5) { direction = Direction.Right; mAngle = 90; } else if (angle > 22.5 && angle <= 67.5) { direction = Direction.Right_up; mAngle = 45; } else if (angle > 67.5 && angle <= 112.5) { direction = Direction.Up; mAngle = 0; } else if (angle > 112.5 && angle <= 157.5) { direction = Direction.Left_up; mAngle = 315; } else if (angle > 157.5 && angle <= 202.5) { direction = Direction.Left; mAngle = 270; } else if (angle > 202.5 && angle <= 247.5) { direction = Direction.Left_down; mAngle = 225; } else if (angle > 247.5 && angle <= 292.5) { direction = Direction.Down; mAngle = 180; } else if (angle > 292.5 && angle <= 337.5) { direction = Direction.Right_down; mAngle = 135; } } return direction; } private int getAngleConvert(float radian) { int tmp = (int) Math.round(radian / Math.PI * 180); if (tmp < 0) { return -tmp; } else { return 180 + (180 - tmp); } } public interface RudderListener { void onSteeringWheelChangedBegin(); void onSteeringWheelChangedContinue(Direction direction); void onSteeringWheelChangedSingle(Direction direction); void onSteeringWheelChangedEnd(); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); int len = Math.min(w, h) / 2; mWheelRadius = (int) (len - mSolidRudderRadius / 2); mChangeRudderRadius = mWheelRadius * 17 / 40; mCtrlPoint.set(w / 2, h / 2); mRockerPosition.set(w / 2, h / 2); postInvalidate(); } public void enable(boolean enabled) { if (mCanTouch && !enabled) { mShowDirectPic = false; mRockerPosition = new Point(mCtrlPoint); invalidate(); if (listener != null) { listener.onSteeringWheelChangedContinue(null); listener.onSteeringWheelChangedEnd(); } } setEnabled(enabled); setAlpha(enabled ? 1f : 0.5f); mCanTouch = enabled; } public void unit() { Bitmap bigCirclebg = mLandScapeOnly ? mBigCircleLandScapeBg : mBigCircleBg; Bitmap bigCircleBg_h = mLandScapeOnly ? mBigCircleLandScapeBg_h : mBigCircleBg_h; Bitmap smallCircleBg = mLandScapeOnly ? mSmallCircleLandScapeBg : mSmallCircleBg; if (bigCirclebg != null) { bigCirclebg.recycle(); if (mLandScapeOnly) { mBigCircleLandScapeBg = null; } else { mBigCircleBg = null; } } if (bigCircleBg_h != null) { bigCircleBg_h.recycle(); if (mLandScapeOnly) { mBigCircleLandScapeBg_h = null; } else { mBigCircleBg_h = null; } } if (smallCircleBg != null) { smallCircleBg.recycle(); if (mLandScapeOnly) { mSmallCircleLandScapeBg = null; } else { mSmallCircleBg = null; } } listener = null; } }