我一直遇到自定义视图的大小和布局问题,我想知道是否有人可以提出“最佳实践”方法。问题如下。想象一个自定义视图,其中内容所需的高度取决于视图的宽度(类似于多行 TextView)。(显然,这只适用于高度不是由布局参数固定的情况。)问题在于,对于给定的宽度,在这些自定义视图中计算内容高度相当昂贵。特别是,在 UI 线程上计算成本太高,因此在某些时候需要启动工作线程来计算布局,并且当它完成时,需要更新 UI。
问题是,这应该如何设计?我想了几个策略。他们都假设无论何时计算高度,都会记录相应的宽度。
第一个策略显示在此代码中:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = measureWidth(widthMeasureSpec);
setMeasuredDimension(width, measureHeight(heightMeasureSpec, width));
}
private int measureWidth(int widthMeasureSpec) {
// irrelevant to this problem
}
private int measureHeight(int heightMeasureSpec, int width) {
int result;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
} else {
if (width != mLastWidth) {
interruptAnyExistingLayoutThread();
mLastWidth = width;
mLayoutHeight = DEFAULT_HEIGHT;
startNewLayoutThread();
}
result = mLayoutHeight;
if (specMode == MeasureSpec.AT_MOST && result > specSize) {
result = specSize;
}
}
return result;
}
当布局线程完成时,它会向 UI 线程发布一个 Runnable 以设置mLayoutHeight
为计算出的高度,然后调用requestLayout()
(and invalidate()
)。
第二种策略是onMeasure
始终使用当时的当前值mLayoutHeight
(不启动布局线程)。测试宽度变化和启动布局线程将通过覆盖来完成onSizeChanged
。
第三种策略是懒惰并等待在onDraw
.
我想尽量减少布局线程启动和/或终止的次数,同时尽快计算所需的高度。最好也尽量减少调用次数requestLayout()
。
从文档中可以清楚地看出,onMeasure
在单个布局过程中可能会被多次调用。onSizeChanged
可能会被多次调用还不太清楚(但似乎很可能) 。所以我认为把逻辑放进去onDraw
可能是更好的策略。但这似乎与自定义视图大小的精神背道而驰,所以我对它有一种公认的非理性偏见。
其他人一定也遇到过同样的问题。有没有我错过的方法?有没有最好的方法?