Предотвращение/отлов IllegalArgumentException: параметр должен быть потомком этой ошибки представления

У меня есть ListView с некоторыми фокусируемыми компонентами внутри (в основном EditTexts). Да, я знаю, что это не совсем рекомендуется, но в целом почти все работает нормально, и фокус идет туда, куда должен идти (с несколькими настройками, которые мне пришлось кодировать). Во всяком случае, моя проблема заключается в том, что возникает странное состояние гонки при прокрутке списка пальцем, а затем внезапное использование трекбола, когда отображается клавиатура IME. Что-то должно выйти за пределы и быть переработано, после чего должен сработать метод offsetRectBetweenParentAndChild() и выдать IllegalArgumentException.

Проблема в том, что это исключение выбрасывается вне любого блока, в который я могу вставить try/catch (насколько мне известно). Таким образом, есть два действительных решения этого вопроса:

  1. Кто-то знает, почему возникает это исключение и как его предотвратить.
  2. Кто-то знает, как поместить блок try/catch где-нибудь, что, по крайней мере, позволит моему приложению выжить. Насколько я знаю, проблема заключается в фокусе, поэтому он определенно не должен убивать мое приложение (что оно и делает). Я попытался переопределить методы ViewGroup, но эти два метода offset* помечены как окончательные.

Трассировки стека:

08-17 18:23:09.825: ERROR/AndroidRuntime(1608): FATAL EXCEPTION: main
08-17 18:23:09.825: ERROR/AndroidRuntime(1608): java.lang.IllegalArgumentException: parameter must be a descendant of this view
08-17 18:23:09.825: ERROR/AndroidRuntime(1608):     at android.view.ViewGroup.offsetRectBetweenParentAndChild(ViewGroup.java:2633)
08-17 18:23:09.825: ERROR/AndroidRuntime(1608):     at android.view.ViewGroup.offsetDescendantRectToMyCoords(ViewGroup.java:2570)
08-17 18:23:09.825: ERROR/AndroidRuntime(1608):     at android.view.ViewRoot.scrollToRectOrFocus(ViewRoot.java:1624)
08-17 18:23:09.825: ERROR/AndroidRuntime(1608):     at android.view.ViewRoot.draw(ViewRoot.java:1357)
08-17 18:23:09.825: ERROR/AndroidRuntime(1608):     at android.view.ViewRoot.performTraversals(ViewRoot.java:1258)
08-17 18:23:09.825: ERROR/AndroidRuntime(1608):     at android.view.ViewRoot.handleMessage(ViewRoot.java:1859)
08-17 18:23:09.825: ERROR/AndroidRuntime(1608):     at android.os.Handler.dispatchMessage(Handler.java:99)
08-17 18:23:09.825: ERROR/AndroidRuntime(1608):     at android.os.Looper.loop(Looper.java:130)
08-17 18:23:09.825: ERROR/AndroidRuntime(1608):     at android.app.ActivityThread.main(ActivityThread.java:3683)
08-17 18:23:09.825: ERROR/AndroidRuntime(1608):     at java.lang.reflect.Method.invokeNative(Native Method)
08-17 18:23:09.825: ERROR/AndroidRuntime(1608):     at java.lang.reflect.Method.invoke(Method.java:507)
08-17 18:23:09.825: ERROR/AndroidRuntime(1608):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
08-17 18:23:09.825: ERROR/AndroidRuntime(1608):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
08-17 18:23:09.825: ERROR/AndroidRuntime(1608):     at dalvik.system.NativeStart.main(Native Method)

person dmon    schedule 17.08.2011    source источник
comment
Для чего бы это ни стоило (или кто-то наткнется на это), я отказался от подхода ListView для этого Activity. Помимо случайных сбоев, почти невозможно правильно настроить поведение фокуса без установки windowSoftInputMode="adjustPan", который открывает кучу других червей. Вместо этого я просто выбрал простую ScrollView, и это отлично сработало.   -  person dmon    schedule 19.08.2011
comment
Есть кирпичная стена, делающая что-то подобное, ListView с EditTexts просто не стоит. На самом деле этот самый вопрос был для меня шоу-стоппер.   -  person devmiles.com    schedule 02.02.2012
comment
У меня точно такой же аварийный стек из рабочего приложения, и я не могу смоделировать его самостоятельно. У меня есть один экран с ListView, который содержит динамически заполняемые EditViews, Chekcboxes и Spinners в зависимости от выборки данных с сервера. Авария раздражает. Какой динамический родительский вид следует использовать в качестве контейнера для динамических фокусируемых представлений (EditViews, Check, Spinners)?   -  person kiruwka    schedule 31.10.2014
comment
Кроме того, ссылаясь на цитату ОП: Yeah, I know this isn't exactly recommended. Есть ли у кого-нибудь ссылка, подтверждающая это и дающая объяснения, почему? Огромное спасибо!   -  person kiruwka    schedule 31.10.2014
comment
Альтернативой EditTexts в ListView является TextViews, оформленный как EditTexts, который при нажатии всплывает стилизованный Dialog с EditText в нем.   -  person FunkTheMonk    schedule 30.01.2015
comment
Я использую EditText внутри элемента RecyclerView, и при нанесении второго элемента появляется исключение EditText.   -  person Vivek Pratap Singh    schedule 22.09.2017


