Android自定义View实现方向控制控件,可拖拽中间圆

有朋自远方来,不亦乐乎。

如果本篇文章对您有帮助,请动动您的小手点赞、评论、收藏。

 话不多少,先上效果图。

主要功能

1. 扁平化的控件样式。

2. 实现上、下、左、右点击功能,并实现拖拽中间圆形进行八个方向的控制。

实现思路

1. 由于此功能基于自定义View实现,因此先实现View类,重写onSeasure、onDraw函数。

2. 在onSeasure函数中,计算此view的宽高,后面基于此宽高计算控件中其他元素的宽高和位置信息。

3. 由于自定义View需使用Canvas绘制控件的所有元素,因此,提前定义好不同元素所使用的画笔。

4. 由于是方向控件,需要实现按钮的点击事件和中间圆的拖拽事件,在自定义View中,是使用onTouchListener实现的,在onTouch事件回调函数中,处理不同功能的操作逻辑。

5. 在处理点击事件时,根据用户点击的部位判断点击的是哪个按钮,如果点击的是中间,则可以拖拽。处理好点击事件后,调用invalidate()进行重绘。

6. 最后,在onDraw函数中重绘控件的相关部位。

实现代码见下面,复制可用,有缺失的类是我自己的业务逻辑,删除即可。

public class DirectionView extends View implements View.OnTouchListener {
    private int width;
    private int height;
    private int halfWidth;
    private int halfHeight;
    private int smallCircleRadius;

    private int sideWidth = 4;//外层边框宽度

    private Paint paintCircleSide;
    private Paint paintCircleBg;
    private Paint paintCircleCenter;
    private Paint paintDirection;

    private int pressDirectionH = 0;
    private int pressDirectionV = 0;

    private int smallCircleCenterX = -1;
    private int smallCircleCenterY = -1;
    private int halfTouchWidth = 65;

    private BlurMaskFilter blurMaskFilter;
    private Path path = new Path();

    public DirectionView(Context context) {
        super(context);
        init(context);
    }

    public DirectionView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public DirectionView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    private void init(Context context)
    {
        paintCircleSide = createPaintWithCommon(sideWidth, "#66ffffff", Paint.Style.STROKE);
        paintCircleBg = createPaintWithCommon(2, "#662a3845", Paint.Style.FILL);
        paintCircleCenter = createPaintWithCommon(2, "#ffffff", Paint.Style.FILL);
        paintDirection = createPaintWithCommon(6, "#ffffff", Paint.Style.STROKE);

        blurMaskFilter = new BlurMaskFilter(20, BlurMaskFilter.Blur.SOLID);

        this.setOnTouchListener(this);
    }

    private Paint createPaintWithCommon(int strokeWidth, String color, Paint.Style style)
    {
        Paint paint = new Paint();
        paint.setStrokeWidth(strokeWidth);
        paint.setColor(Color.parseColor(color));
        paint.setStyle(style);
        paint.setAntiAlias(true);
        paint.setStrokeJoin(Paint.Join.ROUND);
        paint.setStrokeCap(Paint.Cap.ROUND);
        return paint;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        canvas.drawCircle(halfWidth, halfHeight, halfWidth - sideWidth / 2, paintCircleBg);
        canvas.drawCircle(halfWidth, halfHeight, halfWidth - sideWidth, paintCircleSide);

        if(this.smallCircleCenterY != -1)
        {
//            paintCircleCenter.setMaskFilter(blurMaskFilter);
            canvas.drawCircle(smallCircleCenterX, smallCircleCenterY, smallCircleRadius, paintCircleCenter);
        }
        else
        {
//            paintCircleCenter.setMaskFilter(null);
            canvas.drawCircle(halfWidth, halfHeight, smallCircleRadius, paintCircleCenter);
        }

        int offsetSize = 25;
        int offsetPosition = 20;

        //上
        if(pressDirectionV == PtzAction.Direction.Up)
            paintDirection.setColor(Color.parseColor("#0000ff"));
        else
            paintDirection.setColor(Color.parseColor("#ffffff"));

        path.reset();
        path.moveTo(halfWidth - offsetSize, halfHeight / 2 - offsetPosition);
        path.lineTo(halfWidth, halfHeight / 2 - offsetSize - offsetPosition);
        path.lineTo(halfWidth + offsetSize, halfHeight / 2 - offsetPosition);
        canvas.drawPath(path, paintDirection);

        //右
        if(pressDirectionH == PtzAction.Direction.Right)
            paintDirection.setColor(Color.parseColor("#0000ff"));
        else
            paintDirection.setColor(Color.parseColor("#ffffff"));

        path.reset();
        path.moveTo(halfWidth + halfWidth / 2 + offsetPosition, halfHeight - offsetSize);
        path.lineTo(halfWidth + halfWidth / 2 + offsetPosition + offsetSize, halfHeight);
        path.lineTo(halfWidth + halfWidth / 2 + offsetPosition, halfHeight + offsetSize);
        canvas.drawPath(path, paintDirection);

        //下
        if(pressDirectionV == PtzAction.Direction.Down)
            paintDirection.setColor(Color.parseColor("#0000ff"));
        else
            paintDirection.setColor(Color.parseColor("#ffffff"));

        path.reset();
        path.moveTo(halfWidth - offsetSize, halfHeight + halfHeight / 2 + offsetPosition);
        path.lineTo(halfWidth, halfHeight + halfHeight / 2 + offsetSize + offsetPosition);
        path.lineTo(halfWidth + offsetSize, halfHeight + halfHeight / 2 + offsetPosition);
        canvas.drawPath(path, paintDirection);

        //左
        if(pressDirectionH == PtzAction.Direction.Left)
            paintDirection.setColor(Color.parseColor("#0000ff"));
        else
            paintDirection.setColor(Color.parseColor("#ffffff"));

        path.reset();
        path.moveTo(halfWidth / 2 - offsetPosition, halfHeight - offsetSize);
        path.lineTo(halfWidth / 2 - offsetPosition - offsetSize, halfHeight);
        path.lineTo(halfWidth / 2 - offsetPosition, halfHeight + offsetSize);
        canvas.drawPath(path, paintDirection);
    }

