Android custom controls_direction control panel

Recently, the company needs to make a control panel to control the direction of the camera. It was done with a circular background and 4 circular Buttons. Colleagues may feel that it is too perfunctory, and the click effect is blocked by fingers, so he sent a small report to the leader and asked me to redo it. Since the leader said to do it, there is no reason not to do it.

Look at the effect first, the one on the left is the effect when pressed, when there is no button, the color is the same as the color on the upper, lower and right sides, the button is sensitive and easy to use

Let's see how to draw this Panel with the canvas (of course, the color can be changed)

First of all, we need to inherit a View and initialize an isTouch array for the View. This array records the direction you clicked when you click, and then when the panel is redrawn, the color of the clicked direction will be darkened;

Then initialize the brush, and some default sizes, both arrowLeft and arrowLeftDP represent the size. In order to adapt to different screen sizes, we use arrowLeftDP to record the size of the default dp size. During initialization, arrowLeftDP is converted into PX (pixels) and assigned to arrowLeft, so all the following size variables with DP and without DP are based on this principle

When initializing, we set up the touch event by the way, and we will use it later to click

public class PanelView extends View implements View.OnTouchListener {

    private final String TAG = "PanelView";
    boolean[] isTouch = new boolean[]{false,false,false,false};
    private Paint paint;
    private int smallCircleRadius = 180;
    private final int  strokeWidth = 12;
    private final int  halfStrokeWidth = strokeWidth/2;
    private int arrowLeft = -70;
    private int arrowTop = 280;
    private int arrowRight = 70;
    private int arrowBottom = 450;
    private final int smallCircleRadiusDP = 51;
    private int arrowLeftDP = -20;
    private int arrowTopDP = 80;
    private int arrowRightDP = 20;
    private int arrowBottomDP = 125;

    public PanelView(Context context) {
        super(context);
        setOnTouchListener(this);
        dpTopx(context);
    }

    public PanelView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        setOnTouchListener(this);
        dpTopx(context);
    }

    public PanelView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setOnTouchListener(this);
        dpTopx(context);
    }

    public PanelView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        setOnTouchListener(this);
        dpTopx(context);
    }

    private void dpTopx(Context context){
        Log.i(TAG,"small Circle Radius " + DensityUtil.px2dip(context,smallCircleRadius) + "\r"
        + "stroke Width " + DensityUtil.px2dip(context,strokeWidth) + "\r"
        + "arrow Left " + DensityUtil.px2dip(context,arrowLeft)+ "\r"
        + "arrow Top " + DensityUtil.px2dip(context,arrowTop)+ "\r"
        + "arrow Right " + DensityUtil.px2dip(context,arrowRight)+ "\r"
        + "arrow Bottom " + DensityUtil.px2dip(context,arrowBottom));
        smallCircleRadius = DensityUtil.dip2px(context,smallCircleRadiusDP);
        arrowLeft = DensityUtil.dip2px(context,arrowLeftDP);
        arrowTop = DensityUtil.dip2px(context,arrowTopDP);
        arrowRight = DensityUtil.dip2px(context,arrowRightDP);
        arrowBottom = DensityUtil.dip2px(context,arrowBottomDP);
    }
}

Next we have to draw the graphics of the panel, the whole drawing is in Ondraw

The first step is to draw a fan shape. When drawing a fan shape, it is necessary to judge whether it is in a click state. Depending on the state, the color of the fan shape is different when we draw it. rectF is the rectangular frame used when drawing the fan shape.

Then draw a fan-shaped border, when rectFSstroke draws a rectangle of the border, because the brush has a width, so when setting the rectangle, you need to reduce the size of the rectangle by half of the width of the brush to display the border completely.

Then rotate 90 degrees every time a sector is drawn until all 4 sectors are drawn

After drawing the fan shape, rotate the arrow and draw it on the canvas. The size and position of the arrow are drawn according to the value set when the class is initialized

