Фрагмент - removeGlobalOnLayoutListener IllegalStateException

м пытается получить высоту и ширину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)

Документация говорит, чтоremoveGlobalOnLayoutListener устарела, но если я используюremoveOnGlobalLayoutListener, как и предполагалось, я получаю неопределенную ошибку.

Что я делаю не так?

Ответы на вопрос(3)

Решение Вопроса

попробуй это :

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

       imageViewPicture.getViewTreeObserver().removeGlobalOnLayoutListener(this);
      }
    });
 float13 окт. 2012 г., 00:20
Благодарю вас! Теперь я также могу использовать предложенный removeOnGlobalLayoutListener.
 Zordid16 мар. 2014 г., 11:03
Но я'Мы видели много кода, где наблюдатель хранится в локальной переменной - и это сработало. Дон»не понимаю, почему во многих случаях это действительно, но не во всех? Вы знаете??
 Pratik Butani AndroidDev10 нояб. 2015 г., 07:12
removeGlobalOnLayoutListener устарела. Любое предложение?
 TWiStErRob20 мар. 2015 г., 18:30
@Zordid, эти случаи должны быть позже в жизненном цикле, когда представление уже присоединено к иерархии, смотрите "Но иногда это работает!
 Danyal Aytekin20 мая 2014 г., 15:18
Арен»эти два наблюдателя разные? Так что у оригинала все равно будет слушатель.
 TWiStErRob20 мар. 2015 г., 18:31
@DanyalAytekin, они разные, но один является надмножеством другого, видите "Они одни и те же наблюдатели?

Лучше попытаться проверить, еслиgetViewTreeObserver является живым. Я думаю, что следующий код будет работать. На основеhttps://stackoverflow.com/a/15301092/2914140,https://stackoverflow.com/a/26193736/2914140 и некоторые другие.

** Обновить **

После прочтенияУдалить слушателя из 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() Значение true, тогда следующий вызов 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() ...

Другое решение будет работать просто отлично, но это не такНе могу объяснить, почему это происходит.

Здесь есть несколько вопросов для решения:

GlobalOn В.С.OnGlobal

Использование версии GlobalOn выдаст вам предупреждение об устаревании, но если вы проверите источник,просто вызывает версию OnGlobal, поэтому ониповторный эквивалент. Разница в том, что вы можете использовать только OnGlobal из API уровня 16, так что если выВы нацелены на более раннюю версиюПридется использовать GlobalOn и разобраться с этим предупреждением об устаревании.

Почему он мертв?

Обратите внимание, что этот вопрос о коде вonCreateView, гдеimageViewPicture еще не привязан к иерархии представлений, он только что раздут. Если вы посмотрите наView.getViewTreeObserver() Вы можете видеть, что это создает "плавающий» наблюдатель в этом случае. Затем, когда представление помещается в иерархиюdispatchAttachedToWindow называется который затем сливает окнои вид "Плавающие наблюдатели:

info.mTreeObserver.merge(mFloatingTreeObserver);

который перемещает всех зарегистрированных слушателей в окноНаблюдатель и убивает плавающего наблюдателя.

Оригинальный наблюдатель, который вы приобрели, является плавающим, который мертв, поэтому вызов add / remove для него приводит к вышеприведенному исключению.

Они одни и те же наблюдатели?

Как Danyal, я также был озадачен, почемуremoveGlobalOnLayoutListener работает на другого наблюдателя, но теперь этоясно с временным плавающим наблюдателем. Как плавающий один сливается с окномS наблюдателя слушатели перемещаются к другому наблюдателю, так что вызываяView.getViewTreeObserver() позже даст вам наблюдателя, содержащего вашего слушателя. Новый наблюдатель теперь отвечает за обработку вашего слушателя.

Но иногда работает !, и другое решение

Что касается Зордидакомментарий, почему во многих случаях этоПравильно удерживать локальную переменную (замыкание) можно объяснить аналогичным рассуждением: только что завышенное представление вonCreateView еще не прикреплено только немного позжес вернулся. Большинство из васВидно, вероятно, в методе послеonCreateView в жизненном цикле. поплавок»Решение s (OP) будет работать нормально, если код, связанный с наблюдателем, находится вonViewCreated, У каждого метода жизненного цикла есть этоs свои обязанности, поэтому я бы посоветовал разделить код следующим образом:

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 таким образом, иерархия жива к тому времени, когда вы играете с наблюдателями / слушателями.

 Weizhi03 нояб. 2017 г., 07:05
Отличный ответ. Я просто хочу указать на небольшую ошибку, почему это не работает для @NimrodDayan.final ViewTreeObserver observer = imageViewPicture.getViewTreeObserver(); назначитobserver к плавающемуViewTreeObserver, ПослеdispatchAttachedToWindow называется, этот плавающий наблюдатель будет объединен с окномViewTreeObserver и убит, поэтому есть исключение наobserver не живой. призваниеimageViewPicture.getViewTreeObserver() каждый раз должно быть решение и возвращает правильное (объединенное или плавающее).ViewTreeObserver
 TWiStErRob16 июн. 2015 г., 18:19
@ CodePond.org спасибо за указание на это. Я'Я посмотрю, когда вернусь к разработке Android снова. Просто любопытно: вы используете статический или динамический фрагмент? не должен»не имеет значения, но кто знает ... Я также сделал все тестирование на 4.4.2, мыя увижу.
 Nimrod Dayan16 июн. 2015 г., 18:02
Слава за подробный анализ, но ямне жаль говорить вам, что выВы не правы по поводу вашего совета по регистрации слушателя вonViewCreated и используя локальный окончательный вариантfinal ViewTreeObserver observer, Я протестировал его на Nexus 6 (Android 5.1) и постоянно получаю исключение, что наблюдатель не жив. Единственный способ, которым это работает, согласно @ ρяσѕρєя К решению.

Ваш ответ на вопрос