Ответы (16)


Хотя ответ Брюса действительно решает проблему, он делает это очень жестоким образом, что вредит UX, поскольку очищает фокус каждого представления, как только мы сделали прокрутку.

Он имеет дело с симптомом проблемы, но не устраняет фактическую причину.

как воспроизвести проблему:

Ваш EditText имеет фокус, и клавиатура открыта, затем вы прокручиваете до точки, когда EditText находится за пределами экрана, и он не был переработан в новый EditText, который теперь отображается.

Давайте сначала разберемся, почему возникает эта проблема:

ListView перерабатывает свои представления и использует их снова, как вы все знаете, но иногда ему не нужно использовать представление, которое сразу исчезло с экрана, поэтому он сохраняет его для будущего использования, и поскольку его больше не нужно показывать, он отсоединит его, в результате чего view.mParent будет нулевым. однако клавиатура должна знать, как передать ввод, и она делает это, выбирая сфокусированное представление или, если быть точным, EditText.

Итак, проблема в том, что у нас есть EditText, который имеет фокус, но внезапно не имеет родителя, поэтому мы получаем ошибку «параметр должен быть потомком этого представления». Имеет смысл.

Используя прослушиватель прокрутки, мы создаем больше проблем.

Решение:

Нам нужно прослушать событие, которое сообщит нам, когда представление перешло в боковую кучу и больше не привязано, к счастью, ListView предоставляет это событие.

listView.setRecyclerListener(new AbsListView.RecyclerListener() {
        @Override
        public void onMovedToScrapHeap(View view) {
            if ( view.hasFocus()){
                view.clearFocus(); //we can put it inside the second if as well, but it makes sense to do it to all scraped views
                //Optional: also hide keyboard in that case
                if ( view instanceof EditText) {
                    InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
                    imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
                }
            }
        }
    });
person ndori    schedule 17.11.2016
comment
У вас есть пример кода для воспроизведения исключения? - person Guillaume Perrot; 24.01.2017
comment
У меня нет примера кода, но я думаю, что его довольно легко воспроизвести: создайте listView, создайте много строк listView с текстом редактирования внутри, запустите, затем сосредоточьтесь на editTexts (клавиатура будет открыта), теперь прокрутите чтобы этой строки не было на экране (пока клавиатура еще открыта) - person ndori; 24.01.2017
comment
Это было единственное решение, представленное здесь, которое сработало для меня. - person Rob; 03.02.2017
comment
Потрясающее объяснение! - person Edson Horacio Junior; 22.05.2017
comment
Я получаю то же исключение, используя EditText RecyclerView. Кто-нибудь находил сталкивался на recycler view? - person Vivek Pratap Singh; 22.09.2017
comment
Я еще не использовал EditText с RecyclerView, но я вижу, что в RecyclerView также есть setRecyclerListener, вы пробовали это решение? можете подтвердить, работает или нет? - person ndori; 24.09.2017
comment
Для разработчиков Xamarin, заинтересованных в этой проблеме/исправлении: bugzilla.xamarin.com/show_bug. cgi?id=59775#c3 - person Frank Schwieterman; 30.09.2017

Мне жаль сообщать вам, я обнаружил, что мой предыдущий ответ не самый идеальный способ решить эту проблему.

Итак, я пробую это:
Добавьте ScrollListener к своей деятельности, когда listView начнет прокручиваться, очистите текущий фокус.

protected class MyScrollListener implements OnScrollListener {

        @Override
        public void onScroll(AbsListView view, int firstVisibleItem,
                int visibleItemCount, int totalItemCount) {
            // do nothing 
        }

        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {
            if (SCROLL_STATE_TOUCH_SCROLL == scrollState) {
                View currentFocus = getCurrentFocus();
                if (currentFocus != null) {
                    currentFocus.clearFocus();
                }
            }
        }

    }
