您好,登錄后才能下訂單哦!
本篇內(nèi)容主要講解“Android怎么實(shí)現(xiàn)點(diǎn)匯聚成字的動(dòng)態(tài)效果”,感興趣的朋友不妨來看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“Android怎么實(shí)現(xiàn)點(diǎn)匯聚成字的動(dòng)態(tài)效果”吧!
在講解代碼實(shí)現(xiàn)之前,我們先科普一個(gè)知識(shí),即點(diǎn)陣。點(diǎn)陣在日常生活中很常見,比如廣告屏,停車系統(tǒng)的顯示,行業(yè)內(nèi)稱之為 LED 顯示屏。
LED 顯示屏實(shí)際上就是由很多 LED 燈組合成的一個(gè)顯示面板,然后通過顯示驅(qū)動(dòng)某些燈亮,某些燈滅就可以實(shí)現(xiàn)文字、圖形的顯示。LED 顯示屏的點(diǎn)距足夠小時(shí),色彩足夠豐富時(shí)其實(shí)就形成了我們?nèi)粘5娘@示屏,比如 OLED 顯示屏其實(shí)原理也是類似的。之前報(bào)道過的大學(xué)宿舍樓通過控制每個(gè)房間的燈亮燈滅來形成文字的原理也是一樣的。
現(xiàn)在來看看 LED顯示文字是怎么回事,比如我們要 顯示島上碼農(nóng)的“島”字,在16x16的點(diǎn)陣上,通過排布得到的就是下面的結(jié)果(不同字體的排布會(huì)有些差別)。
因?yàn)槊恳恍惺?6個(gè)點(diǎn),我們可以對(duì)應(yīng)為16位二進(jìn)制數(shù),把黑色的標(biāo)記為1,灰色的標(biāo)記為0,每一行就可以得到一個(gè)二進(jìn)制數(shù)。比如上面的第一行第8列為1,其他都是0,對(duì)應(yīng)的二進(jìn)制數(shù)就是0000000100000000,對(duì)應(yīng)的16進(jìn)制數(shù)就是0x0100。把其他行也按這種方式計(jì)算出來,最終得到的“島”字對(duì)應(yīng)的是16個(gè)16進(jìn)制數(shù),如下所示。
[ 0x0100, 0x0200, 0x1FF0, 0x1010, 0x1210, 0x1150, 0x1020, 0x1000, 0x1FFC, 0x0204, 0x2224, 0x2224, 0x3FE4, 0x0004, 0x0028, 0x0010 ];
又了這個(gè)基礎(chǔ),我們就可以用 Flutter 繪制點(diǎn)陣圖形。
首先我們繪制一個(gè)“LED 面板”,也就是繪制一個(gè)有若干個(gè)點(diǎn)構(gòu)成的矩陣,這個(gè)比較簡(jiǎn)單,保持相同的間距,逐行繪制相同的圓即可,比如我們繪制一個(gè)16x16的點(diǎn)陣,實(shí)現(xiàn)代碼如下所示。
var paint = Paint()..color = Colors.grey; final dotCount = 16; final fontSize = 100.0; var radius = fontSize / dotCount; var startPos = Offset(size.width / 2 - fontSize, size.height / 2 - 2 * fontSize); for (int i = 0; i < dotCount; ++i) { var position = startPos + Offset(0.0, radius * i * 2); for (int j = 0; j < dotCount; ++j) { var dotPosition = startPos + Offset(radius * 2 * j, position.dy); canvas.drawCircle(dotPosition, radius, paint); } }
繪制出來的效果如下:
接下來是點(diǎn)亮對(duì)應(yīng)的位置來繪制文字了。上面我們講過了,每一行是一個(gè)16進(jìn)制數(shù),那么我們只需要判斷每一行的16進(jìn)制數(shù)的第幾個(gè) bit是1就可以了,如果是1就點(diǎn)亮,否則不點(diǎn)亮。點(diǎn)亮的效果用不同的顏色就可以了。 怎么判斷16進(jìn)制數(shù)的第幾個(gè) bit 是不是1呢,這個(gè)就要用到位運(yùn)算技巧了。實(shí)際上,我們可以用一個(gè)第 N 個(gè) bit 是1,其他 bit 都是0的數(shù)與要判斷的數(shù)進(jìn)行“位與”運(yùn)算,如果結(jié)果不為0,說明要判斷的數(shù)的第 N 個(gè) bit 是1,否則就是0。聽著有點(diǎn)繞,看個(gè)例子,我們以0x0100為例,按從第0位到第15位逐個(gè)判斷第0位和第15位是不是1,代碼如下:
for (i = 0 ; i < 16; ++i) { if ((0x0100 & (1 << i)) > 0) { // 第 i 位為1 } }
這里有兩個(gè)位操作,1 << i
是將1左移 i 位,為什么是這樣呢,因?yàn)檫@樣可以構(gòu)成0x0001,0x0002,0x0004,...,0x8000等數(shù)字,這些數(shù)字依次從第0位,第1位,第2位,...,第15位為1,其他位都是0。然后我們用這樣的數(shù)與另外一個(gè)數(shù)做位與運(yùn)算時(shí),就可以依次判斷這個(gè)數(shù)的第0位,第1位,第2位,...,第15位是否為1了,下面是一個(gè)計(jì)算示例,第11位為1,其他位都是0,從而可以 判斷另一個(gè)數(shù)的第11位是不是0。
通過這樣的邏輯我們就可以判斷一行的 LED 中第幾列應(yīng)該點(diǎn)亮,然后實(shí)現(xiàn)文字的“顯示”了,實(shí)現(xiàn)代碼如下。wordHex
是對(duì)應(yīng)字的16個(gè)16進(jìn)制數(shù)的數(shù)組。dotCount
的值是16,用于控制繪制16x16大小的點(diǎn)陣。每隔一行我們向下移動(dòng)一段直徑距離,每隔一列,我們向右移動(dòng)一段直徑距離。然后如果當(dāng)前繪制位置的數(shù)值對(duì)應(yīng)的 bit位為1,就用藍(lán)色繪制,否則就用灰色繪制。這里說一下為什么左移的時(shí)候要用dotCount - j - 1
,這是因?yàn)槔L制是從左到右的,而16進(jìn)制數(shù)的左邊是高位,而數(shù)字j是從小到大遞增的,因此要通過這種方式保證判斷的順序是從高位(第15位)到低位(第0位),和繪制的順序保持一致。
for (int i = 0; i < dotCount; ++i) { var position = startPos + Offset(0.0, radius * i * 2); for (int j = 0; j < dotCount; ++j) { var dotPosition = startPos + Offset(radius * 2 * j, position.dy); if ((wordHex[i] & ((1 << dotCount - j - 1))) != 0) { paint.color = Colors.blue[600]!; canvas.drawCircle(dotPosition, radius, paint); } else { paint.color = Colors.grey; canvas.drawCircle(dotPosition, radius, paint); } } }
繪制的結(jié)果如下所示。
接下來我們來考慮如何實(shí)現(xiàn)開篇說的類似的動(dòng)畫效果。實(shí)際上方法也很簡(jiǎn)單,就是先按照文字應(yīng)該“點(diǎn)亮”的 LED 的數(shù)量,先在隨機(jī)的位置繪制這么多數(shù)量的 LED,然后通過動(dòng)畫控制這些 LED 移動(dòng)到目標(biāo)位置——也就是文字本該繪制的位置。這個(gè)移動(dòng)的計(jì)算公式如下,其中 t 是動(dòng)畫值,取值范圍為0-1.
需要注意的是,隨機(jī)點(diǎn)不能在繪圖過程生成,那樣會(huì)導(dǎo)致每次繪制產(chǎn)生新的隨機(jī)位置,也就是初始位置會(huì)變化,導(dǎo)致上面的公式實(shí)際不成立,就達(dá)不到預(yù)期的效果。另外,也不能在 build
方法中生成,因?yàn)槊看嗡⑿?build 方法就會(huì)被調(diào)用,同樣會(huì)導(dǎo)致初始位置發(fā)生變化。所以,生成隨機(jī)位置應(yīng)該在 initState
方法完成。但是又遇到一個(gè)新問題,那就是 initState
方法里沒有 context
,拿不到屏幕寬高,所以不能直接生成位置,我們只需要生成一個(gè)0-1
的隨機(jī)系數(shù)就可以了,然后在繪制的時(shí)候在乘以屏幕寬高就得到實(shí)際的初始位置了。初始位置系數(shù)生成代碼如下:
@override void initState() { super.initState(); var wordBitCount = 0; for (var hex in dao) { wordBitCount += _countBitOne(hex); } startPositions = List.generate(wordBitCount, (index) { return Offset( Random().nextDouble(), Random().nextDouble(), ); }); ... }
wordBitCount
是計(jì)算一個(gè)字中有多少 bit 是1的,以便知道要繪制的 “LED” 數(shù)量。接下來是繪制代碼了,我們這次對(duì)于不亮的直接不繪制,然后要點(diǎn)亮的位置通過上面的位置計(jì)算公式計(jì)算,這樣保證了一開始繪制的是隨機(jī)位置,隨著動(dòng)畫的過程,逐步移動(dòng)到目標(biāo)位置,最終匯聚成一個(gè)字,就實(shí)現(xiàn)了預(yù)期的動(dòng)畫效果,代碼如下。
void paint(Canvas canvas, Size size) { final dotCount = 16; final fontSize = 100.0; var radius = fontSize / dotCount; var startPos = Offset(size.width / 2 - fontSize, size.height / 2 - fontSize); var paint = Paint()..color = Colors.blue[600]!; var paintIndex = 0; for (int i = 0; i < dotCount; ++i) { var position = startPos + Offset(0.0, radius * i * 2); for (int j = 0; j < dotCount; ++j) { // 判斷第 i 行第幾位不為0,不為0則繪制,否則不繪制 if ((wordHex[i] & ((1 << dotCount - j))) != 0) { var startX = startPositions[paintIndex].dx * size.width; var startY = startPositions[paintIndex].dy * size.height; var endX = startPos.dx + radius * j * 2; var endY = position.dy; var animationPos = Offset(startX + (endX - startX) * animationValue, startY + (endY - startY) * animationValue); canvas.drawCircle(animationPos, radius, paint); paintIndex++; } } } }
來看看實(shí)現(xiàn)效果吧,是不是很酷炫?完整源碼已提交至:繪圖相關(guān)源碼,文件名為:dot_font.dart
。
到此,相信大家對(duì)“Android怎么實(shí)現(xiàn)點(diǎn)匯聚成字的動(dòng)態(tài)效果”有了更深的了解,不妨來實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。