最后
代码真的是重质不重量,质量高的代码,是当前代码界提倡的,当然写出高质量的代码肯定需要一个相当高的专业素养,这需要在日常的代码书写中逐渐去吸收掌握,谁不是每天都在学习呀,目的还不是为了一个,为实现某个功能写出高质量的代码。
所以,长征路还长,大家还是好好地做个务实的程序员吧。
最后,小编这里有一系列Android提升学习资料,有兴趣的小伙伴们可以来看下哦~
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
save()
mMatrix.reset()
mCamera.save()
mCamera.translate(0F, 0F, depthZ)
mCamera.rotateX(rotateX)
mCamera.rotateY(rotateY)
mCamera.getMatrix(mMatrix)
mCamera.restore()
val scale = resources.displayMetrics.density
val mValues = FloatArray(9)
mMatrix.getValues(mValues)
mValues[6] = mValues[6] / scale
mValues[7] = mValues[7] / scale
mMatrix.setValues(mValues)
mMatrix.preTranslate(-width / 2F, -height / 2F)
mMatrix.postTranslate(width / 2F, height / 2F)
concat(mMatrix)
mPaint.color = Color.WHITE
mPaint.setShadowLayer(cardShadowSize, 0F, cardShadowDistance, Color.GRAY)
val rectF = RectF(paddingSize, paddingSize + cardHeight, paddingSize + cardWidth, paddingSize + cardHeight * 2)
drawRoundRect(
rectF,
20F,
20F,
mPaint
)
//todo: 绘制数字图片
restore()
}
}
结合我在《今日头条loading控件,隔壁产品都馋哭了》文章中提到过的坐标计算框架,用户手指在屏幕上移动的距离与中间卡片旋转角度存在某种函数关系,通过IFunc进行保存和计算。
代码如下:
/**
- Card翻转函数
/
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时,无需绘制下卡片
最后
今天关于面试的分享就到这里,还是那句话,有些东西你不仅要懂,而且要能够很好地表达出来,能够让面试官认可你的理解,例如Handler机制,这个是面试必问之题。有些晦涩的点,或许它只活在面试当中,实际工作当中你压根不会用到它,但是你要知道它是什么东西。
最后在这里小编分享一份自己收录整理上述技术体系图相关的几十套腾讯、头条、阿里、美团等公司2021年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。
还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。
【算法合集】
【延伸Android必备知识点】
【Android部分高级架构视频学习资源】
**Android精讲视频领取学习后更加是如虎添翼!**进军BATJ大厂等(备战)!现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!