本文共 7618 字,大约阅读时间需要 25 分钟。
关注 ,免费获取全套安卓开发学习资料
支持对任何视图进行包裹后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() }}
关注头条号,第一时间获取最新文章:
转载地址:http://hwodf.baihongyu.com/