利用Android封裝一個有趣的Loading組件
前言
在上一篇普通的加載千篇一律,有趣的 loading 萬里挑一 中,我們介紹了使用Path
類的PathMetrics
屬性來控制繪制點在路徑上運動來實現(xiàn)比較有趣的loading
效果。有評論說因為是黑色背景,所以看著好看。黑色背景確實顯得高端一點,但是并不是其他配色也不行,本篇我們來封裝一個可以自定義配置前景色和背景色的Loading
組件。
組件定義
loading
組件共定義4個入口參數(shù):
- 前景色:繪制圖形的前景色;
- 背景色:繪制圖形的背景色;
- 圖形尺寸:繪制圖形的尺寸;
- 加載文字:可選,如果有文字就顯示,沒有就不顯示。
得到的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
我們先來實現(xiàn)一個圓形的loading
,效果如下所示。
這里繪制了兩組沿著一個大圓運動的軸對稱的實心圓,半徑依次減小,圓心間距隨著動畫時間逐步拉大。實際上實現(xiàn)的核心還是基于Path
的PathMetrics
。具體實現(xiàn)代碼如下:
_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
乘以一個系數(shù)使得實心圓的間距越來越大 ,同時通過Offset(size.width - tangent.position.dx, size.height - tangent.position.dy)
繪制了一組對對稱的實心圓,這樣整體就有一個圓形的效果了,動起來也會更有趣一點。
橢圓運動Loading
橢圓和圓形沒什么區(qū)別,這里我們搞個漸變的效果看看,利用之前介紹過的Paint
的shader
可以實現(xiàn)漸變色繪制效果。
實現(xiàn)代碼如下所示。
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);
這里實際是構建了兩條貝塞爾曲線,先從左邊到右邊,然后再折回來。之后就是運動的實心圓了,這個只是數(shù)量上多了,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); } }
這里還可以改變運動方向,實現(xiàn)一些其他的效果,例如下面的效果,第二組圓球的繪制位置實際上是第一組圓球的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
。
總結
本篇介紹了Loading
組件的封裝方法,核心要點還是利用Path
和動畫控制繪制元素的運動軌跡來實現(xiàn)更有趣的效果。在實際應用過程中,也可以根據(jù)交互設計的需要,做一些其他有趣的加載動效,提高等待過程的趣味性。
到此這篇關于利用Android封裝一個有趣的Loading組件的文章就介紹到這了,更多相關Android封裝Loading組件內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Android 自動化測試經驗分享 深入UiScrollable
UiScrollable是一個UiCollection(這東西還沒搞懂),我們可以使用它,在可滑動的頁面(水平滑動或上下滑動都可以)上查找我們想要的控件(item)2013-05-05Android利用startActivityForResult返回數(shù)據(jù)到前一個Activity
這篇文章主要介紹了Android利用startActivityForResult返回數(shù)據(jù)到前一個Activity,幫助大家更好的利用Android進行開發(fā),感興趣的朋友可以了解下2021-01-01Android實現(xiàn)ListView異步加載圖片的方法
這篇文章主要介紹了Android實現(xiàn)ListView異步加載圖片的方法,以實例形式較為詳細的分析了Android中ListView異步加載圖片的原理與相關實現(xiàn)技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-10-10Android使用MediaRecorder實現(xiàn)錄音及播放
這篇文章主要為大家詳細介紹了Android使用MediaRecorder實現(xiàn)錄音及播放,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-02-02詳解Android Studio如何導入第三方類庫、jar包和so庫
這篇文章主要介紹了Android Studio如何導入第三方類庫、jar包和so庫的相關資料,需要的朋友可以參考下2017-06-06Android?studio?利用共享存儲進行用戶的注冊和登錄驗證功能
這篇文章主要介紹了Android?studio?利用共享存儲進行用戶的注冊和登錄驗證功能,包括注冊頁面布局及登錄頁面功能,本文通過實例代碼給大家介紹的非常詳細,需要的朋友可以參考下2021-12-12Android 滑動Scrollview標題欄漸變效果(仿京東toolbar)
這篇文章主要介紹了Android 滑動Scrollview標題欄漸變效果(仿京東toolbar),本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-01-01