关注 安卓007 ,免费获取全套安卓开发学习资料
支持对任何视图进行包裹后3D翻转.
class ThreeDLayout(context: Context?, attrs: AttributeSet? = null) : ViewGroup(context, attrs) {
private val mCamera: Camera
private val mMatrix: Matrix
//this viewgroup's center
private var mCenterX = 0
private var mCenterY = 0
//rotateDegree
private var mCanvasRotateY = 0f
private var mCanvasRotateX = 0f
private var mCanvasMaxRotateDegree = 50f
private var mMode = MODE_BOTH_X_Y
private val mDensity: Float
private val mValues = FloatArray(9)
//the flag of touch
private var isCanTouch = false
//the degree of animation
private var mDegreeY = 0f
private var mDegreeX = 0f
//the flag of animate
private var isPlaying = false
//the degree of longer animate
private var mLoopAnimateY = 0
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
check(childCount == 1) { "ThreeDLayout can only have one child" }
val child = getChildAt(0)
measureChild(child, widthMeasureSpec, heightMeasureSpec)
//only one child view,so give the same size
setMeasuredDimension(child.measuredWidth, child.measuredHeight)
}
override fun onLayout(
changed: Boolean,
l: Int,
t: Int,
r: Int,
b: Int
) {
val child = getChildAt(0)
child.layout(0, 0, child.measuredWidth, child.measuredHeight)
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
mCenterX = w / 2
mCenterY = h / 2
}
override fun onDraw(canvas: Canvas) {
mMatrix.reset()
mCamera.save()
if (mMode == MODE_Y || mMode == MODE_BOTH_X_Y) {
mCamera.rotateX(mCanvasRotateX)
}
if (mMode == MODE_X || mMode == MODE_BOTH_X_Y) {
mCamera.rotateY(mCanvasRotateY)
}
mCamera.rotateY(mDegreeY)
mCamera.rotateX(mDegreeX)
if (isPlaying) {
mCamera.rotateY(mLoopAnimateY++.toFloat())
if (mLoopAnimateY == 360) {
mLoopAnimateY = 0
}
invalidate()
}
mCamera.getMatrix(mMatrix)
// fix the Camera bug,
mMatrix.getValues(mValues)
mValues[6] = mValues[6] / mDensity
mValues[7] = mValues[7] / mDensity
mMatrix.setValues(mValues)
mCamera.restore()
mMatrix.preTranslate(-mCenterX.toFloat(), -mCenterY.toFloat())
mMatrix.postTranslate(mCenterX.toFloat(), mCenterY.toFloat())
canvas.concat(mMatrix)
super.onDraw(canvas)
}
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
return if (isCanTouch) {
true
} else {
super.onInterceptTouchEvent(ev)
}
}
override fun onTouchEvent(event: MotionEvent): Boolean {
return if (isCanTouch) {
val x = event.x
val y = event.y
when (event.action) {
MotionEvent.ACTION_MOVE -> {
rotateCanvasWhenMove(x, y)
invalidate()
return true
}
MotionEvent.ACTION_UP -> {
mDegreeY = 0f
rotateCanvasWhenMove(mCenterX.toFloat(), mCenterY.toFloat())
invalidate()
return true
}
}
true
} else {
super.onTouchEvent(event)
}
}
/**
* get the value to rotate
*/
private fun rotateCanvasWhenMove(x: Float, y: Float) {
val dx = x - mCenterX
val dy = y - mCenterY
var percentX = dx / mCenterX
var percentY = dy / mCenterY
if (percentX > 1f) {
percentX = 1f
} else if (percentX < -1f) {
percentX = -1f
}
if (percentY > 1f) {
percentY = 1f
} else if (percentY < -1f) {
percentY = -1f
}
mCanvasRotateY = mCanvasMaxRotateDegree * percentX
mCanvasRotateX = -(mCanvasMaxRotateDegree * percentY)
}
fun setTouchable(canTouch: Boolean) {
isCanTouch = canTouch
}
fun setTouchMode(mode: Int) {
mMode = mode
isCanTouch = true
}
/**
* set the max rotate degree
*/
fun setMaxRotateDegree(degree: Int) {
mCanvasMaxRotateDegree = degree.toFloat()
}
/**
* start horizontal turn animate
*/
fun startHorizontalAnimate(duration: Long) {
val animator = ValueAnimator.ofFloat(-180f, 0f)
animator.addUpdateListener { animation ->
mDegreeY = animation.animatedValue as Float
invalidate()
}
animator.addListener(object : Animator.AnimatorListener {
override fun onAnimationStart(animation: Animator) {}
override fun onAnimationEnd(animation: Animator) {
mDegreeY = 0f
animator.removeAllUpdateListeners()
}
override fun onAnimationCancel(animation: Animator) {}
override fun onAnimationRepeat(animation: Animator) {}
})
animator.duration = duration
animator.start()
}
/**
* start horizontal turn animate delayed
*/
fun startHorizontalAnimateDelayed(delayed: Long, duration: Long) {
Thread(Runnable {
try {
Thread.sleep(delayed)
} catch (e: InterruptedException) {
e.printStackTrace()
}
post { startHorizontalAnimate(duration) }
}).start()
}
/**
* start vertical turn animate
*/
fun startVerticalAnimate(duration: Long) {
val animator = ValueAnimator.ofFloat(-180f, 0f)
animator.addUpdateListener { animation ->
mDegreeX = animation.animatedValue as Float
invalidate()
}
animator.addListener(object : Animator.AnimatorListener {
override fun onAnimationStart(animation: Animator) {}
override fun onAnimationEnd(animation: Animator) {
mDegreeX = 0f
animator.removeAllUpdateListeners()
}
override fun onAnimationCancel(animation: Animator) {}
override fun onAnimationRepeat(animation: Animator) {}
})
animator.duration = duration
animator.start()
}
/**
* start vertical turn animate delayed
*/
fun startVerticalAnimateDelayed(delayed: Long, duration: Long) {
Thread(Runnable {
try {
Thread.sleep(delayed)
} catch (e: InterruptedException) {
e.printStackTrace()
}
post { startVerticalAnimate(duration) }
}).start()
}
/**
* start loop animate
*/
fun startHorizontalAnimate() {
isPlaying = true
invalidate()
}
/**
* stop the loop animate
*/
fun stopAnimate() {
isPlaying = false
mLoopAnimateY = 0
invalidate()
}
companion object {
//the touch mode
var MODE_X = 0
var MODE_Y = 1
var MODE_BOTH_X_Y = 2
}
init {
//set a default background to make sure onDraw() dispatch
if (background == null) {
setBackgroundColor(Color.parseColor("#ffffff"))
}
var dm = DisplayMetrics()
dm = resources.displayMetrics
mDensity = dm.density
mCamera = Camera()
mMatrix = Matrix()
}
}
https://gitee.com/cxyzy1/ThreeDLayout
关注头条号,第一时间获取最新文章: