1.invalidate()源码分析
p.invalidateChild(this, damage);
do{
parent = parent.invalidateChildInParent(location, dirty);
if (view != null) {
// Account for transform on current parent
Matrix m = view.getMatrix();
if (!m.isIdentity()) {
RectF boundingRect = attachInfo.mTmpTransformRect;
boundingRect.set(dirty);
m.mapRect(boundingRect);
dirty.set((int) Math.floor(boundingRect.left),
(int) Math.floor(boundingRect.top),
(int) Math.ceil(boundingRect.right),
(int) Math.ceil(boundingRect.bottom));
}
}
} while (parent != null);
mView.draw(canvas);
invlidate()流程 :一路往上跑 ,跑到最外层draw()-> dispatchDraw() 一路往下画 最终画到当前调用invaldate的view的onDraw()方法
invlidate()牵连着整个layout布局中的view
为什么不能在子线程更新UI?
开了线程,更新UI一般会调用setText(), setImageBitmap(),setVisibility最终调到这里面来ViewRootImpl #checkThread()
void checkThread() {
Thread current = Thread.currentThread();
if (mThread != current) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views."
+ " Expected: " + mThread.getName()
+ " Calling: " + current.getName());
}
}
mThread在构造函数中初始化的,为主线程MainThread
public ViewRootImpl(@UiContext Context context, Display display, IWindowSession session,WindowLayout windowLayout) {
mThread = Thread.currentThread();
}
UI的绘制流程performTraversals(),performMeasure(),performLayout(),performDraw()
测量,摆放,绘制,三大流程
2.如何像WX朋友圈一样优化过度渲染
看自己界面有没有过度渲染 开发者选项 打开调试GPU过度绘制
2.1 网上的解决方案
尽量不要嵌套
能不设置背景不要设置背景
2.2 最好的解决方案
获取到数据去设置 setText(),setlmageView其实 invalidate()
最好是自己画,不要用系统的嵌套布局,运行效率高,实现功能效率低(抉择问题)
3.仿QQ运动步数进度效果
3.1 流程
- 分析效果:
- 确定自定义属性,编写attrs.xml
- 在布局中使用
- 在自定义view中获取自定义属性
- onMeasure()
- 画弧 (1.画外圆弧,2.画内圆弧,百分比,是使用者设置的,3.画文字)
- 其他 写几个方法动起来
3.2 自定义属性
<declare-styleable name="StepView">
<attr name="outCircleColor" format="color|reference" />
<attr name="inCircleColor" format="color|reference" />
<attr name="borderWidth" format="dimension" />
<attr name="centerTextSize" format="dimension" />
<attr name="centerTextColor" format="color|reference" />
</declare-styleable>
3.3 自定义View
StepView
class StepView @JvmOverloads constructor(
context: Context,
attributeSet: AttributeSet? = null,
defStyleAttr: Int = 0
) :
View(context, attributeSet, defStyleAttr) {
private val outCircleColor: Int
private val inCircleColor: Int
private val borderWidth: Int
private val centerTextSize: Int
private val centerTextColor: Int
/**
* 总共步数
* */
private var stepMax = 100
/**
* 当前步数
* */
private var currentStep = 45
init {
val array = context.obtainStyledAttributes(attributeSet, R.styleable.StepView)
inCircleColor = array.getColor(R.styleable.StepView_inCircleColor, Color.RED)
outCircleColor = array.getColor(R.styleable.StepView_outCircleColor, Color.BLUE)
centerTextColor = array.getColor(R.styleable.StepView_centerTextColor, Color.BLUE)
borderWidth = array.getDimension(R.styleable.StepView_borderWidth, 20F).toInt()
centerTextSize = array.getDimensionPixelSize(R.styleable.StepView_centerTextSize, context.sp2px(15))
array.recycle()
}
/**
* 5.onMeasure()
* */
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
//调用者在布局中可能warp_content,
val isWrap = (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.AT_MOST) ||
(MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST)
val size = if (isWrap) {
40
} else {
val width = MeasureSpec.getSize(widthMeasureSpec)
val height = MeasureSpec.getSize(heightMeasureSpec)
//宽高不一致,取最小值,确保是个正方形
min(width, height)
}
setMeasuredDimension(size, size)
}
/**
* 区域
* */
private val arcRectF by lazy {
//半圆边缘没显示完整的原因?:描边有宽度
//中心点
// val center = width / 2
// val radius = center - borderWidth/2
// val leftAndTop = (center - radius).toFloat()
val leftAndTop = (borderWidth / 2).toFloat()
// val rightAndBottom = (center + radius).toFloat()
val rightAndBottom = (width - borderWidth / 2).toFloat()
RectF(leftAndTop, leftAndTop, rightAndBottom, rightAndBottom)
}
private val inCirclePaint by lazy {
Paint().apply {
isAntiAlias = true
strokeWidth = borderWidth.toFloat()
color = inCircleColor
style = Paint.Style.STROKE
strokeCap = Paint.Cap.ROUND
}
}
private val outCirclePaint by lazy {
Paint().apply {
isAntiAlias = true
strokeWidth = borderWidth.toFloat()
color = outCircleColor
//画笔实心
style = Paint.Style.STROKE
strokeCap = Paint.Cap.ROUND
}
}
private val textPaint by lazy {
Paint().apply {
isAntiAlias = true
color = centerTextColor
textSize = centerTextSize.toFloat()
}
}
/**
* 文字的区域
* */
private val textRect by lazy { Rect() }
/**
* 6.画弧
* */
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// 6.1 画外圆弧
canvas.drawArc(arcRectF, 135F, 270F, false, outCirclePaint)
// 6.2 画内圆弧,百分比,是使用者设置的
val sweepAngle = if ((stepMax < currentStep) || stepMax == 0) {
0F
} else {
(currentStep.toFloat() / stepMax.toFloat()) * 270F
}
canvas.drawArc(arcRectF, 135F, sweepAngle, false, inCirclePaint)
// 6.3 画文字
val centerText = "${currentStep}/${stepMax}"
textPaint.getTextBounds(centerText, 0, centerText.length, textRect)
val dx = (width / 2 - textRect.width() / 2).toFloat()
val dy = with(textPaint.fontMetricsInt) {
(bottom - top) / 2 - bottom
}
val baseLine = (height / 2 + dy).toFloat()
canvas.drawText(centerText, dx, baseLine, textPaint)
}
// 7.其他 写几个方法动起来
@Synchronized
fun inflateStepMax(stepMax: Int) {
this.stepMax = stepMax
}
@Synchronized
fun inflateCurrentStep(currentStep: Int) {
this.currentStep = currentStep
//不断绘制onDraw()
invalidate()
}
}
3.4 界面调用
val view = findViewById<StepView>(R.id.step_view).apply {
inflateStepMax(4000)
}
//属性动画,后面的内容
val anim = with(ObjectAnimator.ofFloat(0f, 3000f)) {
duration = 1000
start()
interpolator = DecelerateInterpolator()
addUpdateListener {
val currentStep = (it.animatedValue as Float).toInt()
view.inflateCurrentStep(currentStep)
}
this
}