Android利用控制點(diǎn)的拖拽畫(huà)一個(gè)粽子
前言
上一篇我們通過(guò)Listener獲取觸控點(diǎn)的位置作為貝塞爾曲線的控制點(diǎn),實(shí)現(xiàn)曲線的交互式繪制。不過(guò),上一篇有個(gè)缺陷,控制點(diǎn)繪制完成后只能撤銷,沒(méi)法修改,如果要調(diào)整繪制的圖形的話會(huì)非常麻煩,這一篇我們來(lái)實(shí)現(xiàn)控制點(diǎn)的拖拽式移動(dòng),動(dòng)態(tài)調(diào)整位置來(lái)調(diào)整繪制的圖形。
實(shí)現(xiàn)邏輯
上一篇的主要代碼我們不做更改,主要是需要實(shí)現(xiàn)控制點(diǎn)的拖拽式移動(dòng),移動(dòng)過(guò)程中動(dòng)態(tài)繪制新的曲線。不過(guò)由于繪制過(guò)程中不能同時(shí)移動(dòng)點(diǎn),因此需要有個(gè)完成繪制的控制,完成繪制后才支持拖拽控制點(diǎn)。拖拽控制點(diǎn)實(shí)現(xiàn)這里有兩個(gè)主要邏輯:
- 控制點(diǎn)的命中判斷:即拖拽開(kāi)始時(shí)判斷哪個(gè)控制點(diǎn)被命中,需要移動(dòng)。
- 監(jiān)聽(tīng)觸控位置的移動(dòng)過(guò)程:移動(dòng)過(guò)程中動(dòng)態(tài)繪制新的圖形,以便直接看到對(duì)應(yīng)的效果。
控制點(diǎn)的命中判斷在完成繪制后,首先需要監(jiān)聽(tīng)觸控按下事件,看看觸控點(diǎn)是否覆蓋了某個(gè)控制點(diǎn)的觸控響應(yīng)范圍,同時(shí)對(duì)于距離較近的點(diǎn),可能會(huì)同時(shí)命中多個(gè)點(diǎn)的觸控響應(yīng)范圍,這個(gè)時(shí)候需要取距離最近的那個(gè)點(diǎn)。對(duì)于觸控范圍,我們定義為每個(gè)觸控點(diǎn)的周邊10個(gè)像素點(diǎn)。命中觸控點(diǎn)的實(shí)現(xiàn)代碼如下:
int checkPointToMove(Offset pressedPoint) {
// 控制點(diǎn)非空才查找
if (points.isNotEmpty) {
var pointsToCheck = <Offset>[];
final maxDistance = 10.0;
// 查找觸控響應(yīng)范圍內(nèi)的控制點(diǎn)
for (Offset p in points) {
if ((p.dx - pressedPoint.dx).abs() < maxDistance &&
(p.dy - pressedPoint.dy).abs() < maxDistance) {
pointsToCheck.add(p);
}
}
// 未找到
if (pointsToCheck.length == 0) {
return -1;
} else if (pointsToCheck.length == 1) {
// 只有一個(gè)點(diǎn),直接返回
return points.indexOf(pointsToCheck[0]);
} else {
// 有多個(gè)點(diǎn)命中,找到距離最近的點(diǎn)返回
Offset point = pointsToCheck[0];
var distance = distanceBetween(pointsToCheck[0], pressedPoint);
for (int i = 1; i < pointsToCheck.length; i++) {
var newDistance = distanceBetween(pointsToCheck[i], pressedPoint);
if (newDistance < distance) {
point = pointsToCheck[i];
distance = newDistance;
}
}
return points.indexOf(point);
}
}
return -1;
}
移動(dòng)過(guò)程的處理就比較簡(jiǎn)單了,我們已經(jīng)找到了命中的控制點(diǎn),那就在觸控位置移動(dòng)監(jiān)聽(tīng)響應(yīng)方法onPointerMove中更新控制點(diǎn)位置,并重新繪制即可,代碼如下,其中indexOfPointToMove是一個(gè)狀態(tài)變量,即找到的控制點(diǎn)下標(biāo):
onPointerMove: ((event) {
if (indexOfPointToMove != -1) {
points[indexOfPointToMove] = event.localPosition;
setState(() {});
}
}),應(yīng)用
邏輯完成了,我們就來(lái)做一個(gè)繪制應(yīng)用吧??紤]端午節(jié)快到了,我們嘗試來(lái)繪制一個(gè)粽子的線條畫(huà)看看。下面是調(diào)整前后的對(duì)比效果以及調(diào)整過(guò)程的動(dòng)圖,可以看到,調(diào)整后的還是更像粽子一些。



總結(jié)
本篇介紹了如何通過(guò)拖拽調(diào)整貝塞爾曲線繪制的控制點(diǎn)來(lái)調(diào)整圖形的繪制,實(shí)際上很多繪圖都可能用到拖拽式的控制點(diǎn)位的調(diào)整,比如電子圍欄的設(shè)置。實(shí)際上主要的代碼是判斷觸控位置命中了具體哪個(gè)控制點(diǎn)。本篇代碼已經(jīng)上傳至:繪圖相關(guān)代碼。
以上就是Android利用控制點(diǎn)的拖拽畫(huà)一個(gè)粽子的詳細(xì)內(nèi)容,更多關(guān)于Android粽子的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android自定義View實(shí)現(xiàn)拖動(dòng)自動(dòng)吸邊效果
這篇文章主要為大家詳細(xì)介紹了Android自定義View實(shí)現(xiàn)拖動(dòng)自動(dòng)吸邊效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-06-06
詳解Android Activity之間切換傳遞數(shù)據(jù)的方法
這篇文章主要介紹了詳解Android Activity之間切換傳遞數(shù)據(jù)的方法 的相關(guān)資料,需要的朋友可以參考下2016-04-04
實(shí)時(shí)獲取股票數(shù)據(jù)的android app應(yīng)用程序源碼分享
本文我們分享一個(gè)實(shí)時(shí)獲取股票數(shù)據(jù)的android app應(yīng)用程序源碼分享,可以作為學(xué)習(xí)使用,本文貼出部分重要代碼,需要的朋友可以參考下本文2015-09-09
解析Android截取手機(jī)屏幕兩種實(shí)現(xiàn)方案
這篇文章主要介紹了解析Android截取手機(jī)屏幕兩種實(shí)現(xiàn)方案,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2017-04-04
分析Android多主題顏色的相關(guān)問(wèn)題
這篇文章總結(jié)了在Android開(kāi)發(fā)多主題顏色的時(shí)候會(huì)遇到的一些問(wèn)題,然后給出解決方案,讓大家可以解決問(wèn)題,有需要的下面一起來(lái)看看吧。2016-08-08
Android xUtils更新到3.0后的基本使用規(guī)則詳解
xUtils是基于android的開(kāi)發(fā)框架,簡(jiǎn)化了很多的開(kāi)發(fā)步驟,可以說(shuō)是非常好的開(kāi)發(fā)工具。下面小編給大家?guī)?lái)了Android xUtils更新到3.0后的基本使用規(guī)則詳解,感興趣的朋友一起學(xué)習(xí)吧2016-08-08
Android Studio 一鍵生成Json實(shí)體類教程
這篇文章主要介紹了Android Studio 一鍵生成Json實(shí)體類教程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-04-04

