android自定义view书籍,Android自定义View--翻书控件(一)

本文介绍了作者在重读《封神演义》后,如何利用Canvas的clipPath功能,通过贝塞尔曲线计算裁剪区域,实现在触摸事件中动态调整显示内容的开发过程。作者分享了关键代码段和翻页控制逻辑,展示了前端交互设计的一个实践案例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

0.前言

最近重看了一遍封神演义,感觉QQ阅读那个翻书的效果挺好的,准备做一个。上周五下午用了两个小时只写了一部分功能,以后有时间再完善

1.分析

先看效果图

a8f7fa92927af581791cb9fb4613081a.gif

这个空间,说简单也简单,说难也难,简单就在于这个效果主要就是依赖canvas的clippath才见到部分canvas,难就难在裁剪区域的点的计算,上传一张我自己画得草图

b29c0f3dee05d60d36a3d08c5afd2fc6.png

其中a点就是手指的位置,b点就是控件右下角,cd为ab的垂直平分线,e为交点,gh为ae的垂直平分线,f为交点,i为ac与gh的交点,j为ad与gh的交点,其中icg为贝塞尔曲线,c为控制点,jdh为贝塞尔曲线,d为控制点,构建一个aigbh的path,通过canvas裁剪这个path

2.代码

2.1计算点

private fun calPoints(x: Float,y:Float) {

pointA.x = x

pointA.y = y

calPointB()

pointE.x = (pointA.x + pointB.x) / 2

pointE.y = (pointA.y + pointB.y) / 2

pointK.x = pointE.x

pointK.y = pointB.y

pointC.x = pointK.x - (pointK.y - pointE.y) * (pointK.y - pointE.y) / (pointB.x - pointK.x)

pointC.y = pointB.y

pointG.x = pointC.x - (pointB.x - pointC.x) / 2

pointG.y = pointB.y

pointL.x = pointB.x

pointL.y = pointE.y

pointD.x = pointB.x

pointD.y = pointE.y - (pointL.x - pointE.x) * (pointL.x - pointE.x) / (pointB.y - pointL.y)

pointF.x = (pointA.x + pointE.x) / 2

pointF.y = (pointA.y + pointE.y) / 2

pointH.x = pointB.x

pointH.y = pointD.y - (pointB.y - pointD.y) / 2

pointI = getIntercetPoint(pointA, pointC, pointH, pointG)

pointJ = getIntercetPoint(pointA, pointD, pointH, pointG)

}

private fun calPointB() {

when {

this.touchPos == TOUCH_END -> {

pointB.x = width.toFloat()

pointB.y = height.toFloat()

}

this.touchPos == TOUCH_TOP -> {

pointB.x = width.toFloat()

pointB.y = 0f

}

else -> {

pointB.x = pointA.x

pointB.y = height.toFloat()

}

}

}

private fun getIntercetPoint(point1: Point, point2: Point, point3: Point, point4: Point): Point {

var x1 = point1.x

var y1 = point1.y

var x2 = point2.x

var y2 = point2.y

var x3 = point3.x

var y3 = point3.y

var x4 = point4.x

var y4 = point4.y

var pointX =

((x1 - x2) * (x3 * y4 - x4 * y3) - (x3 - x4) * (x1 * y2 - x2 * y1)) / ((x3 - x4) * (y1 - y2) - (x1 - x2) * (y3 - y4))

var pointY =

((y1 - y2) * (x3 * y4 - x4 * y3) - (x1 * y2 - x2 * y1) * (y3 - y4)) / ((y1 - y2) * (x3 - x4) - (x1 - x2) * (y3 - y4))

return Point(pointX, pointY)

}

复制代码

每个点怎么算代码已经很清楚了,我也就不再赘述了。

2.2绘制

override fun draw(canvas: Canvas?) {

canvas?.let {

it.save()

calPath()

it.clipPath(path, Region.Op.XOR)

super.draw(it)

it.restore()

}

}

private fun calPath() {

path.reset()

path.moveTo(pointA.x, pointA.y)

path.lineTo(pointI.x, pointI.y)

path.quadTo(pointC.x, pointC.y, pointG.x, pointG.y)

path.lineTo(pointB.x, pointB.y)

path.lineTo(pointH.x, pointH.y)

path.quadTo(pointD.x, pointD.y, pointJ.x, pointJ.y)

path.lineTo(pointA.x, pointA.y)

}

复制代码

复写draw方法,通过calPath构建ajgbhja这样的一个path,裁减掉这部分后进行绘制。

2.3最大翻页距离

当g点x的坐标为0是,就停止移动了

override fun onTouchEvent(event: MotionEvent?): Boolean {

when (event?.action) {

MotionEvent.ACTION_DOWN -> {

lastTouchX=event.x

lastTouchY=event.y

when {

event.y < height / 3 -> this.touchPos = TOUCH_TOP

event.y > height - height / 3 -> this.touchPos = TOUCH_END

else -> this.touchPos = TOUCH_MID

}

}

MotionEvent.ACTION_MOVE -> {

calPoints(event.x,event.y)

if(pointG.x<=0)

{

calPoints(lastTouchX,lastTouchY)

}

else{

lastTouchX=event.x

lastTouchY=event.y

}

postInvalidate()

}

MotionEvent.ACTION_UP -> {

}

}

return true

}

复制代码

3最后

最近状态很差,写代码总是写写停停,不能静下心来,所以这个控件有很多没有做完,以后有时间再弄吧。附github地址

关注我的公众号

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值