Android中如何识别对不规则图形的点击命中

2,518 阅读2分钟

最近在一些微信技术群里,连续两次看到类似 "如何判断图形中的点击落点" 性质的问题。并且第二次遇到这个问题的同学,需求上是个不规则的图形,没有找到合适的解决办法,当时在地铁上手头没有电脑,简单查了一下,没有进行验证。

今天放假回家下高铁在酒店住一晚,睡不着正好想起了这个问题,于是动手验证了一下,在此记录一下,供有类似需求的同学参考。

先上效果:

  • 实现代码
package com.example.rectdemo;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.graphics.Region;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import androidx.annotation.Nullable;

public class CanvasView extends View {

    Paint mPaint = new Paint();
    Path path = new Path();

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

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

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

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


    private void init() {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setColor(0xfff00000);
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setTextSize(36);
        mPaint.setStrokeWidth(5);
        setPointCheck();
    }


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


    public void drawable(Canvas canvas){
        path.moveTo(10, 10); //移动到 坐标10,10
        path.lineTo(100, 200);
        path.lineTo(200,40);
        path.lineTo(300, 20);
        path.lineTo(200, 10);
        path.lineTo(100, 70);
        path.lineTo(50, 40);
        path.close();
        canvas.drawPath(path,mPaint);
    }

    public void setPointCheck(){
        this.setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (event.getAction() == MotionEvent.ACTION_DOWN) {
                    System.out.println("点击了:" + event.getX() + "," + event.getY() +", 点击落点在Path内:"+isInPath((int)event.getX(),(int)event.getY()));
                }
                return false;
            }
        });
    }

    //判断核心代码
    public boolean isInPath(int x,int y){
        RectF rectF = new RectF();
        path.computeBounds(rectF, true);
        Region region = new Region();
        region.setPath(path, new Region((int) rectF.left, (int) rectF.top, (int) rectF.right, (int) rectF.bottom));
        return region.contains(x,y);
    }

}

当我们碰到一个不规则的图形,并想要知道点击事件是否在这个不规则图形内的时候,实际上是想知道点击的坐标点是否被构成不规则图形的坐标点的集合所包含。

实现这一目标,最关键的代码其实是 Path.computeBounds(RectF,Boolean) 这个API,根据官网的API文档介绍,大概意思是将 Path 的坐标点边界存储到 RectF 中。

再使用 Region.setPath(Path path, Region clip)Path 和 上面得到的 RectF 相结合得到一个 Region

这时 Region 是有 contains(x,y) 的相关 API ,可以很方便的完成对坐标点是否命中的判断。从而简单快捷的完成群里同学提出的需求。

掌握了这个方法,基本可以覆盖所有不规则图形的点击事件命中检查。