Preventing/catching “IllegalArgumentException: parameter must be a descendant of this view” error

丶灬走出姿态 提交于 2019-11-26 18:38:49

I am sorry to tell you, I found my previous answer isn't the most perfect way to solve this problem.

So i try this :
Append a ScrollListener to your Activity, when listView start scrolling, clear current focus.

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();
                }
            }
        }

    }
ndori

While Bruce's answer does solve the problem, it does it in a very brutal way which harms the UX, as it will clear the focus of every view once we did a scroll.

It deals with the symptom of the problem but it does not solve the actual cause.

how to reproduce the problem:

Your EditText has focus and the keyboard is opened, you then scroll till the point the EditText is off the screen, and it wasn't recycled to a new EditText that is now shown.

Let's first understand why this problem happens:

ListView recycles its views and uses them again as you all know, but sometimes it does not need to use a view that has gone off the screen immediately so it keeps it for future use, and because it doesn't need to be shown anymore it will detach it causing that view.mParent to be null. however the keyboard needs to know how to pass the input to, and it does it by choosing the focused view, or EditText to be precise.

So the problem is that we have an EditText who has the focus, but suddenly does not have a parent, so we get a "parameter must be a descendant of this view” error. makes sense.

By using the scroll listener we are causing more problems.

The Solution:

We need to listen to an event which will tell us when a view has gone to the side heap and is no longer attached, luckily ListView exposes this event.

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);
                }
            }
        }
    });
Bruce

try this

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

See also: Better Solution

For what it's worth (or whoever stumbles on this), I've abandoned the ListView approach for this Activity. Aside from the random crashes, it's almost impossible to get the focus behavior correctly without setting the windowSoftInputMode="adjustPan" which opens a bunch of other cans of worms. Instead, I just went for a "simple" ScrollView and that has been working great.

I faced with the same problem and found out this solution - in OnGroupCollapseListener/OnGroupExpandListener and OnScrollListener for ExpandableListView i clear focuse and hide forced keyboard. Also do not forget to set in manifest for your activity 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) {}

    });

I don't know exactly OnGroupExpandListener is needed or no, it could be useless.

Hadi

I used bruce's answer with a slight adjustment.

I needed adjustResize in my activity instead of adjustpan but when I tried it the error occurred again.
I replaced ScrollView with <android.support.v4.widget.NestedScrollView and it works fine now. Hope this helps someone!

Bassel Shmali

I faced that problem, too, and the solution by validcat worked for me, but I had to call getWindow().getCurrentFocus().clearFocus().

In case of Expandable List View, if your child items have edit text then you need to change the focusability before descendants for Expandable List View

expandableListView.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);

I faced the same problem when using EditText in Recyclerview. After a lot of struggle and trying different option i found out the after deleting the row when my keyboard is opened produces this issue. I solved it by force closing my keyboard and changing notifyItemRemoved(position) with notifyDataSetChanged().

I have the simplest but not good solution. Just extend the NestedScrollView and override onSizeChanged method, add a try catch block.

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();
    }
}}

In my case, I have tow layer view, top layer is listView, bottom is NestedScrollView. The error is happend when I switch the layer. The focus be taked by ListeView item (button).

So I can't make button lose focus. Then the best solution is extends NestedScrollView.

In my case it was related to windowSoftInputMode="adjustPan", listView and editText on list element (header view).

In order to fix that I call hide soft keyboard method before activity is finished.

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);
    }
}

If none of the solutions suggested here apply to you...

I've experienced a similar error and noticed it was reported by my users' devices (after a crash) without any clear explanation on what was causing it (same as the log shown on the question) - more specifically the issue only happened on Samsung Galaxy (including S6) devices (but not on Nexus devices or others, which is why my testing initially failed to reveal the issue). So, first, it is worth checking if the issue is device specific or not.

What I later found is that when pressing the back button while a Samsung virtual keyboard was displayed on a text field, the application would crash throwing this error - but not always!

Indeed, the text field causing the crash also happened to be displayed within a scrollview with fillViewPort="true" enabled.

What I found is that removing the fillViewPort option from the scrollview would not conflict with the Samsung keyboard being displayed/hidden. I suspect the issue is partly due to the fact that Samsung keyboards are different virtual keyboards than the stock Nexus keyboards, which is why only a subset of my users were experiencing the issue and it would crash on their devices only.

As a general rule, and if none of the other solutions suggested here apply to you, I would check if the issue is device specific, and also attempt to simplify the view I am working on until I can find the "culprit component" (component and view that, I should add, wasn't reported in the crash logs - so I only stumbled on the specific view causing the issue by chance!).

Sorry I cannot be more specific, but I hope this gives some pointers for further investigation if someone experience a similar but unexplained issue.

My answer is related to most of the answers here, but I just wanted to add that in my case, this crash occurred due to removing a row with an edit text that currently had the focus.

So all I did was override the remove method of the adapter, and queried whether the removed row contains the current focus edit and if so, clear the focus.

That solved it for me.

Based on @Bruce answer, can resolve error with recyclerview like this:

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

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

I'm using RecyclerView and none of the presented solutions worked. I got the error when removing items.

What did work was overriding the Adapter's 'onItemDismiss(int position)' so that it first does a 'notifyDataSetChanged()' prior to removing the item and then does 'notifyItemRemoved(position)' after removing the item. Like this:

// 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);
    }
}

Also do an Override of 'removeAt(int position)' in the TabFragment to invoke the new cleanup code, like this:

// TabFragment code
@Override
public void removeAt(int position) {
    mAdapter.onItemDismiss(position);
    mAdapter.notifyItemRemoved(position); // <--- I put an extra notify here too
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!