上述代码除了提供减速算法,还一并更新了对应View数据模型的中间值,也就是模型定义种的 left, top, right, bottom。
通过减速算法提供的进度值,乘以目标坐标与起始坐标的间距,得出中间值 。
逐渐减速的算法关键代码为:
if (progress > 0.8f) {var offset = ((progress - 0.7f) / 0.25f)if (offset > 1.0f)offset = 1.0fprocessing += waitingTime - waitingTime * progress * 0.95f * offset} else {processing += waitingTime}
这个算法实现的有缺陷,因为它直接修改了进度时间,大概率会导致执行完毕的时间与设置的预期时间(如设置200ms执行完毕,实际可能超过200ms)不符 。文末我会提供一个优化的减速算法 。
变量 waitingTime 表示等待多久执行下一帧动画 。用每秒1000ms计算即可,如果目标为60刷新率的动画,设置为1000 / 60 = 16.66667即可(近似值) 。
计算并存储每个 View 的中间值后,调用 requestLayout() 通知系统的 onMeasure 和 onLayout 方法,重新摆放 View。
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {if (childCount == 0)returnfor (i in 0 until childCount) {val child = getChildAt(i)val layoutInfo = child.tag as ViewLayoutInfochild.layout(layoutInfo.left.toInt(),layoutInfo.top.toInt(),layoutInfo.right.toInt(),layoutInfo.bottom.toInt())if (layoutInfo.isAlpha) {val progress = if (layoutInfo.isConverted)1.0f - layoutInfo.progresselselayoutInfo.progresschild.alpha = progress}}}
【Android技术分享|自定义ViewGroup实现直播间大小屏无缝切换】
4.定义边距相关的变量,供简单的定制修改
/** * @param multipleWidgetPadding : 等分模式读取 * @param maxWidgetPadding : 大小屏布局读取 * @param defMultipleVideosTopPadding : 距离顶部变距 */private var multipleWidgetPadding = 0private var maxWidgetPadding = 0private var defMultipleVideosTopPadding = 0init {viewTreeObserver.addOnGlobalLayoutListener(this)attrs?.let {val typedArray = resources.obtainAttributes(it, R.styleable.AnyVideoGroup)multipleWidgetPadding = typedArray.getDimensionPixelOffset(R.styleable.AnyVideoGroup_between23viewsPadding, 0)maxWidgetPadding = typedArray.getDimensionPixelOffset(R.styleable.AnyVideoGroup_at4smallViewsPadding, 0)defMultipleVideosTopPadding = typedArray.getDimensionPixelOffset(R.styleable.AnyVideoGroup_defMultipleVideosTopPadding, 0)layoutTopicMode = typedArray.getBoolean(R.styleable.AnyVideoGroup_initTopicMode, layoutTopicMode)typedArray.recycle()}}
取名时对这三个变量的职责定义,与编写逻辑时的定义有出入,所以有点词不达意,需参考注释 。
由于这只是定制化的变量,并不重要,可根据业务逻辑自行随意修改 。
5.复写 onMeasure 方法,这里主要是通知 TextureView 更新大小 。
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {val widthSize = MeasureSpec.getSize(widthMeasureSpec)val heightSize = MeasureSpec.getSize(heightMeasureSpec)multiViewWidth = widthSize.shr(1)multiViewHeight = (multiViewWidth.toFloat() * 1.33334f).toInt()smallViewWidth = (widthSize * 0.3125f).toInt()smallViewHeight = (smallViewWidth.toFloat() * 1.33334f).toInt()for (i in 0 until childCount) {val child = getChildAt(i)val info = child.tag as ViewLayoutInfochild.measure(MeasureSpec.makeMeasureSpec((info.right - info.left).toInt(), MeasureSpec.EXACTLY),MeasureSpec.makeMeasureSpec((info.bottom - info.top).toInt(), MeasureSpec.EXACTLY))}setMeasuredDimension(MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY),MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY))}
总结1.明确数据模型,一般情况下记录起始上下左右坐标、目标上下左右坐标、和进度百分比就足够了 。
2.根据需求明确动画算法,这里补充一下优化的减速算法:
factor = 1.0if (factor == 1.0)(1.0 - (1.0 - x) * (1.0 - x))else(1.0 - pow((1.0 - x), 2 * factor))// x = time.
3.根据算法计算出来的值更新 layout 布局即可 。
此类 ViewGroup 实现简单方便,只涉及到几个基本系统API 。如不想写 onMeasure 方法可继承 FrameLayout 等已写好 onMeasure 实现的 ViewGroup。
推荐阅读
- MySQL安全加固方法分享
- Android 多返回栈技术详解
- JPA自定义ID生成器,雪花算法实现代码分享
- 安卓|Android 13隐藏功能曝光:可降分辨率 让续航再度提升
- 大厂Android开发高频面试问题:说说你对Zygote的理解
- 华为|打响国产超高清视频及音频第一枪 华为智慧屏V Pro升级新一代画质技术
- fic技术大江大河 fic技术
- Android app专项测试之耗电量测试
- Intel|下一代硅光子芯片技术:Intel、NVIDIA都出手了
- 爆盆黄金玉露图片,完美爆盆