網站首頁 編程語言 正文
前言
對于Android開發,實現貝塞爾曲線還是比較方便的,有對應的API供你調用。由于一階貝塞爾曲線就是一條直線,實際沒啥多大用處,因此,下面主要講解二階和三階。
二階貝塞爾曲線
在Android中,使用quadTo來實現二階貝塞爾
path.reset()
path.moveTo(startX, startY)
path.quadTo(currentX, currentY, endX, endY)
canvas.drawPath(path, curvePaint)
startX和startY,endX和endY為兩個固定點,currentX和currentY就是控制點,通過改變控制點的位置來改變二階貝塞爾曲線的形狀。
a點和b點就是固定點,c點是控制點,我們可以改變c點的位置來改變曲線的形狀。
override fun onTouchEvent(event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE -> {
currentX = event.x
currentY = event.y
postInvalidate()
}
}
return true
}
三階貝塞爾曲線
在Android中,使用cubicTo來實現三階貝塞爾
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
path.reset()
path.moveTo(startX, startY)
path.cubicTo(fixedX1, fixedY1, fixedX2, fixedY2, endX, endY)
canvas.drawPath(path, curvePaint)
//繪制輔助線
drawHelpLine(canvas)
}
override fun onTouchEvent(event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE -> {
//divideLine區分觸摸點是左邊還是右邊
if (event.x < divideLine) {
fixedX1 = event.x
fixedY1 = event.y
} else {
fixedX2 = event.x
fixedY2 = event.y
}
postInvalidate()
}
}
return true
}
其中,startX和startY,endX和endY為兩個固定點,fixedX1和fixedY1,fixedX2和fixedY2分別為兩個控制點,通過改變控制點的位置來改變三階貝塞爾曲線的形狀。
a點和b點就是固定點,c點和d點是控制點,我們可以改變c點或d點的位置來改變曲線的形狀。
OK,貝塞爾曲線的基礎到此就講完了,下面來個實戰,體驗一下貝塞爾曲線的絲滑吧!
關于貝塞爾曲線,最典型的應用就是波浪球了,那咱們也來整一個,先上圖
首先裁剪一下畫布,變為圓形
val circlePath = Path()
circlePath.addCircle(width / 2f, height / 2f, width / 2f, Path.Direction.CW)
canvas.clipPath(circlePath)
Path.Direction.CW:沿順時針方向繪制,Path.Direction.CCW:沿逆時針方向繪制
以View為中心,畫圓
canvas.drawCircle(width / 2f, height / 2f, width / 2f, circularPaint)
利用二階貝塞爾,繪制波浪,起點為屏幕外,circleLen為曲線1/4周期長度
private val startPoint = Point(-4 * circleLen, 0)
根據進度改變起點坐標的y值,控制點為曲線的頂部和底部,循環繪制,然后構建曲線之下的封閉區域,填充
//根據進度改變起點坐標的y值
startPoint.y = ((1 - (progress / 100.0)) * height).toInt()
//移動到起點
wavePath.moveTo(startPoint.x.toFloat(), startPoint.y.toFloat())
var j = 1
//循環繪制曲線
for (i in 1..8) {
val controlX = (startPoint.x + circleLen * j).toFloat()
//波頂和波底
val controlY =
if (i % 2 == 0) (startPoint.y + waveHeight).toFloat() else (startPoint.y - waveHeight).toFloat()
//二階貝塞爾
wavePath.quadTo(
controlX,
controlY,
(startPoint.x + circleLen * 2 * i).toFloat(),
startPoint.y.toFloat()
)
j += 2
}
//繪制封閉的區域
wavePath.lineTo(width.toFloat(), height.toFloat())
wavePath.lineTo(startPoint.x.toFloat(), height.toFloat())
wavePath.lineTo(startPoint.x.toFloat(), startPoint.y.toFloat())
wavePath.close()
canvas.drawPath(wavePath, wavePaint)
wavePath.reset()
//走完一周回到原點
startPoint.x =
if (startPoint.x + translateX >= 0) -circleLen * 4 else startPoint.x + translateX
這里是設置每隔100ms,進度加一
progress = if (progress >= 100) 0 else progress + 1
postInvalidateDelayed(100)
全部代碼如下
class ProgressBallView : View {
//曲線1/4周期的長度
private val circleLen = DensityUtils.dp2px(context, 53)
//曲線高度
private val waveHeight = DensityUtils.dp2px(context, 27)
//默認的長寬值
private val defaultSize = DensityUtils.dp2px(context, 300)
//進度
private var progress = 0
//平移的長度
private val translateX = circleLen / 4
//圓形Paint
private val circularPaint = Paint()
//波浪Paint
private val wavePaint = Paint()
//波浪的路徑
private val wavePath = Path()
//曲線的起始坐標
private val startPoint = Point(-4 * circleLen, 0)
constructor(context: Context) : super(context)
constructor(context: Context, attributeSet: AttributeSet) : super(context, attributeSet) {
initPaint()
}
private fun initPaint() {
with(circularPaint) {
isAntiAlias = true
color = Color.GRAY
}
with(wavePaint) {
isAntiAlias = true
color = Color.RED
}
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
var viewWidth = measureView(widthMeasureSpec)
var viewHeight = measureView(heightMeasureSpec)
//取最小的,作為長寬
viewWidth = min(viewWidth, viewHeight)
viewHeight = viewWidth
setMeasuredDimension(viewWidth, viewHeight)
}
private fun measureView(measureSpec: Int): Int {
val mode = MeasureSpec.getMode(measureSpec)
return if (mode == MeasureSpec.AT_MOST || mode == MeasureSpec.EXACTLY) {
MeasureSpec.getSize(measureSpec)
} else {
defaultSize
}
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
//裁剪畫布為圓形
cutCanvas(canvas)
//繪制圓形
drawRound(canvas)
//繪制波浪
drawWave(canvas)
//自動增長進度
autoGrow()
}
//進度從0-100,自動增長
private fun autoGrow() {
progress = if (progress >= 100) 0 else progress + 1
postInvalidateDelayed(100)
}
//裁剪畫布為圓形
private fun cutCanvas(canvas: Canvas) {
val circlePath = Path()
circlePath.addCircle(width / 2f, height / 2f, width / 2f, Path.Direction.CW)
canvas.clipPath(circlePath)
}
//繪制圓形
private fun drawRound(canvas: Canvas) {
canvas.drawCircle(width / 2f, height / 2f, width / 2f, circularPaint)
}
//繪制波浪
private fun drawWave(canvas: Canvas) {
//根據進度改變起點坐標的y值
startPoint.y = ((1 - (progress / 100.0)) * height).toInt()
//移動到起點
wavePath.moveTo(startPoint.x.toFloat(), startPoint.y.toFloat())
var j = 1
//循環繪制曲線
for (i in 1..8) {
val controlX = (startPoint.x + circleLen * j).toFloat()
//波頂和波底
val controlY =
if (i % 2 == 0) (startPoint.y + waveHeight).toFloat() else (startPoint.y - waveHeight).toFloat()
//二階貝塞爾
wavePath.quadTo(
controlX,
controlY,
(startPoint.x + circleLen * 2 * i).toFloat(),
startPoint.y.toFloat()
)
j += 2
}
//繪制封閉的區域
wavePath.lineTo(width.toFloat(), height.toFloat())
wavePath.lineTo(startPoint.x.toFloat(), height.toFloat())
wavePath.lineTo(startPoint.x.toFloat(), startPoint.y.toFloat())
wavePath.close()
canvas.drawPath(wavePath, wavePaint)
wavePath.reset()
//走完一周回到原點
startPoint.x =
if (startPoint.x + translateX >= 0) -circleLen * 4 else startPoint.x + translateX
}
}
原文鏈接:https://blog.csdn.net/qq_45485851/article/details/124692717
相關推薦
- 2022-11-19 Golang接口使用教程詳解_Golang
- 2023-11-24 局部路由守衛path守衛
- 2022-04-09 用C語言實現簡單的計算器功能_C 語言
- 2022-10-27 Python?Pandas中布爾索引的用法詳解_python
- 2023-04-28 react如何獲取URL中參數_React
- 2022-03-17 C#表達式樹Expression基礎講解_C#教程
- 2022-09-08 python?logging模塊的分文件存放詳析_python
- 2022-10-16 Qt實現進程間通信_C 語言
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支