当前位置: 首页 > 知识库问答 >
问题:

Fragment - removeGlobalOnLayoutListener IllegalStateException

郭易安
2023-03-14

我尝试使用以下< code>ViewTreeObserver获取< code >片段中< code>ImageView的高度和宽度:

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)

文档说 removeGlobalOnLayoutListener 已被弃用,但是如果我按照建议使用 removeOnGlobalLayoutListener,我会收到一个未定义的错误。

我做错了什么?

共有3个答案

仇迪
2023-03-14

最好尝试检查< code>getViewTreeObserver是否处于活动状态。我认为下面的代码会起作用。基于https://stackoverflow.com/a/15301092/2914140,·https://stackoverflow.com/a/26193736/2914140和其他人。

** 更新 **

阅读后删除监听器从视图树观察者,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();
                }
            });
        }
    });
}

大概是< code>view.post(...)就够了,但我是从后台线程调用它的,所以如果你也这样做,最好应该从< code>runOnUiThread(new Runnable()调用它...。

沃瑾瑜
2023-03-14

另一个解决方案也很好,但是它没有解释为什么会发生这种情况。

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

使用GlobalOn版本会给您一个弃用警告,但如果您检查源代码,它只是调用OnGlobal版本,因此它们是等效的。不同之处在于,您只能使用API级别16中的OnGlobal版本,因此如果您针对的是早期版本,则必须使用GlobalOn并处理该弃用警告。

请注意,此问题与< code>onCreateView中的代码有关,其中< code>imageViewPicture尚未附加到视图层次结构,它只是被放大了。如果您快速查看一下< code > view . getviewtreeobserver(),您会发现它在这种情况下创建了一个“浮动”观察器。然后,当视图被放入层次结构中时,调用< code > dispatchatchedtowindow ,然后合并窗口和视图的浮动观察点:

info.mTreeObserver.merge(mFloatingTreeObserver);

这会将所有已注册的侦听器移动到窗口的观察点并杀死浮动观察器。

您获取的原始观察者是浮动观察者,它已死,因此对其调用添加/删除会导致上述异常。

作为Danyal,我也很困惑为什么<code>RemoVegLobalonLayOutliner</code>可以在不同的观察者上工作,但现在对于临时浮动观察者已经很清楚了。当浮动的一个被合并到窗口的观察者时,侦听器被移动到另一个观察者,因此调用<code>视图。getViewTreeObserver()稍后将为您提供一个包含侦听器的观察者。新的观察者现在负责处理您的听众。

至于Zordid关于为什么在许多情况下可以在本地(闭包)变量中保持不变的评论,可以用类似的推理来解释:onCreateView中刚刚膨胀的视图在返回后不久还没有附加。您看到的大部分可能都在生命周期中onCreateView之后的方法中。如果观察者相关的代码在onViewCreated中,则浮点数(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将是一个局部变量。

对于< code > activity . setcontentview 可能也是如此,它会立即附加展开的视图,并且通常在< code>onCreate中调用,因此当您使用观察者/监听器时,层次结构是活动的。

强宾白
2023-03-14

试试这个:

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

       imageViewPicture.getViewTreeObserver().removeGlobalOnLayoutListener(this);
      }
    });
 类似资料:
  • Fragment 组件提供了用于减少不必要嵌套的组件。组件能够在不额外创建 DOM 元素的情况下,在 render() 方法中返回多个元素。更早的 Rax 版本已经对数组的返回进行了支持,Rax 1.0 采用了更为标准的方式。 示例 <Fragment> <header>A heading</header> <footer>A footer</footer> </Fragment> //

  • 描述 可以在 vnode 片段上添加生命周期方法 var groupVisible = true var log = function() { console.log("group is now visible") } m("ul", [ m("li", "child 1"), m("li", "child 2"), groupVisible ? m.fragme

  • 一、目录 什么是Fragment Fragment的生命周期 Fragment的使用方式 什么是Fragment的回退栈?【重要】 Fragment与Activity之间的通信【难点】 Fragment与Activity通信的优化【超难点】 如何处理运行时配置发生变化【以屏幕翻转为例】 二、Fragment详解 1. 什么是Fragment ? 你可以简单的理解为,Fragment是显示在Acti

  • 具体问题是调用自定义arrayadapter的getView方法失败。我使用一个片段来显示一个列表。生成列表的活动允许从微调器中选择人员。然后,我使用所选人员的id从DB中检索数据。我将这些数据打包到指定给ArrayList的自定义holder对象中。以下是创建片段事务的活动代码。 片段正确地获取绑定的数据。onCreate方法和onCreateView方法执行正确。以下是onCreateView

  • 问题内容: 我正在升级Android应用程序(1.6兼容性),该应用程序使用显示三个带有嵌套活动的不同选项卡。 当时,我使用技巧在选项卡中显示嵌套的活动,但是我对此方法感到非常不满意,因为处理某些功能确实很麻烦。 我听说过1.6版的Fragments API兼容性软件包,并且看起来非常适合我想做的事情(在带有过渡效果和内容的选项卡中显示嵌套视图/功能),但我无法使其与(这是为了与一起使用,但在兼容

  • 我有一个受支持的片段活动,它将加载差异片段。片段有一些,带有我想得到它的句柄,但score的