做个酷炫的“锤子”-开关效果,30岁转行程序员

本文详细描述了如何在Android应用中使用Canvas进行图形绘制,包括开关和状态指示器的立体阴影效果,以及滑动和点按事件的处理。作者强调了内阴影和外阴影的技巧,以及如何根据用户交互动态调整开关状态。
摘要由CSDN通过智能技术生成

backgroundAreaPaint.setColor(Color.parseColor(“#66bcbcbc”));
backgroundAreaShadowSize = backgroundAreaH / 4;
backgroundAreaShadowDistance = backgroundAreaH / 12;
backgroundAreaPaint.setShadowLayer(backgroundAreaShadowSize + shadowOffset, 0, backgroundAreaShadowDistance, Color.GRAY);

RectF strokeRectF = new RectF(-strokeW + (width - backgroundAreaW) / 2, -strokeW + (height - backgroundAreaH) / 2, strokeW + (width - backgroundAreaW) / 2 + backgroundAreaW, strokeW + (height - backgroundAreaH) / 2 + backgroundAreaH);
Path strokePath = new Path();
strokePath.addRoundRect(strokeRectF, (backgroundAreaH + strokeW) / 2, (backgroundAreaH + strokeW) / 2, Path.Direction.CW);

RectF rectF = new RectF((width - backgroundAreaW) / 2, (height - backgroundAreaH) / 2, (width - backgroundAreaW) / 2 + backgroundAreaW, (height - backgroundAreaH) / 2 + backgroundAreaH);
Path path = new Path();
path.addRoundRect(rectF, backgroundAreaH / 2, backgroundAreaH / 2, Path.Direction.CW);
canvas.clipPath(path);

canvas.drawPath(strokePath, backgroundAreaPaint);

backgroundAreaPaint.setStrokeWidth(2);
backgroundAreaPaint.clearShadowLayer();
canvas.drawPath(path, backgroundAreaPaint);

canvas.restore();

}

2.1.2 开关指示器

其中包括了凸显立体感的内阴影以及外阴影的绘制,见代码:

void drawIndicator(Canvas canvas) {
//绘制外阴影
indicatorPaint.setColor(indicatorColor);
indicatorPaint.setStyle(Paint.Style.FILL);
indicatorShadowSize = indicatorR / 3;
indicatorShadowDistance = indicatorShadowSize / 2;
indicatorPaint.setShadowLayer(indicatorShadowSize - shadowOffset, 0, indicatorShadowDistance, Color.parseColor(“#ffc1c1c1”));
canvas.drawCircle(indicatorX + indicatorXOffset, (height - backgroundAreaH) / 2 + indicatorR, indicatorR, indicatorPaint);

//绘制内阴影
canvas.save();

indicatorPaint.setColor(Color.parseColor(“#66bcbcbc”));
int strokeW = indicatorR / 2;
indicatorPaint.setStrokeWidth(strokeW);
indicatorPaint.setStyle(Paint.Style.STROKE);
indicatorPaint.setShadowLayer(indicatorR / 3, -indicatorR / 6, -indicatorR / 6, Color.parseColor(“#fff1f1f1”));

Path strokePath = new Path();
strokePath.addCircle(indicatorX + indicatorXOffset, (height - backgroundAreaH) / 2 + indicatorR, indicatorR + strokeW / 2, Path.Direction.CW);

Path path = new Path();
path.addCircle(indicatorX + indicatorXOffset, (height - backgroundAreaH) / 2 + indicatorR, indicatorR, Path.Direction.CW);
canvas.clipPath(path);

canvas.drawPath(strokePath, indicatorPaint);

indicatorPaint.setStrokeWidth(2);
indicatorPaint.clearShadowLayer();
canvas.drawPath(path, indicatorPaint);

canvas.restore();
}

2.1.3 状态指示器

这一步中也包含内阴影的绘制。额外注意连续调用两次canvas.save方法,通过clipPath裁剪出背景区域形状的画布。

void drawFlag(Canvas canvas) {

//首先裁剪出背景圆角矩形画布
canvas.save();
RectF rectF = new RectF((width - backgroundAreaW) / 2, (height - backgroundAreaH) / 2, (width - backgroundAreaW) / 2 + backgroundAreaW, (height - backgroundAreaH) / 2 + backgroundAreaH);
Path bgAreaPath = new Path();
bgAreaPath.addRoundRect(rectF, backgroundAreaH / 2, backgroundAreaH / 2, Path.Direction.CW);

canvas.clipPath(bgAreaPath);

//绘制on flag
flagPaint.setStyle(Paint.Style.FILL);
flagPaint.setColor(onColor);
flagPaint.clearShadowLayer();
canvas.drawCircle(indicatorX + indicatorXOffset - backgroundAreaW * 3 / 5, height / 2, indicatorR / 4, flagPaint);

//内阴影
flagPaint.setStyle(Paint.Style.STROKE);
int onStrokeW = indicatorR / 4;
flagPaint.setStrokeWidth(onStrokeW);
flagPaint.setShadowLayer(onStrokeW, -onStrokeW, onStrokeW, onColor);

Path onPath = new Path();
onPath.addCircle(indicatorX + indicatorXOffset - backgroundAreaW * 3 / 5, height / 2, indicatorR / 4 + onStrokeW / 2, Path.Direction.CW);

canvas.save();

canvas.clipPath(onPath);
canvas.drawPath(onPath, flagPaint);

flagPaint.clearShadowLayer();

canvas.restore();

//绘制off flag
flagPaint.setStyle(Paint.Style.FILL);
flagPaint.setColor(offColor);
canvas.drawCircle(indicatorX + indicatorXOffset + backgroundAreaW * 3 / 5, height / 2, indicatorR / 4, flagPaint);

//内阴影
flagPaint.setStyle(Paint.Style.STROKE);
int offStrokeW = indicatorR / 4;
flagPaint.setStrokeWidth(offStrokeW);
flagPaint.setShadowLayer(offStrokeW, -offStrokeW, offStrokeW, offColor);

Path offPath = new Path();
offPath.addCircle(indicatorX + indicatorXOffset + backgroundAreaW * 3 / 5, height / 2, indicatorR / 4 + offStrokeW / 2, Path.Direction.CW);

canvas.save();

canvas.clipPath(offPath);
canvas.drawPath(offPath, flagPaint);

canvas.restore();

canvas.restore();

}

2.2 交互实现

2.2.1 边界判断

当用户滑动超过边界时,强制重新赋值

indicatorXOffset = (int) (event.getX() - downX);
//边界判断
if (indicatorX + indicatorXOffset <= (width - backgroundAreaW) / 2 + indicatorR) {
indicatorXOffset = (width - backgroundAreaW) / 2 + indicatorR - indicatorX;
} else if (indicatorX + indicatorXOffset >= width - (width - backgroundAreaW) / 2 - indicatorR) {
indicatorXOffset = width - (width - backgroundAreaW) / 2 - indicatorR - indicatorX;
}

2.2.2 区分滑动和点按

注意一个细节,当用户的滑动距离非常小时,算作点按,此时控件的开关状态要改变;滑动距离超过一定阈值时,算滑动操作,此时控件的开关状态不一定改变

if (Math.abs(indicatorXOffset) <= 20) {
//todo:点按操作
}else{
//todo:滑动操作
}

2.2.3 状态变化

定义一个字段isChecked表示开关状态,根据开关指示器位置判断状态是否应该改变

if ((indicatorXOffset > 0 && indicatorXOffset >= (backgroundAreaW - 2 * indicatorR) / 2) || (indicatorXOffset < 0 && indicatorXOffset > -(backgroundAreaW - 2 * indicatorR) / 2)) {
indicatorXOffset = 0;
//切换状态:ON
isChecked = true;
startTranslateAnim(true);
} else if ((indicatorXOffset > 0 && indicatorXOffset < (backgroundAreaW - 2 * indicatorR) / 2) || (indicatorXOffset < 0 && indicatorXOffset <= -(backgroundAreaW - 2 * indicatorR) / 2)) {
indicatorXOffset = 0;
//切换状态:OFF
isChecked = false;

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

总结

作为一名从事Android的开发者,很多人最近都在和我吐槽Android是不是快要凉了?而在我看来这正是市场成熟的表现,所有的市场都是温水煮青蛙,永远会淘汰掉不愿意学习改变,安于现状的那批人,希望所有的人能在大浪淘沙中留下来,因为对于市场的逐渐成熟,平凡并不是我们唯一的答案!
在最后我整理了一份资料,而且我们为了感谢很多支持的学者,资料是无偿分享的,需要的同学可以来学习学习
领取方式:GitHub地址
资料.png
资料图.jpg

的答案!
在最后我整理了一份资料,而且我们为了感谢很多支持的学者,资料是无偿分享的,需要的同学可以来学习学习
领取方式:GitHub地址
[外链图片转存中…(img-DMoSdNVU-1710686393786)]
[外链图片转存中…(img-Jx3etwNg-1710686393786)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值