片段 - removeGlobalOnLayoutListener IllegalStateException

flo*_*oat 18 android android-view

我试图得到的高度和宽度ImageViewFragment有以下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;
}
Run Code Online (Sandbox Code Playgroud)

运行此代码会导致以下异常:

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)
Run Code Online (Sandbox Code Playgroud)

文档说不removeGlobalOnLayoutListener推荐使用removeOnGlobalLayoutListener,但如果我按照建议使用,我会得到一个未定义的错误.

我做错了什么?

ρяσ*_*я K 64

试试这个 :

   ViewTreeObserver observer = imageViewPicture.getViewTreeObserver();
   observer.addOnGlobalLayoutListener (new OnGlobalLayoutListener () {
    @Override
     public void onGlobalLayout() {

       imageViewPicture.getViewTreeObserver().removeGlobalOnLayoutListener(this);
      }
    });
Run Code Online (Sandbox Code Playgroud)

  • 谢谢!现在我也可以使用建议的removeOnGlobalLayoutListener. (2认同)
  • 但是我已经看到很多代码将观察者存储在局部变量中 - 这很有效.不明白为什么在很多情况下这是有效的,但不是全部?你知道吗?? (2认同)
  • `removeGlobalOnLayoutListener` 已弃用。有什么建议吗? (2认同)

TWi*_*Rob 18

另一个解决方案可以正常工作,但它无法解释为什么会发生这种情况.

这里有几个问题需要解决:

GlobalOn VS OnGlobal

使用GlobalOn版本会给你一个弃用警告,但如果你检查源它只是调用OnGlobal版本,所以它们是等价的.不同之处在于您只能使用API​​级别16的OnGlobal,因此如果您的目标是早期版本,则必须使用GlobalOn并处理该弃用警告.

为什么死了?

请注意,这个问题是关于代码的onCreateView,其中imageViewPicture没有附加到视图层次结构,它刚刚被夸大了.如果您快速浏览一下,View.getViewTreeObserver()可以看到它在这种情况下会创建一个"浮动"观察者.然后在dispatchAttachedToWindow调用层次结构时调用该层次结构,然后合并窗口和视图的浮动观察者:

info.mTreeObserver.merge(mFloatingTreeObserver);
Run Code Online (Sandbox Code Playgroud)

它将所有已注册的侦听器移动到窗口的观察者并杀死浮动观察者.

你获得的原始观察者是浮动的,它已经死了,因此在它上面调用add/remove会导致上述异常.

他们是同一个观察者吗?

作为Danyal,我也很困惑为什么要removeGlobalOnLayoutListener在不同的观察者身上工作,但现在对于临时漂浮的观察者来说很明显.当浮动的一个被合并到窗口的观察者时,监听器被移动到另一个观察者,因此View.getViewTreeObserver()稍后调用将给你一个包含你的监听器的观察者.新的观察者现在负责处理你的听众.

但它有时会工作!,另一种解决方案

至于Zordid的评论,为什么在很多情况下可以坚持使用本地(闭包)变量可以通过类似的推理来解释:刚刚膨胀的视图在onCreateView返回之后还没有附加一点.你见过的大多数人可能是在onCreateView生命周期之后的一种方法.如果观察者相关的代码在,则float(OP)解决方案可以正常工作onViewCreated.每个生命周期方法都有自己的职责,所以我建议像这样分割代码:

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);
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

我也喜欢连接其他侦听器onViewCreated,这样实例变量的数量最小化,因此imageViewPicture将是一个局部变量.

同样可能是真实的,因为Activity.setContentView它立即附加膨胀的视图并且通常被调用,onCreate因此当您与观察者/听众一起玩时,层次结构是活的.