22

我正在尝试使用以下方法获取ImageViewa的高度和宽度:FragmentViewTreeObserver

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按照建议使用 ,我会收到未定义的错误。

我做错了什么?

4

3 回答 3

71

尝试这个 :

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

       imageViewPicture.getViewTreeObserver().removeGlobalOnLayoutListener(this);
      }
    });
于 2012-10-12T22:15:32.833 回答
20

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

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

GlobalOnVSOnGlobal

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

为什么它死了?

请注意,这个问题是关于 中的代码onCreateView,其中imageViewPicture尚未附加到视图层次结构,它刚刚被夸大了。如果您快速浏览一下,View.getViewTreeObserver()您会发现它在这种情况下创建了一个“浮动”观察者。然后当视图被放入层次结构时dispatchAttachedToWindow,它会合并窗口和视图的浮动观察者:

info.mTreeObserver.merge(mFloatingTreeObserver);

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

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

他们是同一个观察者吗?

作为 Danyal,我也很困惑为什么要removeGlobalOnLayoutListener在不同的观察者身上工作,但现在有了临时浮动观察者就很清楚了。当浮动的被合并到窗口的观察者时,监听器被移动到另一个观察者,所以View.getViewTreeObserver()稍后调用会给你一个包含你的监听器的观察者。新的观察者现在负责处理你的监听器。

但它有时会起作用!和另一种解决方案

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

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

Activity.setContentView立即附加膨胀视图并且通常被调用的情况可能也是如此,onCreate因此在您与观察者/听众一起玩时,层次结构是活跃的。

于 2015-03-20T17:27:52.330 回答
3

最好尝试检查 agetViewTreeObserver是否还活着。我认为下面的代码会起作用。基于https://stackoverflow.com/a/15301092/2914140https://stackoverflow.com/a/26193736/2914140等。

** 更新 **

从 ViewTreeObserver阅读Remove listener 后,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() ....

于 2016-08-17T16:20:38.597 回答