Finally we complete our panel by drawing a small circle in the center

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        paint = new Paint();
        int width = getWidth();
        int height = getHeight();
        int radius = Math.min(width,height);
        paint.setAntiAlias(true);
        @SuppressLint("DrawAllocation")
        RectF rectF = new RectF((width-radius)/2.0f,
                (height-radius)/2.0f,
                width - (width-radius)/2.0f,
                height - (height-radius)/2.0f);

        @SuppressLint("DrawAllocation")
        RectF rectFStroke = new RectF((width-radius)/2.0f + halfStrokeWidth,
                (height-radius)/2.0f + halfStrokeWidth,
                width - (width-radius)/2.0f - halfStrokeWidth,
                height - (height-radius)/2.0f - halfStrokeWidth);
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(),
                R.drawable.jiantou);

        float startAngle = 45.0f;
        for(int i = 0; i< 4; i++) {
            if(isTouch[i])
                paint.setColor(0xff8a8a8a);
            else
                paint.setColor(0xffcdcdcd);
            paint.setStyle(Paint.Style.FILL);
            canvas.drawArc(rectF, startAngle, 90.0f, true, paint);

            paint.setStrokeWidth(strokeWidth);
            paint.setStyle(Paint.Style.STROKE);
            paint.setColor(0xffbfbfbf);
            canvas.drawArc(rectFStroke, startAngle, 90.0f, true, paint);
            startAngle += 90;
        }

        float bitmapStartAngle = 90f;
        Rect rectFBitmap = new Rect(0,
                0,
                bitmap.getWidth(),
                bitmap.getHeight());
        //RectF rect = new RectF(- 80,200,80,radius/2-40);
        RectF rect = new RectF(arrowLeft,arrowTop,arrowRight,arrowBottom);
        Log.i(TAG, rect.toString());
        canvas.save();
        canvas.translate(width/2.0f,height/2.0f);
        for(int i = 0; i< 4; i++){
            canvas.rotate(bitmapStartAngle);
            canvas.drawBitmap(bitmap,rectFBitmap,rect,paint);
        }
        canvas.restore();

        paint.setColor(0xffe6e6e6);
        paint.setStyle(Paint.Style.FILL);
        canvas.drawCircle(width/2.0f,height/2.0f,smallCircleRadius,paint);
        paint.setStyle(Paint.Style.STROKE);
        paint.setColor(0xffbfbfbf);
        canvas.drawCircle(width/2.0f,height/2.0f,smallCircleRadius-halfStrokeWidth,paint);
    }

At this time, the panel has no click effect, we need to add a click event to the panel

When clicking on the panel, we first judge whether it is within the circle (clicking on the small circle in the middle is not included in the range). If it is not within the valid range, the click is invalid, and then determine which area is clicked, and set the corresponding isTouch flag to true according to the clicked area, and redraw the panel. When the finger is lifted, set all isTouch to false, and redraw the panel to restore the initial state of the panel

To judge whether it is in the circle, calculate the radius according to the Pythagorean theorem, if the radius is smaller than the radius of the large circle and greater than the radius of the small circle, it is in the circle

Judging which direction is also very simple, you only need to judge whether X>Y or X<Y, because our panel is like a circle with 2 straight longitudes drawn at 45 degrees and -45 degrees, when we point our finger on the 45-degree dividing line, X==Y; but if it is a little bit off, X is either greater than Y or smaller than Y. According to this principle, we can judge the range we click

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        Log.i(TAG,"Motion Event is "+event.getAction());
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                boolean isInCircle = IsPointInCircle(event.getX(),event.getY());
                if(!isInCircle) break;
                int area = inWitchArea(event.getX(),event.getY());
                if(area == -1) break;
                isTouch[area] = true;
                invalidate();
                return true;
            case MotionEvent.ACTION_OUTSIDE:
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                isTouch[0] = false;
                isTouch[1] = false;
                isTouch[2] = false;
                isTouch[3] = false;
                invalidate();
                break;
        }
        return false;
    }

    private int inWitchArea(float x1,float y1){
        int width = getWidth();
        int height = getHeight();
        float x = width/2.0f;
        float y = height/2.0f;
        boolean XGreaterThanY = Math.abs((x - x1)) > Math.abs((y - y1));
        if(x1 > x && y1 < y){
            //第一象限
            if(XGreaterThanY)
                return 3;
            else
                return 2;
        }else if(x1 > x && y1 > y){
            if(XGreaterThanY)
                return 3;
            else
                return 0;
            //第四象限
        }else if(x1 < x && y1 > y){
            if(XGreaterThanY)
                return 1;
            else
                return 0;
            //第三象限
        }else if(x1 < x && y1 < y){
            //第二象限
            if(XGreaterThanY)
                return 1;
            else
                return 2;
        }
        return -1;
    }


    private boolean IsPointInCircle(float x1,float y1)
    {
        int width = getWidth();
        int height = getHeight();
        int radius = Math.min(width,height);
        //到圆心的距离 是否大于半径。半径是R
        //如O(x,y)点圆心,任意一点P(x1,y1) (x-x1)*(x-x1)+(y-y1)*(y-y1)>R*R 那么在圆外 反之在圆内
        float x = width/2.0f;
        float y = height/2.0f;
        float r = radius/2.0f;
        if(!((x - x1)*(x - x1) + (y - y1)*(y - y1) > smallCircleRadius*smallCircleRadius))
            return false;
        else if (!((x - x1)*(x - x1) + (y - y1)*(y - y1) > r*r))
            return true;        //当前点在圆内
        else
            return false;       //当前点在圆外
    }

Now that our panel is finished, you can happily refer to it in your layout file. If you find the article useful, please give it a thumbs up

Guess you like

Origin blog.csdn.net/baoolong/article/details/125083266