var cardRotateFunc: IFunc? = null
/**
- 阴影大小变化函数
/
var cardShadowSizeFunc: IFunc? = null
/* - 阴影距离变化函数
/
var cardShadowDistanceFunc: IFunc? = null
/* - 配置各个函数
*/
private fun configFunc() {
cardRotateFunc = CardRotateFunc()
with(cardRotateFunc!!) {
inParamMin = 0F
inParamMax = cardHeight * 2
outParamMin = 0F
outParamMax = 180F
initValue = 45F
}
cardShadowSizeFunc = CardShadowSizeFunc()
with(cardShadowSizeFunc!!) {
inParamMin = 0F
inParamMax = 180F
outParamMax = 50F
outParamMin = 0F
initValue = 10F
}
cardShadowDistanceFunc = CardShadowDistanceFunc()
with(cardShadowDistanceFunc!!) {
inParamMin = 0F
inParamMax = 180F
outParamMax = 50F
outParamMin = 0F
initValue = 10F
}
}
复制代码
2.5.3 阴影变化
为了更好地模拟3D效果,卡片阴影也存在微小的变化
/**
- 根据旋转角度计算阴影大小、距离
*/
private fun executeShadowFunc(rotate: Float) {
cardShadowSizeFunc?.let {
cardShadowSize = it.execute(rotate)
}
cardShadowDistanceFunc?.let {
cardShadowDistance = it.execute(rotate)
}
}
复制代码
2.5.4 数字图片绘制
数字的绘制就是图片的绘制,需要注意的是中间活动卡片在上翻或下翻转超过90度时,绘制的数字需要改变,涉及到图片的水平镜像翻转,调用matrix.postScale(-1F, 1F)实现。以下翻为例,代码如下:
if (curState == STATE_DOWN_ING) {
//往下翻
if (abs(cardRotateFunc!!.initValue - rotateX) >= 90F) {
//绘制前一个数字
if (curShowNum - 1 >= 0) {
tempBm = Bitmap.createBitmap(numBms[curShowNum - 1], 0, 0, curNumBm.width, curNumBm.height, matrix, false)
} else {
tempBm = Bitmap.createBitmap(numBms[0], 0, 0, curNumBm.width, curNumBm.height, matrix, false)
}
} else {
tempBm = Bitmap.createBitmap(numBms[curShowNum], 0, 0, curNumBm.width, curNumBm.height, matrix, false)
}
}
tempBm?.let {
drawBitmap(it, Rect(0, it.height / 2, it.width, it.height), rectF, mPaint)
}
复制代码
2.6 实现交互
2.6.1 上下翻转
逻辑主要在onTouchEvent方法中,通过中间卡片初始角度和当前的翻转角度判断是上翻还是下翻
/**
- 手指按下的初始坐标
*/
private var downX: Float = 0F
private var downY: Float = 0F
private var offsetY: Float = 0F
override fun onTouchEvent(event: MotionEvent?): Boolean {
when (event?.action) {
MotionEvent.ACTION_DOWN -> {
downX = event.x
downY = event.y
if (downY >= height / 2) {
//绘制下方的mid card
rotateX = 0F
curState = STATE_UP_ING
} else {
rotateX = 180F
curState = STATE_DOWN_ING
}
resetInitValue()
postInvalidate()
}
MotionEvent.ACTION_MOVE -> {
offsetY = event.y - downY
executeFunc(offsetY)
postInvalidate()
}
MotionEvent.ACTION_UP -> {
//判断是上翻还是下翻
if (rotateX >= 90F) {
if (abs(cardRotateFunc!!.initValue - rotateX) >= 90F) {
if (curShowNum + 1 <= 9) {
startCardUpAnim(curShowNum + 1)
} else {
curShowNum = 9
startCardDownAnim(9)
}
} else {
startCardUpAnim(curShowNum)
}
} else {
if (abs(cardRotateFunc!!.initValue - rotateX) >=
90F) {
if (curShowNum - 1 >= 0) {
startCardDownAnim(curShowNum - 1)
} else {
curShowNum = 0
startCardUpAnim(0)
}
} else {
startCardDownAnim(curShowNum)
}
}
downX = 0F
downY = 0F
}
else -> {
}
}
return true
}
复制代码
2.6.2 翻页动画、阴影动画
由于采用了坐标计算框架,动画的实现就变得非常简单了,控制好offset变量,即可控制中间卡片的翻转角度、阴影距离、阴影大小。以上翻动画为例:
/**
- 卡片上翻动画
*/
private fun startCardUpAnim(curNum: Int) {
cardRotateAnim?.cancel()
cardRotateAnim = ValueAnimator.ofFloat(rotateX, 180F)
with(cardRotateAnim!!) {
duration = 400L
addUpdateListener {
rotateX = it.animatedValue as Float
executeShadowFunc(rotateX)
postInvalidate()
}
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator?) {
super.onAnimationEnd(animation)
resetInitValue()
curState = STATE_NORMAL
curShowNum = curNum
}
})
start()
}
}
复制代码
2.6.3 显示数字的变化
当动画结束时,当前显示数字才是真正地发生了改变,对curShowNum进行赋值即可。也可以方便添加数字变化时的监听暴露给调用者。
2.7 加点细节
2.7.1 边界控制
当前数字为0时,无法再向下翻转,这时无需绘制上卡片;同样的,当前数字为9时,无需绘制下卡片
private fun judgeState(state: Int) {
when (state) {
STATE_NORMAL -> {
isNeedDrawMidCard = false
isNeedDrawUpCard = true
isNeedDrawDownCard = true
}
STATE_UP_ING -> {
isNeedDrawMidCard = true
if (curShowNum + 1 > 9) {
isNeedDrawDownCard = false
}
}
STATE_DOWN_ING -> {
isNeedDrawMidCard = true
if (curShowNum - 1 < 0) {
isNeedDrawUpCard = false
}
}
}
}
复制代码
2.7.2 翻页回弹
处于边界数字时,用户继续进行翻转操作,需要进行回弹至原位。
if (curShowNum + 1 <= 9) {
startCardUpAnim(curShowNum + 1)
} else {
curShowNum = 9
startCardDownAnim(9)
}
三、后记
}
}
复制代码
2.7.2 翻页回弹
[外链图片转存中…(img-xnewZwbz-1642753565232)]
处于边界数字时,用户继续进行翻转操作,需要进行回弹至原位。
if (curShowNum + 1 <= 9) {
startCardUpAnim(curShowNum + 1)
} else {
curShowNum = 9
startCardDownAnim(9)
}