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

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


device-2018-05-18-111253.gif

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

Region

Region是繪制中的區(qū)域页滚,是一個(gè)閉合的區(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的兩個(gè)區(qū)域取交集

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

circleRegion = new Region();
path = new Path();
//Path.Direction.CW---順時(shí)針  Path.Direction.CCW逆時(shí)針
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概念后锨亏,我們可以對一個(gè)圓形圖案的觸摸坐標(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---順時(shí)針  Path.Direction.CCW逆時(shí)針
        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

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


image.png

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

invert
通過上面的mapXXX方法八匠,可以獲取變換后的坐標(biāo)或者矩形絮爷。但假設(shè)我們知道了變換后的坐標(biāo),如何計(jì)算Matrix變換前的坐標(biāo)那梨树?坑夯!
此時(shí)通過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---順時(shí)針  Path.Direction.CCW逆時(shí)針
        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)行點(diǎn)擊事件的判斷了厌处。
繪制第一張的圖時(shí),主要用到的就是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ù)瑰排,那么就是按順時(shí)針方向旋轉(zhuǎn),如果是負(fù)數(shù)就是按逆時(shí)針方向旋轉(zhuǎn)暖侨。
         */
        rightPath.addArc(rectFBig, 5, sweepAngle);
        /**
         * arcTo是先畫一個(gè)橢圓椭住,然后再在這個(gè)橢圓上面截取一部分部形。這個(gè)圖形自然就是一個(gè)弧線了
         */
        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(), "點(diǎn)擊了右上部", Toast.LENGTH_LONG).show();
                } else if (leftRegion.contains((int) touchPoints[0], (int) touchPoints[1])) {
                    Toast.makeText(getContext(), "點(diǎn)擊了左上部", Toast.LENGTH_LONG).show();
                } else if (bottomRegion.contains((int) touchPoints[0], (int) touchPoints[1])) {
                    Toast.makeText(getContext(), "點(diǎn)擊了左下部", Toast.LENGTH_LONG).show();
                } else if (rightRegion.contains((int) touchPoints[0], (int) touchPoints[1])) {
                    Toast.makeText(getContext(), "點(diǎn)擊了右下部", Toast.LENGTH_LONG).show();
                } else if (centerRegion.contains((int) touchPoints[0], (int) touchPoints[1])) {
                    Toast.makeText(getContext(), "點(diǎn)擊了中部", Toast.LENGTH_LONG).show();
                }
                break;
        }
        return true;

    }
}

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末字逗,一起剝皮案震驚了整個(gè)濱河市京郑,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌葫掉,老刑警劉巖些举,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異俭厚,居然都是意外死亡户魏,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進(jìn)店門挪挤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來绪抛,“玉大人,你說我怎么就攤上這事电禀〈甭耄” “怎么了?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵尖飞,是天一觀的道長症副。 經(jīng)常有香客問我店雅,道長,這世上最難降的妖魔是什么贞铣? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任闹啦,我火速辦了婚禮,結(jié)果婚禮上辕坝,老公的妹妹穿的比我還像新娘窍奋。我一直安慰自己,他們只是感情好酱畅,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布琳袄。 她就那樣靜靜地躺著,像睡著了一般纺酸。 火紅的嫁衣襯著肌膚如雪窖逗。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天餐蔬,我揣著相機(jī)與錄音碎紊,去河邊找鬼。 笑死樊诺,一個(gè)胖子當(dāng)著我的面吹牛仗考,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播词爬,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼秃嗜,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了缸夹?” 一聲冷哼從身側(cè)響起痪寻,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎虽惭,沒想到半個(gè)月后橡类,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡芽唇,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年顾画,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片匆笤。...
    茶點(diǎn)故事閱讀 39,977評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡研侣,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出炮捧,到底是詐尸還是另有隱情庶诡,我是刑警寧澤,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布咆课,位于F島的核電站末誓,受9級特大地震影響扯俱,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜喇澡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一迅栅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧晴玖,春花似錦读存、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至榨惰,卻和暖如春拜英,著一層夾襖步出監(jiān)牢的瞬間静汤,已是汗流浹背琅催。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留虫给,地道東北人藤抡。 一個(gè)月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像抹估,于是被迫代替她去往敵國和親缠黍。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評論 2 355

推薦閱讀更多精彩內(nèi)容