Предотвращение / отлов ошибки «IllegalArgumentException: параметр должен быть потомком этого представления» - PullRequest
51 голосов
/ 18 августа 2011

У меня есть ListView с некоторыми фокусируемыми компонентами внутри (в основном EditText с). Да, я знаю, что это не совсем рекомендуется, но в целом почти все работает нормально, и фокус направлен на то, что нужно (с некоторыми изменениями, которые мне пришлось кодировать). В любом случае, моя проблема в том, что при прокрутке списка пальцем и странном использовании трекбола при отображении клавиатуры 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)

Ответы [ 15 ]

31 голосов
/ 15 октября 2012

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

Итак, я пытаюсь это сделать:
Добавить 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();
                }
            }
        }

    }
27 голосов
/ 17 ноября 2016

В то время как ответ Брюса действительно решает проблему, он делает это очень жестоко, что наносит вред 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);
                }
            }
        }
    });
23 голосов
/ 15 октября 2012

попробуйте

 @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
}

EDIT:

См. Также: Лучшее решение

10 голосов
/ 24 апреля 2012

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

7 голосов
/ 28 мая 2014

Я столкнулся с той же проблемой и нашел это решение - в 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 нужно или нет, это может быть бесполезно.

5 голосов
/ 30 октября 2016

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

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

3 голосов
/ 09 сентября 2015

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

2 голосов
/ 22 августа 2018

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

public class FixFocusErrorNestedScrollView extends NestedScrollView {
@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.

2 голосов
/ 20 февраля 2018

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

2 голосов
/ 10 апреля 2017

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

expandableListView.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
...