我正在尝试使用以下ImageView
获取Fragment
中ViewTreeObserver
的高度和宽度:
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
private ImageView imageViewPicture;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_general_activity_add_recipe, container, false);
setHasOptionsMenu(true);
...
final ViewTreeObserver observer = imageViewPicture.getViewTreeObserver();
observer.addOnGlobalLayoutListener (new OnGlobalLayoutListener () {
@Override public void onGlobalLayout() {
observer.removeGlobalOnLayoutListener(this);
}
});
return view;
}
运行此代码会导致以下异常:
10-12 23:45:26.145: E/AndroidRuntime(12592): FATAL EXCEPTION: main
10-12 23:45:26.145: E/AndroidRuntime(12592): java.lang.IllegalStateException: This ViewTreeObserver is not alive, call getViewTreeObserver() again
10-12 23:45:26.145: E/AndroidRuntime(12592): at android.view.ViewTreeObserver.checkIsAlive(ViewTreeObserver.java:509)
10-12 23:45:26.145: E/AndroidRuntime(12592): at android.view.ViewTreeObserver.removeGlobalOnLayoutListener(ViewTreeObserver.java:356)
10-12 23:45:26.145: E/AndroidRuntime(12592): at com.thimmey.rezepte.AddRecipeActivity_GeneralFragment$1.onGlobalLayout(AddActivity_GeneralFragment.java:83)
10-12 23:45:26.145: E/AndroidRuntime(12592): at android.view.ViewTreeObserver.dispatchOnGlobalLayout(ViewTreeObserver.java:566)
10-12 23:45:26.145: E/AndroidRuntime(12592): at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1736)
10-12 23:45:26.145: E/AndroidRuntime(12592): at android.view.ViewRootImpl.handleMessage(ViewRootImpl.java:2644)
10-12 23:45:26.145: E/AndroidRuntime(12592): at android.os.Handler.dispatchMessage(Handler.java:99)
10-12 23:45:26.145: E/AndroidRuntime(12592): at android.os.Looper.loop(Looper.java:137)
10-12 23:45:26.145: E/AndroidRuntime(12592): at android.app.ActivityThread.main(ActivityThread.java:4517)
10-12 23:45:26.145: E/AndroidRuntime(12592): at java.lang.reflect.Method.invokeNative(Native Method)
10-12 23:45:26.145: E/AndroidRuntime(12592): at java.lang.reflect.Method.invoke(Method.java:511)
10-12 23:45:26.145: E/AndroidRuntime(12592): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:993)
10-12 23:45:26.145: E/AndroidRuntime(12592): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:760)
10-12 23:45:26.145: E/AndroidRuntime(12592): at dalvik.system.NativeStart.main(Native Method)
文档说{I}已被弃用,但如果我按照建议使用removeGlobalOnLayoutListener
,则会收到未定义的错误。
我做错了什么?
答案 0 :(得分:64)
试试这个:
ViewTreeObserver observer = imageViewPicture.getViewTreeObserver();
observer.addOnGlobalLayoutListener (new OnGlobalLayoutListener () {
@Override
public void onGlobalLayout() {
imageViewPicture.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
});
答案 1 :(得分:17)
The other solution可以正常使用,但它无法解释原因。
这里有几个问题需要解决:
GlobalOn
VS OnGlobal
使用GlobalOn版本会给你一个弃用警告,但是如果你检查它只是调用OnGlobal版本的源,那么它们就是等价的。不同之处在于,您只能使用API级别16的OnGlobal,因此,如果您要定位早期版本,则必须使用GlobalOn并处理该弃用警告。
请注意,此问题与onCreateView
中的代码有关,其中imageViewPicture
尚未附加到视图层次结构,它刚刚被夸大了。如果您快速查看View.getViewTreeObserver()
,您会发现它会创建一个"浮动"在这种情况下观察者。然后,当视图放入层次结构中时,调用dispatchAttachedToWindow
,然后合并窗口和视图的浮动观察者:
info.mTreeObserver.merge(mFloatingTreeObserver);
将所有已注册的侦听器移动到窗口的观察者并杀死浮动观察者。
你获得的原始观察者是浮动的观察者,它已经死了,因此在其上调用添加/删除会导致上述异常。
作为Danyal,我也很困惑为什么removeGlobalOnLayoutListener
对不同的观察者起作用,但现在它对于临时浮动的观察者来说很清楚。当浮动的一个被合并到窗口的观察者时,监听器被移动到另一个观察者,因此稍后调用View.getViewTreeObserver()
会给你一个包含你的监听器的观察者。新的观察者现在负责处理你的听众。
至于Zordid的评论,为什么在很多情况下可以坚持使用本地(闭包)变量可以通过类似的推理来解释:onCreateView
中刚刚膨胀的视图在它返回之后还没有附加一点。您所看到的大部分内容可能是在生命周期onCreateView
之后的方法中。如果观察者相关代码在onViewCreated
中,则float(OP)解决方案可以正常工作。每个生命周期方法都有它自己的职责,所以我建议像这样分割代码:
private ImageView imageViewPicture;
@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_general_activity_add_recipe, container, false);
// assuming ... includes:
this.imageViewPicture = view.findViewById(R.id.image);
return view;
}
@Override public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
final ViewTreeObserver observer = imageViewPicture.getViewTreeObserver();
observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener () {
@Override public void onGlobalLayout() {
observer.removeGlobalOnLayoutListener(this);
}
});
}
我也喜欢在onViewCreated
中连接其他侦听器,这样实例变量的数量最小化,因此imageViewPicture
将是一个局部变量。
Activity.setContentView
可能会立即附加膨胀的视图,并且通常在onCreate
中调用,因此当您与观察者/听众一起玩时,层次结构就会生效。
答案 2 :(得分:1)
最好尝试检查getViewTreeObserver
是否还活着。我认为以下代码将起作用。基于https://stackoverflow.com/a/15301092/2914140,https://stackoverflow.com/a/26193736/2914140和其他一些内容。
**更新**
阅读Remove listener from ViewTreeObserver后,https://stackoverflow.com/a/40013262/2914140我重写了一下。
public static void captureGlobalLayout(@NonNull final View view,
@NonNull final ViewTreeObserver.OnGlobalLayoutListener listener) {
ViewTreeObserver vto = view.getViewTreeObserver();
if (vto.isAlive()) {
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
ViewTreeObserver vto = view.getViewTreeObserver();
if (vto.isAlive()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
vto.removeOnGlobalLayoutListener(this);
} else {
//noinspection deprecation
vto.removeGlobalOnLayoutListener(this);
}
listener.onGlobalLayout();
}
}
});
} else {
view.post(new Runnable() {
@Override
public void run() {
listener.onGlobalLayout();
}
});
}
}
**旧答案**
奇怪,但即便如此
view.getViewTreeObserver().isAlive()
为真,然后再调用view.getViewTreeObserver()可能会再次产生相同的异常。所以,我用try-catch包围了一个代码。可能您可以跳过所有这些并仅保留view.post(...)
块。
if (view.getViewTreeObserver().isAlive()) {
try {
view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if (view.getViewTreeObserver().isAlive()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
} else {
view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
}
yourCode();
}
});
} catch (IllegalStateException e) {
e.printStackTrace();
// The same as below branch.
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
view.post(new Runnable() {
@Override
public void run() {
yourCode();
}
});
}
});
}
} else {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
// Try to wait until the view is ready.
view.post(new Runnable() {
@Override
public void run() {
yourCode();
}
});
}
});
}
可能view.post(...)
就足够了,但是我从后台线程调用它,所以如果你这样做,最好从runOnUiThread(new Runnable() ...
调用它。