person Bruce    schedule 15.10.2012
comment
Это сработало для меня. В моем сценарии у меня был ExpandableListView с EditText, установленным в качестве заголовка, с использованием android:windowSoftInputMode=adjustPan в манифесте для действия. Прокрутка ExpandableListView вниз, когда клавиатура IME была поднята, надежно приводила к сбою устройства. Это исправлено, спасибо. - person Dororo; 14.01.2013
comment
В моем случае проблема возникала, когда я удалял строку из представления списка, когда фокус находился внутри текста редактирования этой строки. Этот ответ заставил меня добавить parent.clearChildFocus(parent.findFocus()) в мой прослушиватель onClick для удаления щелчка. Я поместил его в try-catch перед моим вызовом, чтобы удалить элемент в этой позиции. Мой прослушиватель onClick находится в getView моего адаптера, где parent является ViewGroup - person Mira_Cole; 29.11.2018
comment
У меня такая же проблема @Mira_Cole. Не могли бы вы объяснить мне немного больше, пожалуйста? - person jvargas; 23.04.2020

попробуй это

 @Override
public View getView(int position, View convertView, ViewGroup parent) {
    //abandon current focus
    View currentFocus = ((Activity)mContext).getCurrentFocus();
    if (currentFocus != null) {
        currentFocus.clearFocus();
    }

    // other code
}

ИЗМЕНИТЬ:

См. также: лучшее решение

person Bruce    schedule 15.10.2012
comment
Попробуйте добавить объяснение того, что делает этот код и как он решает исходную проблему. - person Jake McGraw; 15.10.2012
comment
Я не очень уверен, что это решение адаптирует такую ​​​​проблему. На мой взгляд, эта проблема из-за конфликта между listView и IME KeyBoard. Когда вы помещаете EditText в ListView и пытаетесь его отредактировать, IME KeyBoard может отображать подсказку, наведенную на EditText, и когда вы перемещаете ListView до тех пор, пока сфокусированный EditText не исчезнет, ​​произойдет сбой приложения. Поэтому я думаю, что представление подсказки имеет какой-то конфликт с ListView. - person Bruce; 31.10.2012

Для чего бы это ни стоило (или кто-то наткнется на это), я отказался от подхода ListView для этого действия. Помимо случайных сбоев, почти невозможно правильно настроить поведение фокуса без установки windowSoftInputMode="adjustPan", который открывает кучу других червей. Вместо этого я просто выбрал «простой» ScrollView, и он отлично работает.

person dmon    schedule 24.04.2012
comment
Я могу воспроизвести эту проблему с помощью ListView и ScrollView в своем приложении, ListView дает сбой, а ScrollView прокручивается самостоятельно, если я быстро прокручиваю EditText невидимым после нажатия на него фокуса. - person Niko; 02.12.2013
comment
Не могли бы вы помочь с решением вокруг этого? Я не могу воспроизвести проблему, но она часто падает с этой трассировкой стека в производстве. У меня есть ListView для SearchFilter с динамически заполняемыми (фокусируемыми) элементами, такими как EditText, Spinners, на основе ответа сервера. Нужно ли мне избавиться от моего ListView и разместить их в каком-то представлении, отличном от адаптера? - person kiruwka; 31.10.2014

Я использовал ответ Брюса с небольшой корректировкой.

Мне нужно было adjustResize в моей деятельности вместо adjustpan, но когда я попробовал, ошибка возникла снова.
Я заменил ScrollView на <android.support.v4.widget.NestedScrollView, и теперь все работает нормально. Надеюсь, это поможет кому-то!

person Hadi    schedule 30.10.2016
comment
Потрясающий ! Этот тихий, похороненный ответ помог мне! - person Krishnan V S; 30.12.2017
comment
Удивительно. Большое тебе спасибо - person Renat Kaitmazov; 11.12.2018

У меня есть самое простое, но не очень хорошее решение. Просто расширьте NestedScrollView и переопределите метод onSizeChanged, добавьте блок try catch.

public class FixFocusErrorNestedScrollView extends NestedScrollView {

    public FixFocusErrorNestedScrollView(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        try {
            super.onSizeChanged(w, h, oldw, oldh);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

В моем случае у меня есть представление двух слоев, верхний слой — listView, нижний — NestedScrollView. Ошибка возникает, когда я переключаю слой. Фокус принимает элемент ListeView (кнопка).

Поэтому я не могу заставить кнопку потерять фокус. Тогда лучшим решением будет расширение NestedScrollView.

person Snow Albert    schedule 22.08.2018
comment
Когда четкий фокус и другие вещи не работают, это решение работает. - person SANAT; 22.08.2019

Я столкнулся с той же проблемой и нашел это решение - в OnGroupCollapseListener/OnGroupExpandListener и OnScrollListener для ExpandableListView я очищаю фокус и скрываю принудительную клавиатуру. Также не забудьте установить в manifest для своей активности windowSoftInputMode="adjustPan":

    expListView.setOnGroupCollapseListener(new OnGroupCollapseListener() {

        @Override
        public void onGroupCollapse(int groupPosition) {
            InputMethodManager inputManager = (InputMethodManager) getApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE);
            if (getWindow().getCurrentFocus() != null) {
                inputManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
                getCurrentFocus().clearFocus();
            }
        }
    });

    expListView.setOnGroupExpandListener(new OnGroupExpandListener() {

        @Override
        public void onGroupExpand(int groupPosition) {
            InputMethodManager inputManager = (InputMethodManager) getApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE);
            if (getWindow().getCurrentFocus() != null) {
                inputManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
                getCurrentFocus().clearFocus();
            }
        }
    });

    expListView.setOnScrollListener(new OnScrollListener() {

        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {
            InputMethodManager inputManager = (InputMethodManager) getApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE);
            if (getCurrentFocus() != null) {
                inputManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
                getCurrentFocus().clearFocus();
            }
        }

        @Override
        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {}

    });

Я не знаю точно нужен OnGroupExpandListener или нет, он может быть бесполезен.

person validcat    schedule 28.05.2014

В случае представления расширяемого списка, если ваши дочерние элементы имеют редактируемый текст, вам необходимо изменить фокусируемость перед потомками для представления расширяемого списка.

expandableListView.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
person Fenil    schedule 10.04.2017
comment
Для меня это также сработало на RecyclerView - person Klaasel; 17.05.2021

Я столкнулся с той же проблемой при использовании EditText в Recyclerview. После долгих усилий и попыток использовать другой вариант я обнаружил, что после удаления строки при открытии моей клавиатуры возникает эта проблема. Я решил это, принудительно закрыв клавиатуру и заменив notifyItemRemoved(position) на notifyDataSetChanged().

person M.Waqas Pervez    schedule 20.02.2018
comment
Этот ответ помог мне понять, откуда возникла проблема. Однако предложение не решило проблему. Этот ответ привел меня к решению, которое заключалось в том, чтобы очистить фокус перед удалением строки. +1 за помощь в решении этой проблемы. Спасибо! - person Mira_Cole; 29.11.2018
comment
Как ты заставил клавиатуру? Я столкнулся с точно такой же проблемой здесь - person jvargas; 23.04.2020

Я тоже столкнулся с этой проблемой, и решение от validcat помогло мне, но мне пришлось позвонить getWindow().getCurrentFocus().clearFocus().

person Bassel Shmali    schedule 08.09.2015

Основываясь на ответе @Bruce, можно устранить ошибку с помощью recyclerview следующим образом:

@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        View currentFocus = ((Activity)context).getCurrentFocus();
        if (currentFocus != null) {
            currentFocus.clearFocus();
        }
}
person SANAT    schedule 22.06.2016
comment
Я также сталкиваюсь с этой проблемой, и я использую представление ресайклера. Я использую ваше решение, но оно не работает с видом ресайклера. вы пробовали с просмотром ресайклера? - person Vivek Pratap Singh; 22.09.2017
comment
В моем случае это не сработало, и ответ @Hadi сработал для меня. - person Vivek Pratap Singh; 22.09.2017

В моем случае это было связано с windowSoftInputMode="adjustPan", listView и editText в элементе списка (представление заголовка).

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

