Подтвердить что ты не робот

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

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

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

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

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

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)
4b9b3361

Ответ 1

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

Ответ 2

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

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

    }

Ответ 3

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

Он относится к симптому проблемы, но не решает реальной причины.

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

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

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

ListView перерабатывает свои представления и использует их снова, как вы все знаете, но иногда ему не нужно использовать представление, которое сразу же отключилось от экрана, чтобы оно сохраняло его для будущего использования, и потому, что ему не нужно показанный больше, он отделит его, в результате чего view.mParent будет null. однако клавиатура должна знать, как передать вход, и он делает это, выбрав точное сфокусированное представление или 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);
                }
            }
        }
    });

Ответ 4

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

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

Смотрите также: Лучшее решение

Ответ 5

Я столкнулся с той же проблемой и узнал это решение - в OnGroupCollapseListener/OnGroupExpandListener и OnScrollListener для ExpandableListView я очистил focuse и спрятал принудительную клавиатуру. Также не забудьте указать 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 или нет, это может быть бесполезно.

Ответ 6

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

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

Ответ 7

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

Ответ 8

У меня самое простое, но не хорошее решение. Просто расширьте 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.

Ответ 9

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

expandableListView.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);

Ответ 10

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

Ответ 11

В моем случае это было связано с 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);
    }
}

Ответ 12

Если ни одно из предлагаемых решений не применимо к вам...

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

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

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

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

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

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

Ответ 13

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

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

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

Ответ 14

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

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

        View currentFocus = ((Activity)context).getCurrentFocus();
        if (currentFocus != null) {
            currentFocus.clearFocus();
        }
}

Ответ 15

Я использую 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
}