大致需求场景:
类似于个人主页,上端展示一些个人信息,下边是其个人一些作品列表,因为要求滑动顺着 整页滑动。
Recyleview添加headview,结果却是 列表不显示。
思考了一下:
在Avtivity的onCreate()中直接调用View组件的getWidth()、getHeight()、getMeasuredWidth()、getMeasuredHeight()、getTop()、getLeft()等方法是无法获取到真实值的,只会得到0。
这是因为View组件布局要在onResume回调后完成。
然而 是在onCreate()中Recyleview执行的addheadview(),可能是这个原因引起的,高度为0所以不显示。
想到的解决办法:
1getViewTreeObserver().addOnGlobalLayoutListener()重新设置高度
使用getViewTreeObserver().addOnGlobalLayoutListener()来获得宽度或者高度。OnGlobalLayoutListener 是ViewTreeObserver的内部类,当一个视图树的布局发生改变时,可以被ViewTreeObserver监听到,这是一个注册监听视图树的观察者(observer),在视图树的全局事件改变时得到通知。ViewTreeObserver不能直接实例化,而是通过getViewTreeObserver()获得。
需要注意的是:OnGlobalLayoutListener可能会被多次触发,因此在得到了高度之后,要将OnGlobalLayoutListener注销掉。
mHeaderView.getViewTreeObserver().addOnGlobalLayoutListener(
new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
mHeaderViewHeight = mHeaderView.getHeight();
getViewTreeObserver()
.removeGlobalOnLayoutListener(this);
}
});
转念一想,重新请求一下不就好了
2.recyclerview.requestLayout();
请求数据解析完后,执行recyclerview.requestLayout();
requestLayout() 控件会重新执行 onMesure() onLayout() ,解决问题。
引发思考笔记
3invalidate和requestLayout的区别
简单地说invalidate() ,是自定义View 的时候,重新执行onDraw()方法。详情见图,比较直观
图片来源网络
View的measure方法‘
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
..........
final boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;
..........
if (forceLayout || needsLayout) {
..........
onMeasure(widthMeasureSpec, heightMeasureSpec);
mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
}
}
requestLayout时对View设置了PFLAG_FORCE_LAYOUT标记位,而invalidate则没有,所以在调用invalidate重绘时forceLayout 为false,也就是最上层的控件就不会调用onMeasure方法,那么下面的控件也肯定不会进行重新测量。而重新测量的控件又会被标记上PFLAG_LAYOUT_REQUIRED。
layout方法
public void layout(int l, int t, int r, int b) {
..........
boolean changed = isLayoutModeOptical(mParent) ?
setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
onLayout(changed, l, t, r, b);
}
..........
}
调用onLayout方法,要么位置变化,changed为true,要PFLAG_LAYOUT_REQUIRED标记位,所以invalidate的重绘顶层控件也不会调用onLayout方法,invalidate不会经过measure和layout这两个过程。
或者可以说是invalidate应该是在控件内容或是可见性发生了变化,而其大小位置等不会发生变化时调用,所以不需要重新测量布局,而requestLayout则是大小位置发生了变化则调用。