@[TOC] 一个带阴影效果的圆形控制的View,可以上下左右中进行控制
先看一下效果图
用法
<!-- app:center_text="@string/tv" 中间的文本
app:center_icon="@drawable/ok_tv_normal" 中间的图标 两个都设置优先使用图标
app:center_is_can_click="true" 中间是否可以点击 默认可以
app:control_radius="85dp" 圆的半径 设置这个来控制view的大小,设置宽高没有用的
app:first_icon="@drawable/up_tv" //第一个图标 图标顺序为顺时针 上 右 下 左
app:four_icon="@drawable/left_tv" 第四个图标
app:icon_num="4" //设置图标的个数不包括中间的
app:second_icon="@drawable/right_tv" 第二个图标
app:third_icon="@drawable/down_tv" 第三个图标
-->
<at.smarthome.infraredcontrol.views.CommonControlView
android:id="@+id/cv_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:center_icon="@drawable/ok_tv_normal"
app:center_is_can_click="true"
app:control_radius="85dp"
app:first_icon="@drawable/up_tv"
app:four_icon="@drawable/left_tv"
app:icon_num="3"
app:second_icon="@drawable/right_tv"
app:third_icon="@drawable/down_tv" />
本圆形控制View,支持中间小圆里可以放文字或者图标,圆边上的图标最多支持4个图标,可以通过xml自定义属性动态配置1-4个图标。还可以为其点击区域添加点击事件。非常灵活。
设计原理
怎么来绘制这样的一个View呢,首先需要画一个带阴影效果的圆,并在大圆内画一个小圆,然后根据点击区域画点击效果,画中间图标或者文字,再根据配置来画其他的图标。最后我们需要根据手指触摸的位置添加点击事件。
所涉及的主要知识点,利用BlurMaskFilter来绘制阴影效果,利用Region来判断手指触摸的位置是否在Region创建的对象中,利用Path来绘制不规则扇形。
主要代码具体实现
1.Region区域的数据初始化 和Path的设置
private void initRegin() {
//绘制中间圆的Path
//raidus为半径 innerRaidus为内圆半径
centPath.addCircle(raidus, raidus, innerRaidus, Path.Direction.CW);
//中间Region对象
center.setPath(centPath, new Region((int) (raidus - innerRaidus), (int) (raidus - innerRaidus), (int) (raidus + innerRaidus), (int) (raidus + innerRaidus)));
//miconNum为图标个数除中间的图标外
//默认开始角度为 private int startAngel = 225; private int sweepAngel = 90;
//只有为第三个的时候才有改变,等下要计算图标的Region和Path
if (miconNum == 3) {
startAngel = 210;
sweepAngel = 120;
}
for (int i = 0; i < miconNum; i++) {
//因为要有阴影效果才有padding
RectF rect = new RectF(padding, padding, raidus + raidus - padding, raidus + raidus - padding);
RectF rect1 = new RectF(raidus - innerRaidus, raidus - innerRaidus, raidus + innerRaidus, raidus + innerRaidus);
Path path = new Path();
path.moveTo(raidus, raidus);
path.arcTo(rect, startAngel, sweepAngel);
Path path1 = new Path();
path1.moveTo(raidus, raidus);
path1.arcTo(rect1, startAngel, sweepAngel);
if (miconNum == 2) {
startAngel += 2 * sweepAngel;
} else {
startAngel += sweepAngel;
}
RectF rectF = new RectF();
RectF rectF1 = new RectF();
path.computeBounds(rectF, true);
path.close();
path1.computeBounds(rectF1, true);
path1.close();
Path pathReal = new Path();
//取公共部分 Path.Op.DIFFERENCE
pathReal.op(path, path1, Path.Op.DIFFERENCE);
//存储Path的List
mPaths.add(pathReal);
Region region = new Region();
region.setPath(path, new Region((int) rectF.left, (int) rectF.top, (int) rectF.right, (int) rectF.bottom));
Region region1 = new Region();
region1.setPath(path1, new Region((int) rectF1.left, (int) rectF1.top, (int) rectF1.right, (int) rectF1.bottom));
Region region2 = new Region();
//取公共部分 Path.Op.DIFFERENCE
region2.op(region, region1, Region.Op.DIFFERENCE);
//存储Region的List
regions.add(region2);
}
}
到现在为止我们就做好前期工作把Regoin和Path弄好了,接下来看如何绘制和判断点击事件
1.绘制代码
@Override
protected void onDraw(Canvas canvas) {
//绘制圆有阴影效果
mPaint.setStyle(Paint.Style.FILL);
mPaint.setMaskFilter(blurMaskFilter);
//阴影颜色
mPaint.setColor(getResources().getColor(R.color.color_black10));
canvas.drawCircle(raidus, raidus, raidus - padding, mPaint);
mPaint.setMaskFilter(null);
//再把圆填充为白色,就周围才会有阴影了
mPaint.setColor(getResources().getColor(R.color.white));
canvas.drawCircle(raidus, raidus, raidus - padding, mPaint);
//绘制内圆
mPaint.setColor(getResources().getColor(R.color.line_grey));
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(raidus, raidus, innerRaidus, mPaint);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setStrokeWidth(0);
mPaint.setColor(getResources().getColor(R.color.line_grey));
//判断当前点击区域绘制点击效果
if (isDown) {
if (currentSelect != -1 && currentSelect != 4 && currentSelect < mPaths.size()) {
drawRegion(canvas, mPaths.get(currentSelect), mPaint);
} else if (currentSelect == 4) {
drawRegion(canvas, centPath, mPaint);
}
}
//绘制中间的图标或者文字
if (mCenterDrawables != null) {
canvas.drawBitmap(mCenterDrawables, raidus - mCenterDrawables.getWidth() / 2.0f, raidus - mCenterDrawables.getHeight() / 2.0f, null);
} else {
if (!TextUtils.isEmpty(centerText)) {
mPaint.setColor(getResources().getColor(R.color.color666666));
mPaint.setTextSize(DensityUtils.dip2px(getContext(), 14));
float textWidth = mPaint.measureText(centerText);
Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
float y = raidus + (Math.abs(fontMetrics.ascent) - fontMetrics.descent) / 2;
canvas.drawText(centerText, raidus - textWidth / 2, y, mPaint);
}
}
canvas.save();
//绘制其他图标
for (Bitmap mDrawable : mDrawables) {
canvas.drawBitmap(mDrawable, raidus - (mDrawable.getWidth() / 2.0f), (raidus - innerRaidus - mDrawable.getHeight() + padding) / 2, null);
if (miconNum == 2) {
canvas.rotate(180, raidus, raidus);
} else if (miconNum == 3) {
canvas.rotate(120, raidus, raidus);
} else if (miconNum == 4) {
canvas.rotate(90, raidus, raidus);
}
}
canvas.restore();
}
//绘制阴影效果
private void drawRegion(Canvas canvas, Path rgn, Paint paint) {
//用Region方法来绘制图会有很明显的锯齿问题
canvas.drawPath(rgn, paint);
}
2.点击区域判断
//主要根据Region来判断,这里就不做过多解释
@Override
public boolean onTouchEvent(MotionEvent event) {
if (isEnabled()) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//按下
lastX = event.getX();
lastY = event.getY();
if (isCenterTop((int) event.getX(), (int) event.getY()) && isCenterCanClick) {
isDown = true;
currentSelect = 4;
invalidate();
} else if (isDownTop((int) event.getX(), (int) event.getY())) {
isDown = true;
currentSelect = 0;
invalidate();
} else if (isDownLeft((int) event.getX(), (int) event.getY())) {
isDown = true;
currentSelect = 3;
invalidate();
} else if (isDownRight((int) event.getX(), (int) event.getY())) {
isDown = true;
currentSelect = 1;
invalidate();
} else if (isDownBottom((int) event.getX(), (int) event.getY())) {
isDown = true;
currentSelect = 2;
invalidate();
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
//弹起
if (isCenterCanClick && isCenterTop((int) event.getX(), (int) event.getY()) && currentSelect == 4 && isClick(lastX, lastY, event.getX(), event.getY())) {
if (callBack != null) {
callBack.centerClick();
}
} else if (isDownTop((int) event.getX(), (int) event.getY()) && currentSelect == 0 && isClick(lastX, lastY, event.getX(), event.getY())) {
if (callBack != null) {
callBack.oneClick();
}
} else if (isDownLeft((int) event.getX(), (int) event.getY()) && currentSelect == 3 && isClick(lastX, lastY, event.getX(), event.getY())) {
if (callBack != null) {
callBack.fourClick();
}
} else if (isDownRight((int) event.getX(), (int) event.getY()) && currentSelect == 1 && isClick(lastX, lastY, event.getX(), event.getY())) {
if (callBack != null) {
callBack.twoClick();
}
} else if (isDownBottom((int) event.getX(), (int) event.getY()) && currentSelect == 2 && isClick(lastX, lastY, event.getX(), event.getY())) {
if (callBack != null) {
callBack.threeClick();
}
}
isDown = false;
currentSelect = -1;
invalidate();
break;
default:
break;
}
return true;
} else {
return super.onTouchEvent(event);
}
}
到这里我们就把主要代码实现讲完了。最后我会附上整个代码的链接。