项目中用到一个自定义View,功能类似于跑马灯。但是在低端机上有明显的卡顿,故拿出部分代码做优化。代码如下
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (!TextUtils.isEmpty(mText) && mValueAnimator?.isRunning == true && mStartY != -1) {
val x = mWidth - (mWidth + mPaint.measureText(mText)) * mFraction
val y = mStartY.toFloat()
canvas.drawText(mText, x, y, mPaint)
}
}
通过查看 Android Profiler,可以知道
onDraw total = 6.7 self = 2.3 children = 4.4
6.1 1.7 4.3
6.4 2.0 4.3
6.6 2 4.6
onDraw里面,很明显 self 里面的计算占了 2% 左右,其实可以优化. measureText 可以放到外面
canvas.drawText 占了4.5%左右,这一部分也可以优化
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (mValueAnimator?.isRunning == true && mStartY != -1) {
canvas.clipRect(0, mCLipTop, width, mCLipBottom)//限制在规定的区域内重绘
val x = mWidth - (mWidth + mTxtWidth) * mFraction
val y = mStartY.toFloat()
canvas.drawText(mText, x, y, mPaint)
}
}
如上修改以后, self 降到 1% 左右,.同时 children 也降到 3% 左右(可见 canvas.drawText 还是很占CPU的)
从技术角度能做的优化其实已经差不多了,但是从需求的角度还是有优化的空间的
这个自定义View的功能就是跑马灯,就是让文字随着时间走.那么就可以想到两种解决方案
(1)画布移动 canvas.translate
这个方案的问题就是 translate 之后还是要 drawText, 还是很占CPU
(2)整体移动 View, 也就是在 TextView 的基础上修改, 用 scrollTo 来处理
最终方案
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (mValueAnimator?.isRunning == true) {
val x = mWidth - (mWidth + mTxtWidth) * mFraction
scrollTo(-x.toInt(), 0)
}
}
自定义View的优化最重要的就是一开始的时候选择相对好一点的实现方式,这样后期的优化工作可以少一些
最初写这篇文章时2021.02.01。现在是06.02了,发现这种实现方式还是有问题。可能会引发主线程消息队列一直无法清空使得activity无法及时销毁。经过测试,发现可以使用SurfaceView来实现。
简单说一下为什么可以吧。SurfaceView 是在当前窗口上再创建一个新窗口。它有独立的视图,同时刷新的时候不需要重绘应用程序的窗口,不会影响主线程,所以可以实现复杂而高效的UI。当然,也有一定的弊端。因为有自己独立的 Window,因此 SurfaceView 也不能放到 ListView 或者 ScrollView中,而且也无法做旋转等动画。为了解决“无法做旋转等动画”这个问题,还引入了TextureView。具体链接在下面
Android 图形系统 -- SurfaceView 使用
Android 图形系统 -- TextureView 使用
网友评论