记录一次Android自定义View的难忘教训
2020-07-15 本文已影响0人
浅吟且行的时光
一个倒计时控件:CountView extend JustifyTextView extend ImageView,CountView倒计时触发后,调用JustifyTextView中的方法setText(String text);使用invalidate(),进行重绘,而不是使用TextView setText(),调用requestLayout进行重绘,原因是:requestLayout()方法会一层一层调用父布局的:requestLayout()(会导致父布局重新进行测量、布局和绘制流程,这样会影响某些View不能正常显示:例如滑动显示菜单,布局变动时会自动关闭菜单显示,这不是我想要的结果),而invalidate()只针对当前变动的View进行重绘
主要问题:第一个方法会不断调用,第一次调用后会调用onDraw方法,但是UI上没显示画的结果,并且随后再也不调用onDraw方法
//JustifyTextView.java 一秒调用一次
public void setText(String text) {
this.text = text;
invalidate();
}
//正常情况下每次调用invalidate();会调用下面的方法
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (paint == null){
initPaint();
}
Paint.FontMetrics fm = paint.getFontMetrics();
float baseline = fm.descent - fm.ascent;
canvas.drawText(text, desiredWidth/2, baseline, paint);
}
分析问题可能产生的原因:
- onDraw没调用是因为 invalidate(),调用不对?换做下面几个方法都不行,看来不是这个问题
requestLayout();
((View)getParent()).invalidate();
forceLayout();
- 尝试在子View(CountView)中进行绘制,仍然只绘制一次
- 对ImageView设置背景,仍然无果
- 对View绘制的起始坐标进行多次设置尝试,仍然不行
canvas.drawText(text, x, y, paint);
- 改变xml布局中控件的wrap_content为指定宽高,终于显示出来
android:layout_width="wrap_content"
android:layout_height="wrap_content"
# 改为
android:layout_width="100dp"
android:layout_height="100dp"
最后
- 通过重新实现onMeasure方法(xml中仍然采用wrap_content),动态设置View的宽高,达到正常显示的效果
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width;
int height;
//Measure Width
if (widthMode == MeasureSpec.EXACTLY) {
//Must be this size
width = widthSize;
} else if (widthMode == MeasureSpec.AT_MOST) {
//Can't be bigger than...
width = Math.min(desiredWidth, widthSize);
} else {
//Be whatever you want
width = desiredWidth;
}
//Measure Height
if (heightMode == MeasureSpec.EXACTLY) {
//Must be this size
height = heightSize;
} else if (heightMode == MeasureSpec.AT_MOST) {
//Can't be bigger than...
height = Math.min(desiredHeight, heightSize);
} else {
//Be whatever you want
height = desiredHeight;
}
//MUST CALL THIS
setMeasuredDimension(width, height);
}