}
可以看到,paint()
方法获得一个 Canvas 和一个 Size 参数。Canvas 提供一组方法可以让我们绘制任何形状:圆形、直线、圆弧、矩形等等。Size 参数即是画布的尺寸,由画布适配的部件尺寸决定。我们还需要一个 Paint,允许我们定制样式、颜色以及其他东西。
现在 BasePainter 的功能用法已经不言自明,然而 SliderPainter 却有一点儿不寻常,现在我们不仅要绘制一个圆弧而非圆,还需要绘制 Handler。
import ‘dart:math’;
import ‘package:flutter/material.dart’;
import ‘package:flutter_circular_slider/src/utils.dart’;
class SliderPainter extends CustomPainter {
double startAngle;
double endAngle;
double sweepAngle;
Color selectionColor;
Offset initHandler;
Offset endHandler;
Offset center;
double radius;
SliderPainter(
{@required this.startAngle,
@required this.endAngle,
@required this.sweepAngle,
@required this.selectionColor});
@override
void paint(Canvas canvas, Size size) {
if (startAngle == 0.0 && endAngle == 0.0) return;
Paint progress = _getPaint(color: selectionColor);
center = Offset(size.width / 2, size.height / 2);
radius = min(size.width / 2, size.height / 2);
canvas.drawArc(Rect.fromCircle(center: center, radius: radius),
-pi / 2 + startAngle, sweepAngle, false, progress);
Paint handler = _getPaint(color: selectionColor, style: PaintingStyle.fill);
Paint handlerOutter = _getPaint(color: selectionColor, width: 2.0);
// 绘制 handler
initHandler = radiansToCoordinates(center, -pi / 2 + startAngle, radius);
canvas.drawCircle(initHandler, 8.0, handler);
canvas.drawCircle(initHandler, 12.0, handlerOutter);
endHandler = radiansToCoordinates(center, -pi / 2 + endAngle, radius);
canvas.drawCircle(endHandler, 8.0, handler);
canvas.drawCircle(endHandler, 12.0, handlerOutter);
}
Paint _getPaint({@required Color color, double width, PaintingStyle style}) =>
Paint()
…color = color
…strokeCap = StrokeCap.round
…style = style ?? PaintingStyle.stroke
…strokeWidth = width ?? 12.0;
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
再一次地,我们获取了 center 和 radius 的值,但我们这次绘制的是圆弧。SliderPainter 将根据用户交互反馈的值作为 start、end 和 sweap 属性的值,以便于我们根据这些参数来绘制圆弧。值得一提的是我们需要从初始角度中减去 pi/2,因为我们的滑块的圆弧的起始位置是在圆形的正上方,而 drawArc()
方法使用 x 轴正轴作为起始位置。
当我们绘制好圆弧以后我们就需要准备绘制 Handler 了。为此,我们将分别绘制两个圆,一个在内部填充,一个在外部包裹。我调用了一些工具集函数用来将弧度转换为圆的坐标。你可以在 Github 仓库内查阅这些函数。
让滑块响应交互
目前来看,仅仅使用 CustomPaint 以及两个 Painter 就已经足够绘制想要的东西了。然而它们还是不能够进行交互。因此就要使用 GestureDetector 来对它进行封装。这样一来我们就可以在画布上对用户事件做出相应处理。
一开始我们将为 Handler 赋初值,当获取这些 Handler 的坐标后,我们将按照以下策略执行操作:
- 监听对于 Handler 的点击(按下)事件并更新相应 Handler 的状态。(_xHandlerSelected = true)。
- 监听被选中 Handler 的拖动更新事件,更新其坐标,同时分别向下、向上传递给 SliderPainter 和我们的回调函数。
- 监听 Handler 的点击(抬起)事件并重置未选中 Handler 的状态。
因为我们需要分别计算出坐标值、新的角度值再传递给 Handler 和 Painter,所以我们的 CircularSliderPaint 必须是一个 StatefulWidget。
import ‘package:flutter/material.dart’;
import ‘package:flutter_circular_slider/src/base_painter.dart’;
import ‘package:flutter_circular_slider/src/slider_painter.dart’;
import ‘package:flutter_circular_slider/src/utils.dart’;
class CircularSliderPaint extends StatefulWidget {
final int init;
final int end;
final int intervals;
final Function onSelectionChange;
final Color baseColor;
final Color selectionColor;
final Widget child;
CircularSliderPaint(
{@required this.intervals,
@required this.init,
@required this.end,
this.child,
@required this.onSelectionChange,
@required this.baseColor,
@required this.selectionColor});
@override
_CircularSliderState createState() => _CircularSliderState();
}
class _CircularSliderState extends State {
bool _isInitHandlerSelected = false;
bool _isEndHandlerSelected = false;
SliderPainter _painter;
/// 用弧度制表示的起始角度,用来确定 init Handler 的位置。
double _startAngle;
/// 用弧度制表示的结束角度,用来确定 end Handler 的位置。
double _endAngle;
/// 用弧度制表示的选择区间的绝对角度(夹角)
double _sweepAngle;
@override
void initState() {
super.initState();
_calculatePaintData();
}
// 我们需要使用 gesture detector 来更新此部件,
// 当父部件重建自己时也是如此。
@override
void didUpdateWidget(CircularSliderPaint oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.init != widget.init || oldWidget.end != widget.end) {
_calculatePaintData();
}
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onPanDown: _onPanDown,
onPanUpdate: _onPanUpdate,
onPanEnd: _onPanEnd,
child: CustomPaint(
painter: BasePainter(
baseColor: widget.baseColor,
selectionColor: widget.selectionColor),
foregroundPainter: _painter,
child: Padding(
padding: const EdgeInsets.all(12.0),
child: widget.child,
),
),
);
}
void _calculatePaintData() {
double initPercent = valueToPercentage(widget.init, widget.intervals);
double endPercent = valueToPercentage(widget.end, widget.intervals);
double sweep = getSweepAngle(initPercent, endPercent);
_startAngle = percentageToRadians(initPercent);
_endAngle = percentageToRadians(endPercent);
_sweepAngle = percentageToRadians(sweep.abs());
_painter = SliderPainter(
startAngle: _startAngle,
endAngle: _endAngle,
sweepAngle: _sweepAngle,
selectionColor: widget.selectionColor,
);
}
_onPanUpdate(DragUpdateDetails details) {
if (!_isInitHandlerSelected && !_isEndHandlerSelected) {
return;
}
if (_painter.center == null) {
return;
}
RenderBox renderBox = context.findRenderObject();
var position = renderBox.globalToLocal(details.globalPosition);
var angle = coordinatesToRadians(_painter.center, position);
var percentage = radiansToPercentage(angle);
var newValue = percentageToValue(percentage, widget.intervals);
if (_isInitHandlerSelected) {
widget.onSelectionChange(newValue, widget.end);
} else {
widget.onSelectionChange(widget.init, newValue);
}
}
onPanEnd() {
_isInitHandlerSelected = false;
_isEndHandlerSelected = false;
}
_onPanDown(DragDownDetails details) {
if (_painter == null) {
return;
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
![](https://i-blog.csdnimg.cn/blog_migrate/37725e90a04e07b3cf58145110e9c231.jpeg)
尾声
最后,我再重复一次,如果你想成为一个优秀的 Android 开发人员,请集中精力,对基础和重要的事情做深度研究。
对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。 整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。
最后想要拿高薪实现技术提升薪水得到质的飞跃。最快捷的方式,就是有人可以带着你一起分析,这样学习起来最为高效,所以为了大家能够顺利进阶中高级、架构师,我特地为大家准备了一套高手学习的源码和框架视频等精品Android架构师教程,保证你学了以后保证薪资上升一个台阶。
当你有了学习线路,学习哪些内容,也知道以后的路怎么走了,理论看多了总要实践的。
进阶学习视频
附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)
[外链图片转存中…(img-XwXETRl3-1712822484016)]
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!