一、Path相关方法的运用
moveTo :移到下一次操作的起点位置
lineTo :添加上一个点到当前点之间的直线到Path
Close:连接第一个点连接到最后一个点,形成一个闭合区域
quadTo,cubicTo : (quadTo(x1,y1,x2,y2)中(x1,y1)为控制点的坐标,(x2,y2)为终点的坐标。分别为二次和三次贝塞尔曲线的方法
二、对于Path的运用(贝塞尔曲线)
贝塞尔曲线由一系列点来控制曲线状态的,这些点分为两类:数据点,控制点
一阶贝塞尔曲线:仅有数据点(A和B),是一条直线
-
二阶贝塞尔曲线:有两个数据点,一个控制点来描述的曲线
- 三阶贝塞尔曲线:两个数据点,两个控制点
三、绘制水波纹View
class WaveView : View{
constructor(context: Context) : super(context) {
initAttrs(context, null)
initPaintAndPath()
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
initAttrs(context, attrs)
initPaintAndPath()
}
constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
initAttrs(context, attrs)
initPaintAndPath()
}
private lateinit var headPaint: Paint
private lateinit var footPaint: Paint
private lateinit var borderPaint: Paint
private lateinit var headerPath: Path
private lateinit var footPath: Path
private lateinit var circlePath: Path // 主要用于剪切
/**
* 里波长
*/
private var headWaveWidth: Float = 600f
/**
* 里波高度
*/
private var headWaveHeight: Float = 45f
/**
* 里波的颜色
*/
private var headPaintColor: Int = Color.parseColor("#ff0000")
/**
* 外波长
*/
private var footWaveWidth: Float = 550f
/**
* 外波高度
*/
private var footWaveHeight: Float = 35f
/**
* 外波颜色
*/
private var footPaintColor: Int = Color.parseColor("#50ff0000")
private var pxWidth: Float = 0f
private var pxHeight: Float = 0f
/**
* 外围圆的线的宽度
*/
private var borderWidth: Float = 10f
/**
* 外围圆的线的颜色
*/
private var borderColor: Int = Color.parseColor("#ffffff")
/**
* 默认第一次重绘时里面波移动的距离
*/
private var headOffset = 12f
/**
* 默认第一次重绘时外面波移动的距离
*/
private var footOffset = 4f
/**
* 波的目前高度
*/
private var mChangeY: Float = 0f
/**
* 波的数量
*/
private var waveCount: Int = 0
/**
* 水位最终高度
*/
private var finalY: Float = 0f
/**
* 重绘值波峰移动距离的和
*/
private var moveSumHead: Float = 0f
private var moveSumFoot: Float = 0f
private var percent: Float = 0f //需要的最终百分比
private var currentPercent: Float = 0f //当前的百分比
private var timeOffset: Long = 0 // 重绘间隔时间
private var drawFlag = false
private fun initAttrs(mContext: Context, attrs: AttributeSet?) {
if(attrs == null) return
val typedArray = mContext.obtainStyledAttributes(attrs, R.styleable.WaveView)
borderWidth = typedArray.getDimension(R.styleable.WaveView_borderWidth, 10f)
headPaintColor = typedArray.getColor(R.styleable.WaveView_headColor, Color.parseColor("#ff0000"))
footPaintColor = typedArray.getColor(R.styleable.WaveView_footColor, Color.parseColor("#50ff0000"))
borderColor = typedArray.getColor(R.styleable.WaveView_borderColor, Color.parseColor("#ffffff"))
headWaveWidth = typedArray.getDimension(R.styleable.WaveView_headWaveWidth, 600f)
headWaveHeight = typedArray.getDimension(R.styleable.WaveView_headWaveHeight, 45f)
footWaveWidth = typedArray.getDimension(R.styleable.WaveView_footWaveWidth, 550f)
footWaveHeight = typedArray.getDimension(R.styleable.WaveView_footWaveHeight, 35f)
headOffset = typedArray.getDimension(R.styleable.WaveView_headOffset,12f)
footOffset = typedArray.getDimension(R.styleable.WaveView_footOffset,4f)
timeOffset = typedArray.getFloat(R.styleable.WaveView_timeOffset,2f).toLong()
typedArray.recycle()
}
private fun initPaintAndPath() {
headPaint = Paint(Paint.ANTI_ALIAS_FLAG)
headPaint.color = headPaintColor
headPaint.style = Paint.Style.FILL
footPaint = Paint(Paint.ANTI_ALIAS_FLAG)
footPaint.color = footPaintColor
footPaint.style = Paint.Style.FILL
borderPaint = Paint(Paint.ANTI_ALIAS_FLAG)
borderPaint.color = borderColor
borderPaint.strokeWidth = borderWidth
borderPaint.style = Paint.Style.STROKE
headerPath = Path()
footPath = Path()
circlePath = Path()
}
override fun onDraw(canvas: Canvas?) {
Log.d("yxy", "onDraw $headWaveWidth" )
//画边界圆
drawBorder(canvas ?: return)
//画波浪
drawWave(canvas ?: return)
}
fun setPercent(percent: Float) {
this.percent = percent
//根据百分比设置最终水位
finalY = (1 - percent) * pxHeight
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
pxWidth = measuredWidth.toFloat()
pxHeight = measuredHeight.toFloat()
mChangeY = Math.min(pxWidth, pxHeight)
//计算波峰个数
waveCount = Math.round(pxWidth / headWaveWidth + 1.5f) //保证至少2个波纹
}
/**
* 需要圆形以外的不显示,所以需要用到剪切功能
*/
private fun drawBorder(canvas: Canvas) {
circlePath.reset()
val radius = (pxWidth - 2 * borderWidth) / 2
val centerX = radius + borderWidth
val centerY = radius + borderWidth
// 使用canvas的方法剪切一个圆形显示,其余地方不显示
circlePath.addCircle(centerX, centerY, radius, Path.Direction.CW)
canvas.clipPath(circlePath)
canvas.drawCircle(centerX, centerY, radius, borderPaint)
}
private fun drawWave(canvas: Canvas?) {
headerPath.reset()
footPath.reset()
headerPath.moveTo(-headWaveWidth, mChangeY)
footPath.moveTo(-footWaveWidth, mChangeY)
for (i in 0..waveCount) {
//headWaveView路径
headerPath.quadTo(
(-headWaveWidth * 3 / 4) + i * headWaveWidth + moveSumHead,
mChangeY - headWaveHeight,
(-headWaveWidth / 2) + i * headWaveWidth + moveSumHead,
mChangeY
)
headerPath.quadTo(
(-headWaveWidth * 1 / 4) + i * headWaveWidth + moveSumHead,
mChangeY + headWaveHeight,
i * headWaveWidth + moveSumHead,
mChangeY
)
//footWave路径
footPath.quadTo(
(-footWaveWidth * 3 / 4) + i * footWaveWidth + moveSumFoot,
mChangeY - footWaveHeight,
(-footWaveWidth / 2) + i * footWaveWidth + moveSumFoot,
mChangeY
)
footPath.quadTo(
(-footWaveWidth * 1 / 4) + i * footWaveWidth + moveSumFoot,
mChangeY + footWaveHeight,
i * footWaveWidth + moveSumFoot,
mChangeY
)
}
//不断改变高度,实现逐渐水位上涨效果
mChangeY -= 1
if (mChangeY < finalY) mChangeY = finalY
moveSumHead += headOffset
moveSumFoot -= footOffset
if (moveSumHead >= headWaveWidth) moveSumHead = 0F
if (moveSumFoot <= 0) moveSumFoot = footWaveWidth
headerPath.lineTo(pxWidth, pxHeight)
headerPath.lineTo(0f, pxHeight)
headerPath.close()
footPath.lineTo(pxWidth, pxHeight)
footPath.lineTo(0f, pxHeight)
footPath.close()
canvas?.drawPath(headerPath, headPaint)
canvas?.drawPath(footPath, footPaint)
currentPercent = 1 - mChangeY / pxHeight
Log.d("yxy", "percent = $percent,currentPercent = $currentPercent")
if(drawFlag)postInvalidateDelayed(timeOffset)
}
fun start(){
drawFlag = true
postInvalidate()
}
}
//style
//布局文件: