Android饼图好画,但不规则区域点击如何区分?


前言

前阵子帮团队招人,我很喜欢问候选人这个问题。
点击区域是不规则的,该如何让手势事件在不规则区域内做出响应?
如果候选人的答案是通过数学计算的方式来确定落点。
我会微微一笑的反问他,如果是五角星怎么办?


本文主要会介绍以下内容

1、绘制饼图的几种方式

2、不规则区域的点击

3、文字居中公式

先看一下最终实现的效果,代码地址在文末

自定义的饼图

如何绘制饼图?

基础知识

Path

类型 API 描述
添加弧形 addArc 添加弧形
逻辑运算 op A\B(DIFFERENCE), A∩B(INTERSECT), B\A(REVERSE_DIFFERENCE), A∪B(UNION), A⊕B(XOR)
计算边界 computeBounds 计算路径的边界

因为之后会用到逻辑运算,先来看一下逻辑运算的API,minSdkVersion必须大于等于19

op

Added in API level 19

boolean op (Path path1, Path path2, Path.Op op)

Set this path to the result of applying the Op to the two specified paths. The resulting path will be constructed from non-overlapping contours. The curve order is reduced where possible so that cubics may be turned into quadratics, and quadratics maybe turned into lines.

参数1,参数2是需要计算的两个路径。参数3是运算种类。两个path经过逻辑运算后得到新的路径会存放在调用这个api的路径当中。

逻辑运算包含五种类型

描述 示意图
Path.Op. DIFFERENCE Subtract the second path from the first path. 从第一路径中减去第二个路径。
difference
Path.Op. REVERSE_DIFFERENCE Subtract the first path from the second path. 从第二个路径中减去第一个路径。
reverse_diferecne
Path.Op. INTERSECT Intersect the two paths. 两个路径的交集。
intersect
Path.Op. UNION Union (inclusive-or) the two paths. 两个路径的合集。
union
Path.Op. XOR Exclusive-or the two paths. 两个路径的异或。即两个路径的合集减去两个路径的交集。
xor

Canvas

类型 API 描述
绘制弧形 drawArc 绘制弧形
绘制路径 drawPath 绘制路径,路径样式取决于paint

饼图绘制

结合上面的API绘制饼图的方式有很多,我先后尝试了以下3种方式

//方法1 调用canvas.drawArc方法绘制不闭合的弧形
canvas.drawArc(pieInRectF, startAngle, sweepAngle, false, piePaint);
//方法2 添加弧形路径,调用canvas.drawPath绘制
Path path = new Path();
path.addArc(pieInRectF, startAngle, sweepAngle);
canvas.drawPath(path, piePaint);
//以上两种绘制弧形的思路均是绘制不闭合的弧形路径,通过设置路径宽度最终达到环形效果
//方法3
Path path1 = new Path();
path1.moveTo(in.centerX(), in.centerY());
path1.arcTo(in, startAngle, angle);
Path path2 = new Path();
path2.moveTo(out.centerX(), out.centerY());
path2.arcTo(out, startAngle, angle);
Path path = new Path();
path.op(path2, path1, Path.Op.DIFFERENCE);
//从path2中减去path1获取新的路径
通过path绘制饼图

画了一个示意图,简单感受一下...

Path2闭合后是个扇形,Path1是个较小的扇形,Path2 - Path1后就能得到一块饼图所需的区域了。

那么问题来了,已经有两种简单易懂绘制饼图的方法,为什么要舍近求远搞个这么复杂的?

如果你只需要完成饼图绘制,前两种方法确实够用,但如果想实现饼图不同区域点击效果,推荐使用第三种方式。


如何实现不规则区域的点击?

关于不规则区域的点击实现,先提供一些其他思路

1、像素点判断

缺点:容易OOM、图形不能动态修改

png图都是矩形的,无效部分都设置为透明色,把图片作为控件的背景图片。点击时判断点击点的像素是否为透明,不透明则的控件响应点击事件。具体实现方法请见《Android不规则点击区域详解》。

2、数学逻辑判断

在研究不规则区域点击的时候,看到了这种方案。

采用数学公式判断点击坐标是否在圆环上,再通过角度判断落在具体哪一块上。但由于考虑到数学计算方式的复杂(其实是我懒...),最终我没有尝试这种方式。感兴趣的同学具体实现方法请见《Android自定义饼状图,且能区分点击的区域》和《PieChar扇形图实现》。

3、通过Region判断坐标落点

由于Path是由多个坐标点构成的,将path转化成region,可以通过region.contains(int x,int y)来判断点击坐标是否在该区域。该方法不仅适用于饼图,其他更加复杂图形同样适用,当然前提是你能构建出绘制这个图形所需要的路径。

public void setRegion(Path path) {
    Region re = new Region();
    RectF rectF = new RectF();
    path.computeBounds(rectF, true);
    re.setPath(path, new Region((int) rectF.left, (int) rectF.top, (int) rectF.right, (int) rectF.bottom));
    this.region = re;
}

上文中已经通过Path的逻辑运算获取到饼图块的path,通过这个path再生成region。

判断是否在这个区域也非常简单

public boolean isInRegion(float x, float y) {
    return region != null && region.contains((int)x, (int)y);
}

有人可能会有疑问,调用path.addArc方法,也是通过path绘制图案,这种方式行不行呢?事实是检验真理的唯一标准。亲测使用第二种方案获得的path去生成region获得的范围区域完全错误。详见下图。

错误的饼图

每个饼图块都对应一个单独的region,黑色的点是我手指点击的时候绘制的落点,即region.contains((int)x, (int)y)返回true。可以看到判断点位是否在区域内的位置完全错了。


文字居中的公式

在此记录一下文字居中公式,具体推导过程网上很多博客讲解了,就不展开了。

 Paint.FontMetrics fm = mTxtPaint.getFontMetrics();
 float y = centerY - fm.top / 2 - fm.bottom / 2;

本文源码

感兴趣的朋友可以点击本文源码查看。


参考链接

Path图形与逻辑运算

PieChar扇形图实现

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,482评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,377评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,762评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,273评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,289评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,046评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,351评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,988评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,476评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,948评论 2 324
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,064评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,712评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,261评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,264评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,486评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,511评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,802评论 2 345

推荐阅读更多精彩内容