public void hideKeyboard(Activity activity) {
    InputMethodManager inputMethodManager = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
    View focusView = activity.getCurrentFocus();
    if (focusView != null) {
        inputMethodManager.hideSoftInputFromWindow(focusView.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
    }
}
person fabiozo    schedule 15.05.2015

Если ни одно из предложенных здесь решений не подходит вам...

Я столкнулся с аналогичной ошибкой и заметил, что устройства моих пользователей сообщили об этом (после сбоя) без какого-либо четкого объяснения того, что ее вызвало (так же, как журнал, показанный в вопросе) - более конкретно, проблема возникла только на Samsung Устройства Galaxy (включая S6) (но не устройства Nexus или другие, поэтому мое тестирование изначально не выявило проблему). Итак, во-первых, стоит проверить, связана ли проблема с конкретным устройством или нет.

Позже я обнаружил, что при нажатии кнопки «Назад», когда виртуальная клавиатура Samsung отображалась в текстовом поле, приложение вылетало с ошибкой, но не всегда!

Действительно, текстовое поле, вызвавшее сбой, также отображалось в прокрутке с включенным fillViewPort="true".

Я обнаружил, что удаление опции fillViewPort из прокрутки не будет конфликтовать с отображением/скрытием клавиатуры Samsung. Я подозреваю, что проблема отчасти связана с тем, что клавиатуры Samsung — это виртуальные клавиатуры, отличные от стандартных клавиатур Nexus, поэтому только часть моих пользователей сталкивалась с проблемой, и она зависала только на их устройствах.

Как правило, и если ни одно из других предложенных здесь решений не применимо к вам, я бы проверил, связана ли проблема с конкретным устройством, а также попытался упростить представление, над которым я работаю, пока не найду «виновный компонент» (компонент и представление, о котором, должен добавить, не сообщалось в журналах сбоев, поэтому я случайно наткнулся на конкретное представление, вызвавшее проблему!).

Извините, я не могу быть более конкретным, но я надеюсь, что это даст некоторые указания для дальнейшего исследования, если у кого-то возникнет похожая, но необъяснимая проблема.

person Pelpotronic    schedule 11.07.2016
comment
Я столкнулся с этим на LG Nexus 6, так что это не зависит от устройства. - person Harpreet; 12.08.2016
comment
Дело в том, что эта проблема является ошибкой времени выполнения Android, связанной с ViewGroup, и поэтому не будет единого решения, которое будет работать для всех, в зависимости от точной причины, по которой ваш код вообще вызывает эту проблему. Теперь, чтобы внести ясность, я не утверждаю, что эта проблема всегда зависит от устройства (я начинаю с «Если ни одно из других решений не применимо к вам»), но это определенно было в конкретном случае проблемы ViewGroup, с которой я столкнулся. Возможно, попробуйте другие решения в вашем случае, если это не конкретное устройство для вас. - person Pelpotronic; 13.08.2016
comment
Проблема почти наверняка НЕ ​​из-за клавиатуры, а из-за того, что по какой-то причине Samsung вносит какое-то существенное изменение в исходный код ListView. Каждый раз, когда я отлаживаю приложение на устройстве Samsung, отображение исходного кода становится шатким всякий раз, когда выполнение входит в ListView, и это потому, что источник не совпадает. - person j__m; 27.08.2016

Мой ответ связан с большинством ответов здесь, но я просто хотел добавить, что в моем случае этот сбой произошел из-за удаления строки с текстом редактирования, который в данный момент был в фокусе.

Итак, все, что я сделал, это переопределил метод удаления адаптера и запросил, содержит ли удаленная строка текущее редактирование фокуса, и если да, то очистить фокус.

Это решило это для меня.

person Ido Sofi    schedule 11.07.2014
comment
Как бы вы сделали это для recyclerview? Я думаю, что моя проблема именно то, что вы описываете. Но я понятия не имею, как определить, удаляется ли строка. Можете ли вы предоставить пример кода? Спасибо... - person Sam; 06.08.2017
comment
Вау, взрыв из прошлого... к сожалению, слишком древний, чтобы отвечать. Можете ли вы свести проблему к чему-то, что легко воспроизводится? я могу взглянуть - person Ido Sofi; 06.08.2017

Я использую RecyclerView, и ни одно из представленных решений не сработало. Я получил ошибку при удалении элементов.

Что действительно сработало, так это переопределение адаптера «onItemDismiss (int position)», чтобы он сначала выполнял «notifyDataSetChanged ()» до удаления элемента, а затем выполнял «notifyItemRemoved (position)» после удаления элемента. Как это:

// Adapter code
@Override
public void onItemDismiss(int position) {
    if (position >= 0 && getTheList() != null && getTheList().size() > position) {
        notifyDataSetChanged();  // <--- this fixed it.
        getTheList().remove(position);
        scrollToPosition(position);
        notifyItemRemoved(position);
    }
}

Также выполните переопределение «removeAt (int position)» в TabFragment, чтобы вызвать новый код очистки, например:

// TabFragment code
@Override
public void removeAt(int position) {
    mAdapter.onItemDismiss(position);
    mAdapter.notifyItemRemoved(position); // <--- I put an extra notify here too
}
person Lee Hounshell    schedule 18.12.2016

Я заменил ScrollView на ‹android.support.v4.widget.NestedScrollView, и теперь он работает нормально.

person Imran Vora    schedule 17.03.2021
comment
Чем это отличается от ответа Хади? - person Vicky; 18.03.2021
comment
Он все еще существует。 - person jiong103; 06.07.2021