    @Override
    public boolean onTouch(View v, MotionEvent event)
    {
        try
        {
        	int action = event.getAction();
        	int x = (int) event.getX();
        	int y = (int) event.getY();

        	if(action == MotionEvent.ACTION_DOWN)
        	{
        	    //判断按下的是不是中间
        	    if(x > halfWidth - smallCircleRadius && x < halfWidth + smallCircleRadius && y > halfHeight - smallCircleRadius && y < halfHeight + smallCircleRadius)
        	    {
        	        this.smallCircleCenterX = x;
        	        this.smallCircleCenterY = y;
        	        return true;
        	    }

                int offset = 30;
                if(x > halfWidth - halfTouchWidth && x < halfWidth + halfTouchWidth)
                {
                    if(y > halfHeight / 2 - halfTouchWidth - offset && y < halfHeight / 2 + halfTouchWidth - offset)
                    {
                        setPtzDirection(PtzAction.Direction.None, PtzAction.Direction.Up);
                    }
                    else if (y > halfHeight + halfHeight / 2 + offset - halfTouchWidth && y < halfHeight + halfHeight / 2 + offset + halfTouchWidth)
                    {
                        setPtzDirection(PtzAction.Direction.None, PtzAction.Direction.Down);
                    }
                }

                if(y > halfHeight - halfTouchWidth && y < halfHeight + halfTouchWidth)
                {
                    if(x > halfWidth / 2 - halfTouchWidth - offset && x < halfWidth / 2 + halfTouchWidth - offset)
                    {
                        setPtzDirection(PtzAction.Direction.Left, PtzAction.Direction.None);
                    }
                    else if(x > halfWidth + halfWidth / 2 + offset - halfTouchWidth && x < halfWidth + halfWidth / 2 + offset + halfTouchWidth)
                    {
                        setPtzDirection(PtzAction.Direction.Right, PtzAction.Direction.None);
                    }
                }

            }
        	else if (action == MotionEvent.ACTION_MOVE)
            {
                //说明按下的是中间小圆
                if(this.smallCircleCenterX != -1)
                {
                    //小圆拖动时圆心所在的圆的最大半径
                    int maxRadius = halfHeight - smallCircleRadius - 35;
                    int minRadius = 70;

                    int deltaX = x - halfWidth;
                    int deltaY = y - halfHeight;

                    int length = (int) Math.hypot(deltaX, deltaY);

                    if(length > maxRadius)
                    {
//                        LOGUtil.d("在圆外");
                        this.smallCircleCenterX = halfWidth + maxRadius * deltaX / length;
                        this.smallCircleCenterY = halfHeight + maxRadius * deltaY / length;
                    }
                    else
                    {
//                        LOGUtil.d("在圆内");
                        this.smallCircleCenterX = x;
                        this.smallCircleCenterY = y;

                        if(length < minRadius)
                        {
                            setPtzDirection(PtzAction.Direction.None, PtzAction.Direction.None);
                            invalidate();
                            return true;
                        }
                    }

                    double atan = Math.atan((this.smallCircleCenterY - halfHeight) * 1.0 / (this.smallCircleCenterX - halfWidth));
                    double degree = Math.toDegrees(atan);
                    if(this.smallCircleCenterX < halfWidth)
                    {
                        if(degree > 60)
                            setPtzDirection(PtzAction.Direction.None, PtzAction.Direction.Up);
                        else if(degree > 30)
                            setPtzDirection(PtzAction.Direction.Left, PtzAction.Direction.Up);
                        else if(degree > -30)
                            setPtzDirection(PtzAction.Direction.Left, PtzAction.Direction.None);
                        else if(degree > -60)
                            setPtzDirection(PtzAction.Direction.Left, PtzAction.Direction.Down);
                        else
                            setPtzDirection(PtzAction.Direction.None, PtzAction.Direction.Down);
                    }
                    else
                    {
                        if(degree > 60)
                            setPtzDirection(PtzAction.Direction.None, PtzAction.Direction.Down);
                        else if(degree > 30)
                            setPtzDirection(PtzAction.Direction.Right, PtzAction.Direction.Down);
                        else if(degree > -30)
                            setPtzDirection(PtzAction.Direction.Right, PtzAction.Direction.None);
                        else if(degree > -60)
                            setPtzDirection(PtzAction.Direction.Right, PtzAction.Direction.Up);
                        else
                            setPtzDirection(PtzAction.Direction.None, PtzAction.Direction.Up);
                    }

//                    LOGUtil.d("atan = " + atan + ", degree = " + Math.toDegrees(atan));
                }
            }
        	else if(action == MotionEvent.ACTION_UP)
        	{
                setPtzDirection(PtzAction.Direction.None, PtzAction.Direction.None);
                this.smallCircleCenterX = -1;
                this.smallCircleCenterY = -1;
        	}

        	invalidate();
        }
        catch (Exception e)
        {
        	LOGUtil.e(e.getMessage());
        }

        //一定要return true, 否则UP事件不会被监听到。
        return true;
    }

    private void setPtzDirection(int directionH, int directionV)
    {
        PtzAction ptzAction = ApplicationContext.getInstance().getPtzAction();
        ptzAction.setDirectionH(directionH);
        ptzAction.setDirectionV(directionV);
        this.pressDirectionH =  directionH;
        this.pressDirectionV= directionV;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(widthMeasureSpec);

        width = getSize(widthMode, widthMeasureSpec);
        height = getSize(heightMode, heightMeasureSpec);
        halfWidth = width / 2;
        halfHeight = height / 2;
        smallCircleRadius = halfWidth / 4;
    }

    private int getSize(int mode, int sizeMeasureSpec)
    {
        if(mode == MeasureSpec.EXACTLY || mode == MeasureSpec.AT_MOST)
            return MeasureSpec.getSize(sizeMeasureSpec);
        else if (mode == MeasureSpec.UNSPECIFIED)
            return sizeMeasureSpec;
        else
            return 0;
    }
}

猜你喜欢

转载自blog.csdn.net/qq_34215018/article/details/127467686