Ptzview – Custom PTZ control

Time:2021-1-21

Renderings and related two picture resources:

Implementation steps:

  1. Inherit view
  2. Rewrite ontouchevent to calculate the angle according to the touch coordinates
  3. Rewrite OnDraw to rotate and draw pictures according to the angle

The code is as follows:

1 import android.annotation.SuppressLint;
  2 import android.content.Context;
  3 import android.content.res.Resources;
  4 import android.graphics.Bitmap;
  5 import android.graphics.BitmapFactory;
  6 import android.graphics.Canvas;
  7 import android.graphics.Matrix;
  8 import android.graphics.Paint;
  9 import android.util.AttributeSet;
 10 import android.view.MotionEvent;
 11 import android.view.View;
 12 
 13 import androidx.annotation.IntDef;
 14 
 15 /**
 16 * Custom PTZ control
 17  * 2020-06-02: create by zenghm
 18  */
 19 public class PTZView extends View {
 20 
 21     public static final int NONE = 0;
 22     public static final int TOP = 1;
 23     public static final int BOTTOM = 2;
 24     public static final int LEFT = 3;
 25     public static final int RIGHT = 4;
 26 
 27     @IntDef({NONE, LEFT, TOP, RIGHT, BOTTOM})
 28     public @interface Direction {
 29     }
 30 
 31     public interface DirectionChangedListener {
 32         /**
 33 * direction change callback
 34          *
 35 * @ param olddirection
 36 * @ param curdirection current direction
 37          */
 38         void onDirectionChanged(PTZView view, @Direction int oldDirection, @Direction int curDirection);
 39     }
 40 
 41     private DirectionChangedListener mListener;
 42     @Direction
 43     private int mDirection = NONE;
 44     private float mCenterX, mCenterY;
 45     private float mAngel = 0;
 46 
 47     private Bitmap mDefaultBitmap;
 48     private Bitmap mPressBitmap;
 49     private Matrix mMatrix = new Matrix();
 50     private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
 51 
 52     public PTZView(Context context) {
 53         this(context, null, 0);
 54     }
 55 
 56     public PTZView(Context context, AttributeSet attrs) {
 57         this(context, attrs, 0);
 58     }
 59 
 60     public PTZView(Context context, AttributeSet attrs, int defStyle) {
 61         super(context, attrs, defStyle);
 62 // loading resources
 63         Resources res = context.getResources();
 64         mDefaultBitmap = BitmapFactory.decodeResource(res, R.drawable.ptz_default);
 65         mPressBitmap = BitmapFactory.decodeResource(res, R.drawable.ptz_press);
 66     }
 67 
 68     public void setOnDirectionChangedListener(DirectionChangedListener listener) {
 69         mListener = listener;
 70     }
 71 
 72     @Override
 73     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
 74         super.onSizeChanged(w, h, oldw, oldh);
 75         mCenterX = (float) w / 2;
 76         mCenterY = (float) h / 2;
 77     }
 78 
 79     @Override
 80     protected void onDraw(Canvas canvas) {
 81         super.onDraw(canvas);
 82         Bitmap bitmap = isPressed() ? mPressBitmap : mDefaultBitmap;
 83 // calculate image scaling
 84         float sx = (float) getWidth() / bitmap.getWidth();
 85         float sy = (float) getHeight() / bitmap.getHeight();
 86 // set transformation matrix (zoom, rotate)
 87         mMatrix.reset();
 88         mMatrix.postScale(sx, sy);
 89         mMatrix.postRotate(-mAngel, mCenterX, mCenterY);
 90 // draw picture
 91         canvas.drawBitmap(bitmap, mMatrix, mPaint);
 92     }
 93 
 94     @SuppressLint("ClickableViewAccessibility")
 95     @Override
 96     public boolean onTouchEvent(MotionEvent event) {
 97         //super.onTouchEvent(event);
 98         if (!isEnabled())
 99             return false;
100 
101         switch (event.getAction()) {
102             case MotionEvent.ACTION_DOWN:
103                 setPressed(true);
104                 postInvalidate();
105                 break;
106 
107             case MotionEvent.ACTION_UP:
108             case MotionEvent.ACTION_CANCEL:
109                 if (mListener != null) {
110                     mListener.onDirectionChanged(this, mDirection, NONE);
111                 }
112                 mDirection = NONE;
113                 mAngel = 0;
114                 setPressed(false);
115                 postInvalidate();
116                 break;
117 
118             case MotionEvent.ACTION_MOVE:
119 // calculation angle
120                 float x = event.getX() - mCenterX;
121                 float y = mCenterY - event.getY();
122                 mAngel = (float) (Math.atan2(y, x) / Math.PI * 180);
123 // redraw
124                 postInvalidate();
125 // judge the direction according to the angle
126                 int direction;
127                 if (mAngel > -45 && mAngel <= 45) {
128                     direction = LEFT;
129                 } else if (mAngel > 45 && mAngel <= 135) {
130                     direction = TOP;
131                 } else if (mAngel <= -45 && mAngel > -135) {
132                     direction = BOTTOM;
133                 } else {
134                     direction = RIGHT;
135                 }
136 // change of callback direction
137                 if (mListener != null && mDirection != direction) {
138                     mListener.onDirectionChanged(this, mDirection, direction);
139                 }
140                 mDirection = direction;
141                 break;
142 
143             default:
144                 break;
145         }
146         return true;
147     }
148 }

[note] images can be imported through custom attributes to enhance the control’s custom ability. Custom properties are beyond the scope of this article.

Thank you for reading. If you have any mistakes, thank you for correcting them.

Recommended Today

SQL exercise 20 – Modeling & Reporting

This blog is used to review and sort out the common topic modeling architecture, analysis oriented architecture and integration topic reports in data warehouse. I have uploaded these reports to GitHub. If you are interested, you can have a lookAddress:https://github.com/nino-laiqiu/TiTanI recorded a relatively complete development process in my hexo blog deployed on GitHub. You can […]