自定義view的觸摸點擊區(qū)域

先看下實現(xiàn)效果


device-2018-05-18-111253.gif

對于自定義view不規(guī)則區(qū)域的觸摸事件點擊響應(yīng)涉及的知識點
1.Region
2.Matrix坐標(biāo)系變換
拓展知識點:3.Path繪圖

Region

Region是繪制中的區(qū)域,是一個閉合的區(qū)域。使用Region可以取區(qū)域的交集或者并集最后得到我們的不規(guī)則區(qū)域。
Region的構(gòu)造方法有

public Region()  
public Region(Region region) 
public Region(Rect r)  
public Region(int left, int top, int right, int bottom) 

Region也可以后期添加區(qū)域

public void setEmpty()  //清空
public boolean set(Region region)   
public boolean set(Rect r)   
public boolean set(int left, int top, int right, int bottom)   
public boolean setPath(Path path, Region clip)//將path和clip的兩個區(qū)域取交集

對于上述的不規(guī)則區(qū)域,我們可以取一張屏幕畫布和該不規(guī)則區(qū)域的path取交集,這樣就能得到我們想要獲得的區(qū)域了。

circleRegion = new Region();
path = new Path();
//Path.Direction.CW---順時針  Path.Direction.CCW逆時針
path.addCircle(width / 2, height / 2, 250, Path.Direction.CW);

Region region = new Region(0, 0, width, height);
circleRegion.setPath(path, region);

可以使用

//繪制
RegionIterator iterator = new RegionIterator(region);
Rect rect = new Rect();
while (iterator.next(rect)) {
    canvas.drawRect(rect, mPaint);
}

遍歷我們得到的region區(qū)域。
知道了region概念后,我們可以對一個圓形圖案的觸摸坐標(biāo)判斷是否在圓形區(qū)域內(nèi)。

image.png
public class AbsoluteMap extends View {

    private Paint paint;
    private int width, height;
    private Path path;
    private Region circleRegion;

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

    private void init() {
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(Color.BLUE);
        paint.setStrokeWidth(10);


        circleRegion = new Region();
        path = new Path();
        //Path.Direction.CW---順時針  Path.Direction.CCW逆時針
        path.addCircle(width / 2, height / 2, 250, Path.Direction.CW);

        Region region = new Region(0, 0, width, height);
        circleRegion.setPath(path, region);

    }


    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        width = w;
        height = h;
        init();

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawCircle(width / 2, height / 2, 250, paint);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if (circleRegion.contains((int) event.getX(), (int) event.getY())) {
                    Toast.makeText(getContext(), "觸摸區(qū)域內(nèi)", Toast.LENGTH_LONG).show();
                }
                break;
        }
        return true;

    }
}

可以看到,我們?nèi)〕鰣A形region后,再對用戶的觸摸坐標(biāo)getx,gety判斷是否在區(qū)域內(nèi)即可。

2.Matrix

image.png

從第一個例子可以看到我們用的是相對坐標(biāo)系getx,gety來確定用戶的觸摸區(qū)域的,這樣就存在一個問題,有時候開發(fā)者為了方便或者繪制的圖形是對稱圖形時,我們會將坐標(biāo)系平移,做出變換,這時候getx,gety就會發(fā)生變化。
所以一般我們會使用getRawX,getRawY來獲取用戶坐標(biāo),但是怎么把用戶的絕對坐標(biāo)轉(zhuǎn)換成我們的畫布坐標(biāo)呢?
這里就需要了解一下Matrix了。


image.png

mapPoints mapRect mapVectors
這些API主要是根據(jù)當(dāng)前Matrix矩陣對點、矩形區(qū)域和向量進(jìn)行變換,以得到變換后的點、矩形區(qū)域和向量。經(jīng)常和下面的invert方法結(jié)合使用。

invert
通過上面的mapXXX方法,可以獲取變換后的坐標(biāo)或者矩形。但假設(shè)我們知道了變換后的坐標(biāo),如何計算Matrix變換前的坐標(biāo)那?!
此時通過invert方法獲取的逆矩陣就派上用場了。所謂逆矩陣,就是Matrix旋轉(zhuǎn)了30度,逆Matrix就反向旋轉(zhuǎn)30度,Matrix放大n倍,逆Matrix就縮小n倍。

public class ChangeMap extends View {


    private Paint paint;
    private int width, height;
    private Path path;
    private Region circleRegion;
    private Matrix matrix;

    private float[] touchPoints = new float[2];

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

    private void init() {
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(Color.BLUE);
        paint.setStrokeWidth(10);


        circleRegion = new Region();
        path = new Path();
        //Path.Direction.CW---順時針  Path.Direction.CCW逆時針
        path.addCircle(0, 0, 250, Path.Direction.CW);

        Region region = new Region(-width / 2, -height / 2, width / 2, height / 2);
        circleRegion.setPath(path, region);

        matrix = new Matrix();

    }


    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        width = w;
        height = h;
        init();

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.translate(width / 2, height / 2);
        matrix.reset();
        if (matrix.isIdentity()) {
            canvas.getMatrix().invert(matrix);
        }
        canvas.drawPath(path, paint);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:

                touchPoints[0] = event.getRawX();
                touchPoints[1] = event.getRawY();

                matrix.mapPoints(touchPoints);
                if (circleRegion.contains((int) touchPoints[0], (int) touchPoints[1])) {
                    Toast.makeText(getContext(), "觸摸區(qū)域內(nèi)", Toast.LENGTH_LONG).show();
                }
                break;
        }
        return true;

    }
}

明白了基本原理以后,我們就可以對復(fù)雜的不規(guī)則區(qū)域進(jìn)行點擊事件的判斷了。
繪制第一張的圖時,主要用到的就是path工具類。

public class SpecialMap extends View {
    private Paint paint;
    private int width, height;
    private Region topRegion, leftRegion, rightRegion, bottomRegion, totalRegion, centerRegion;
    private Matrix matrix;
    private RectF rectFBig, rectFSmall;
    private Path leftPath, topPath, rightPath, bottomPath, centerPath;

    private float[] touchPoints = new float[2];

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

    private void init() {
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(Color.LTGRAY);
        paint.setStrokeWidth(10);

        topRegion = new Region();
        leftRegion = new Region();
        rightRegion = new Region();
        bottomRegion = new Region();
        totalRegion = new Region();
        centerRegion = new Region();

        rectFBig = new RectF(-400, -400, 400, 400);
        rectFSmall = new RectF(-250, -250, 250, 250);
        totalRegion = new Region(-width / 2, -height / 2, width / 2, height / 2);

        leftPath = new Path();
        topPath = new Path();
        rightPath = new Path();
        bottomPath = new Path();
        centerPath = new Path();

        int sweepAngle = 80;
        /**
         * startAngle是開始度數(shù)
         * sweepAngle指的是旋轉(zhuǎn)的度數(shù),也就是以startAngle開始,旋轉(zhuǎn)多少度,如果sweepAngle是正數(shù),那么就是按順時針方向旋轉(zhuǎn),如果是負(fù)數(shù)就是按逆時針方向旋轉(zhuǎn)。
         */
        rightPath.addArc(rectFBig, 5, sweepAngle);
        /**
         * arcTo是先畫一個橢圓,然后再在這個橢圓上面截取一部分部形。這個圖形自然就是一個弧線了
         */
        rightPath.arcTo(rectFSmall, 5 + sweepAngle, -sweepAngle);
        rightPath.close();

        bottomPath.addArc(rectFBig, 95, sweepAngle);
        bottomPath.arcTo(rectFSmall, 95 + sweepAngle, -sweepAngle);
        bottomPath.close();


        leftPath.addArc(rectFBig, 185, sweepAngle);
        leftPath.arcTo(rectFSmall, 185 + sweepAngle, -sweepAngle);
        leftPath.close();


        topPath.addArc(rectFBig, 275, sweepAngle);
        topPath.arcTo(rectFSmall, 275 + sweepAngle, -sweepAngle);
        topPath.close();

        centerPath.addCircle(0, 0, 120, Path.Direction.CW);
        matrix = new Matrix();

        leftRegion.setPath(leftPath, totalRegion);
        topRegion.setPath(topPath, totalRegion);
        rightRegion.setPath(rightPath, totalRegion);
        bottomRegion.setPath(bottomPath, totalRegion);
        centerRegion.setPath(centerPath, totalRegion);


    }


    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        width = w;
        height = h;
        init();

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.translate(width / 2, height / 2);
        matrix.reset();
        if (matrix.isIdentity()) {
            canvas.getMatrix().invert(matrix);
        }
        canvas.drawPath(leftPath, paint);
        canvas.drawPath(bottomPath, paint);
        canvas.drawPath(rightPath, paint);
        canvas.drawPath(topPath, paint);
        canvas.drawPath(centerPath, paint);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:

                touchPoints[0] = event.getRawX();
                touchPoints[1] = event.getRawY();

                matrix.mapPoints(touchPoints);
                if (topRegion.contains((int) touchPoints[0], (int) touchPoints[1])) {
                    Toast.makeText(getContext(), "點擊了右上部", Toast.LENGTH_LONG).show();
                } else if (leftRegion.contains((int) touchPoints[0], (int) touchPoints[1])) {
                    Toast.makeText(getContext(), "點擊了左上部", Toast.LENGTH_LONG).show();
                } else if (bottomRegion.contains((int) touchPoints[0], (int) touchPoints[1])) {
                    Toast.makeText(getContext(), "點擊了左下部", Toast.LENGTH_LONG).show();
                } else if (rightRegion.contains((int) touchPoints[0], (int) touchPoints[1])) {
                    Toast.makeText(getContext(), "點擊了右下部", Toast.LENGTH_LONG).show();
                } else if (centerRegion.contains((int) touchPoints[0], (int) touchPoints[1])) {
                    Toast.makeText(getContext(), "點擊了中部", Toast.LENGTH_LONG).show();
                }
                break;
        }
        return true;

    }
}

注:可能需要關(guān)閉硬件加速

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容