Android自定義View--翻書控制元件(一)
0.前言
最近重看了一遍封神演義,感覺QQ閱讀那個翻書的效果挺好的,準備做一個。上週五下午用了兩個小時只寫了一部分功能,以後有時間再完善
1.分析
先看效果圖

image
這個空間,說簡單也簡單,說難也難,簡單就在於這個效果主要就是依賴canvas的clippath才見到部分canvas,難就難在裁剪區域的點的計算,上傳一張我自己畫得草圖

image
其中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最後
最近狀態很差,寫程式碼總是寫寫停停,不能靜下心來,所以這個控制元件有很多沒有做完,以後有時間再弄吧。附 ofollow,noindex">github 地址

image
關注我的公眾號