利用Android封装一个有趣的Loading组件
作者:岛上码农 发布时间:2023-01-07 16:25:28
前言
在上一篇普通的加载千篇一律,有趣的 loading 万里挑一 中,我们介绍了使用Path
类的PathMetrics
属性来控制绘制点在路径上运动来实现比较有趣的loading
效果。有评论说因为是黑色背景,所以看着好看。黑色背景确实显得高端一点,但是并不是其他配色也不行,本篇我们来封装一个可以自定义配置前景色和背景色的Loading
组件。
组件定义
loading
组件共定义4个入口参数:
前景色:绘制图形的前景色;
背景色:绘制图形的背景色;
图形尺寸:绘制图形的尺寸;
加载文字:可选,如果有文字就显示,没有就不显示。
得到的Loading
组件类如下所示:
class LoadingAnimations extends StatefulWidget {
final Color bgColor;
final Color foregroundColor;
String? loadingText;
final double size;
LoadingAnimations(
{required this.foregroundColor,
required this.bgColor,
this.loadingText,
this.size = 100.0,
Key? key})
: super(key: key);
@override
_LoadingAnimationsState createState() => _LoadingAnimationsState();
}
圆形Loading
我们先来实现一个圆形的loading
,效果如下所示。
这里绘制了两组沿着一个大圆运动的轴对称的实心圆,半径依次减小,圆心间距随着动画时间逐步拉大。实际上实现的核心还是基于Path
的PathMetrics
。具体实现代码如下:
_drawCircleLoadingAnimaion(
Canvas canvas, Size size, Offset center, Paint paint) {
final radius = boxSize / 2;
final ballCount = 6;
final ballRadius = boxSize / 15;
var circlePath = Path()
..addOval(Rect.fromCircle(center: center, radius: radius));
var circleMetrics = circlePath.computeMetrics();
for (var pathMetric in circleMetrics) {
for (var i = 0; i < ballCount; ++i) {
var lengthRatio = animationValue * (1 - i / ballCount);
var tangent =
pathMetric.getTangentForOffset(pathMetric.length * lengthRatio);
var ballPosition = tangent!.position;
canvas.drawCircle(ballPosition, ballRadius / (1 + i), paint);
canvas.drawCircle(
Offset(size.width - tangent.position.dx,
size.height - tangent.position.dy),
ballRadius / (1 + i),
paint);
}
}
}
其中路径比例为lengthRatio
,通过animationValue
乘以一个系数使得实心圆的间距越来越大 ,同时通过Offset(size.width - tangent.position.dx, size.height - tangent.position.dy)
绘制了一组对对称的实心圆,这样整体就有一个圆形的效果了,动起来也会更有趣一点。
椭圆运动Loading
椭圆和圆形没什么区别,这里我们搞个渐变的效果看看,利用之前介绍过的Paint
的shader
可以实现渐变色绘制效果。
实现代码如下所示。
final ballCount = 6;
final ballRadius = boxSize / 15;
var ovalPath = Path()
..addOval(Rect.fromCenter(
center: center, width: boxSize, height: boxSize / 1.5));
paint.shader = LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [this.foregroundColor, this.bgColor],
).createShader(Offset.zero & size);
var ovalMetrics = ovalPath.computeMetrics();
for (var pathMetric in ovalMetrics) {
for (var i = 0; i < ballCount; ++i) {
var lengthRatio = animationValue * (1 - i / ballCount);
var tangent =
pathMetric.getTangentForOffset(pathMetric.length * lengthRatio);
var ballPosition = tangent!.position;
canvas.drawCircle(ballPosition, ballRadius / (1 + i), paint);
canvas.drawCircle(
Offset(size.width - tangent.position.dx,
size.height - tangent.position.dy),
ballRadius / (1 + i),
paint);
}
}
当然,如果渐变色的颜色更丰富一点会更有趣些。
贝塞尔曲线Loading
通过贝塞尔曲线构建一条Path
,让一组圆形沿着贝塞尔曲线运动的Loading
效果也很有趣。
原理和圆形的一样,首先是构建贝塞尔曲线Path
,代码如下。
var bezierPath = Path()
..moveTo(size.width / 2 - boxSize / 2, center.dy)
..quadraticBezierTo(size.width / 2 - boxSize / 4, center.dy - boxSize / 4,
size.width / 2, center.dy)
..quadraticBezierTo(size.width / 2 + boxSize / 4, center.dy + boxSize / 4,
size.width / 2 + boxSize / 2, center.dy)
..quadraticBezierTo(size.width / 2 + boxSize / 4, center.dy - boxSize / 4,
size.width / 2, center.dy)
..quadraticBezierTo(size.width / 2 - boxSize / 4, center.dy + boxSize / 4,
size.width / 2 - boxSize / 2, center.dy);
这里实际是构建了两条贝塞尔曲线,先从左边到右边,然后再折回来。之后就是运动的实心圆了,这个只是数量上多了,ballCount
为30
,这样效果看着就有一种拖影的效果。
var ovalMetrics = bezierPath.computeMetrics();
for (var pathMetric in ovalMetrics) {
for (var i = 0; i < ballCount; ++i) {
var lengthRatio = animationValue * (1 - i / ballCount);
var tangent =
pathMetric.getTangentForOffset(pathMetric.length * lengthRatio);
var ballPosition = tangent!.position;
canvas.drawCircle(ballPosition, ballRadius / (1 + i), paint);
canvas.drawCircle(
Offset(size.width - tangent.position.dx,
size.height - tangent.position.dy),
ballRadius / (1 + i),
paint);
}
}
这里还可以改变运动方向,实现一些其他的效果,例如下面的效果,第二组圆球的绘制位置实际上是第一组圆球的x、y坐标的互换。
var lengthRatio = animationValue * (1 - i / ballCount);
var tangent =
pathMetric.getTangentForOffset(pathMetric.length * lengthRatio);
var ballPosition = tangent!.position;
canvas.drawCircle(ballPosition, ballRadius / (1 + i), paint);
canvas.drawCircle(Offset(tangent.position.dy, tangent.position.dx),
ballRadius / (1 + i), paint);
组件使用
我们来看如何使用我们定义的这个组件,使用代码如下,我们用Future延迟模拟了一个加载效果,在加载过程中使用loading
指示加载过程,加载完成后显示图片。
class _LoadingDemoState extends State<LoadingDemo> {
var loaded = false;
@override
void initState() {
super.initState();
Future.delayed(Duration(seconds: 5), () {
setState(() {
loaded = true;
});
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text('Loading 使用'),
),
body: Center(
child: loaded
? Image.asset(
'images/beauty.jpeg',
width: 100.0,
)
: LoadingAnimations(
foregroundColor: Colors.blue,
bgColor: Colors.white,
size: 100.0,
),
),
);
}
最终运行的效果如下,源码已提交至:绘图相关源码,文件名为loading_animations.dart
。
来源:https://juejin.cn/post/7129071400509243423


猜你喜欢
- 绘制模糊数学中隶属函数分布图using System; using System.Collect
- 最近部门打算优化下 APP 在低端机上的卡顿情况,既然想优化,就必须获取卡顿情况,那么如何获取卡顿情况就是本文目的。一般主线程过多的 UI
- 样式如下所示:布局:layoutdialog_set_pwd.xml<?xml version="." encod
- 一,配置环境变量步骤1.右击桌面上的“此电脑”图标,选择属性。2.选择高级系统设置3.单击环境变量4.单击系统变量中的新建5.在变量名中输入
- async和awaitasync微软文档:使用 async 修饰符可将方法、lambda 表达式或匿名方法指定为异步。使
- 以下是代码:package cn.study.concurrency.ch11;/** * 锁分段 * @author xiaof * */
- 方法一,修改gradle.properties文件,增加一句gradle.user.home=D\:\\Android\\.gradle但这
- 概述在实际项目开发中如果需要支持多语言,我们需要整理项目中所有的字符串并翻译成对应的语种放在相应的文件夹下,就像这样最让我们头痛的是我们得一
- mybatis in查询条件过长的解决方法1:分次查询,将参数且分割成多个短的查询后合并代码: in
- @Value值注入及配置文件组件扫描spring配置文件对应的是父容器,springMVC配置文件产生的是子容器,前者一般配置数据源,事务,
- 局限性只支持MP4文件经过尝试对于一些MP4文件分割不了依赖<!-- mp4文件操作jar --><!-- https:/
- Java中获取整点时间戳在实际的开发过程中,前端给后端传时间的时候,有时候传的是整点数值,比如:timeList=[00,01,02,03,
- SpringBoot遇到的坑@Qualifier报红今天写项目的时候@Qualifier一直报红,排查半天后面才知道原来是idea生成项目的
- 本章概要文件上传@ControllerAdvice文件上传Java 中的文件上传一共涉及两个组件,一个是 CommonsMultipartR
- 浅谈先来说一下“this指针”:C++中通过引入this指针解决该问题,暨:C++编译器给每个“非静态的成员函数”增加了一个隐藏的指针参数,
- MyBatis的注解实现复杂映射开发实现复杂关系映射之前我们可以在映射文件中通过配置来实现,使用注解开发后,我们可以使用@Results注解
- 女朋友他们项目用了 spring-boot,以 spring-boot-parent 作为 parent:<parent> &l
- Annotation(注解)是JDK1.5及以后版本引入的。它可以用于创建文档,跟踪代码中的依赖性,甚至执行基本编译时检查。注解是以&
- 写在前面SpringBoot创建定时任务的方式很简单,主要有两种方式:一、基于注解的方式(@Scheduled)二、数据库动态配置。实际开发
- 前言String可以说是Java中使用最多最频繁、最特殊的类,因为同时也是字面常量,而字面常量包括基本类型、String类型、空类型。一.