Preventing/catching "IllegalArgumentException: parameter must be a descendant of this view" error
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);
}
}
}
});
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();
}
}
}
}
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