android畫板筆鋒實現

2021-09-03 01:36:08 字數 3248 閱讀 9649

在安卓繪圖中,path是乙個很常用的類,使用它可以實現基本的畫線功能,但是自己用path畫出來的同一條線段大小是不會改變的。如果做書寫型別的軟體,當然想要實現更好的逼真的書寫效果,在實際書寫過程中,我們的筆跡通常是帶有筆鋒的。因此,這篇文章主要講解一下具體的實現思路,具體**就不放上來了,有興趣的可以私密我交流一下。

要實現筆鋒,首先要獲取乙個變化的值,這個值能決定畫線的大小。在安卓手機中,能充當這個值的目前我能想到的有壓感和滑動速度,而壓感在某些安卓裝置是無法獲取的,因此我採用滑動速度作為這個變化值。從實際測試來看,這個值是完全可以做到**筆鋒的。速度可以通過時間和距離計算出來,然後可以根據這個速度,計算寬度的大小,速度越快,兩點間的線條寬度越小,反之則越大。線條採用貝塞爾曲線來畫,可以得到更為圓滑的曲線,貝塞爾曲線可以手動畫點成線,也可以直接path.quadto(),但是path.quadto()無法控制同一條貝塞爾曲線的寬度,因此理論上無法實現大小變化。所以可以採用畫點成線的方案,控制每一點的大小,優點是筆鋒比較完美,缺點是運算量較大,在一些效能不是很好的安卓裝置中表現特別差。另乙個方案是先用path.quadto()畫一條貝塞爾曲線,然後再利用pathmeasure.getsegment()分割path,通過設定引數,把這段貝塞爾曲線的path分割為固定長度的新path,然後給新path新增不同寬度的畫筆即可。這樣做的優點是效率大大提高,可以適應性能低的裝置,缺點是表現效果沒有畫點好,如果長度沒有適配好,線條會出現段落感。

lastvelocity

初始速度或上一條貝塞爾曲線速度

originalwidth

初始寬度

lastwidth

上一條貝塞爾曲線寬度

time

點的觸控時間

minwidth

最小寬度

velocity_weight

權重dst_width

寬度變化範圍量

velocity

當前速度

筆跡初始速度要根據情況的不同而設定不同的值,在首次畫線的時候,初始速度為0。但是如果是筆鋒線段擦除後重繪,則擦除分割出來的線段,每一段的初始速度是不一樣的,第一段初始速度仍然是0,而分割出的其他筆跡線段的初始速度是上一部分線段的速度。注:上一部分線段為已被擦除的線段,速度被記錄為lastvelocity

初始寬度就是白板設定的筆跡寬度originalwidth,用於筆鋒擦除。因為筆鋒擦除後分割出來的筆跡的開始寬度需要根據未分割的整段筆鋒筆跡來計算,因此即使筆跡的前半部分已經被擦除,有了初始寬度和初始速度,依然可以計算出線段開始寬度,這樣可以保證在擦除筆跡部分線段後其他線段還能保持不變。

二階貝塞爾曲線是兩個點加乙個控制點形成的曲線,因此三個點形成一條貝塞爾曲線,而一條筆跡則由n條貝塞爾曲線形成,橡皮擦擦除實際上是擦除某一條或者n條貝塞爾曲線。因此如果某一條貝塞爾曲線前面的貝塞爾曲線被擦除了,那麼它就作為新筆跡的初始線段,它需要乙個初始速度,也就是前面所說的初始速度lastvelocity,實際上就是上一條貝塞爾曲線的速度。

同上一段線段速度,也是儲存的上一條貝塞爾曲線的最後寬度lastwidth。

在ontouch觸控獲取點的時候,可以把每個點的觸控時間time記錄下來,則用兩個點就可以計算出時間和距離,從而得出兩點間的速度。

在會議平板上,1個畫素的線條在視覺上表現不好,因此可以設定乙個最小寬度,即使滑動速度非常快,線條的寬度大小也限定在這個最小寬度之上,目前設定minwidth為2。

velocity = (velocity_weight * velocity + (1-velocity_weight) * lastvelocity);
**中的velocity_weight則為當前採用的權重值,值越大寬度變化越明顯,範圍在0~1之間。

在現實畫筆鋒的情況中,不存在在很短的距離中出現大小變化巨大的線段,因此前一條的貝塞爾曲線大小不能和後一段貝塞爾曲線大小相差太大,需要設定乙個範圍值,超出範圍值則強制限定在範圍值內。如下:

newwidth = math.min(newwidth, lastwidth + dst_width);

newwidth = math.max(newwidth, lastwidth -dst_width);

**中lastwidh+dst_width和lastwidth-dst_width就是寬度變化範圍大小,dst_width就是相對上一條線段的寬度可變化量。

由於兩點之間範圍不是很大,因此採用二階貝塞爾曲線和三階貝塞爾曲線在觀感上差別不大,而二階貝塞爾曲線畫起來要比三階貝塞爾曲線效率快很多,因此採用二階貝塞爾曲線畫線。二階貝塞爾曲線的公式為:

b(t)=(1-t)²p0+2t(1-t)p1+t²p2, t∈[0,1]

其中p0代表座標點第一點,p1代表控制點,p2代表座標點第二點,t=i/steps,steps是總共需要補充的點的數量,i是當前補充到的第i個點的索引。根據這個公式就可以計算出當前需要補充的點和點的大小:

dwidth = endwidth - startwidth;

width = startwidth + tt * dwidth

第二方案在第一方案的基礎上,去掉了手動計算貝塞爾曲線和畫每乙個點的大小的繪圖方案。並且不再在筆跡物件中儲存初始速度和原始寬度,而是儲存當前線段大小。一條筆跡是由很多部分的貝塞爾曲線組成,那麼只需要在第一次畫筆跡抬手後,計算每一小段貝塞爾曲線的寬度,然後從組成貝塞爾曲線的三個點中,取第乙個點的時間值,變更為當前貝塞爾曲線寬度值,則重繪的時候直接取這個寬度值作為新的寬度即可省略掉重複計算寬度(重繪的時候不需要再次計算,因此不需要原來的time值)。

lastvelocity初始速度或上一條貝塞爾曲線速度

originalwidth

初始寬度

lastwidth

上一條貝塞爾曲線寬度

time

點的觸控時間或當前貝塞爾曲線的寬度值

minwidth

最小寬度

velocity_weight

權重畫貝塞爾曲線直接使用path.quadto()函式即可,不過需要使用pathmeasure的getlength()函式獲取當前貝塞爾曲線長度,如果小於乙個設定長度,則直接畫在畫布上,然後繼續下一條貝塞爾曲線。如果大於設定長度,則使用pathmeasure的getsegment()函式擷取設定長度的path,然後重新設定paint大小,畫這條path,重複,直到擷取完畢,繼續下一條貝塞爾曲線。迴圈畫path**如下:

for (float i = 0, j = 1; i < pathlength; i += dl, j++)

android毛筆筆鋒

任何畫線的程式,都是先在介面上獲取若干不連續的點,然後將這些點連成線。一些常見的筆型比較好實現,比如說鉛筆 鋼筆等等,這類筆型的線條的寬度和線條的顏色是固定的,只需要將點連線成固定顏色和固定寬度的線即可。毛筆是比較特殊的筆型,要實現好毛筆效果,有幾點比較特殊,需要考慮。1.同一筆畫內,線條各處的粗細...

Android採用雙緩衝實現畫板

所謂的雙緩衝技術其實很簡單,當程式需要在指定的view上進行繪製時,程式並不需要直接繪製到該view元件,而是先繪製到乙個記憶體中的bitmap上 就是緩衝 等記憶體中的bitmap繪製好之後,再一次性將bitmap繪製到view元件上。1.定義乙個記憶體中,將他作為緩衝區bitmap cacheb...

vue canvas實現簡易畫板

需求 預設後台返回的資料渲染到畫布上,然後使用者可以編輯重新畫線,並且可以點選要移除的線條進行移除。現在做的互動是選中需要移除的線條高亮顯示,然後雙擊進行移除。id mycanvas width 600px height 380px class canvas mousedown drawlinemo...