66

I have a ListView with some focusable components inside (mostly EditTexts). Yeah, I know this isn't exactly recommended, but in general, almost everything is working fine and the focus goes where it has to go (with a few tweaks I had to code). Anyway, my problem is that there's a weird race condition when scrolling the list with your finger and then suddenly using the trackball when the IME keyboard is being displayed. Something must go out of bounds and get recycled at which point the offsetRectBetweenParentAndChild() method must kick in and throw the IllegalArgumentException.

The problem is that this exception is thrown outside of any block in which I can insert a try/catch (as far as I know). So there are two valid solutions to this question, either:

  1. Someone knows why this exception being thrown and how to stop it from happening
  2. Someone knows how to put a try/catch block somewhere that will at least let my application survive. As far as I know the problem is that of focus, so it definitely shouldn't kill my application (which is what it's doing). I tried overriding the ViewGroup's methods but those two offset* methods are marked as final.

Stack trace:

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)
dmon
  • 30,048
  • 8
  • 87
  • 96
  • 1
    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. – dmon Aug 19 '11 at 04:30
  • 1
    Got a brick wall doing something similar, `ListView` with `EditTexts` just isn't worth it. Actually this very issue was a showstopper for me. – devmiles.com Feb 02 '12 at 11:54
  • I have exactly same crash stack coming from app in production and I can not simulate it myself. I have one screen with ListView that contains dynamically populated EditViews, Chekcboxes and Spinners depending on data fetch from the server. The crash is annoying. What Dynamic parent View should I use as a container for the dynamic focusable views (EditViews, Check, Spinners) ? – kiruwka Oct 31 '14 at 13:51
  • Also, referring to OP's quote : `Yeah, I know this isn't exactly recommended`. Does any have any reference supporting this and giving explanations why ? Huge thanks! – kiruwka Oct 31 '14 at 14:10
  • 1
    An alternative to `EditText`s in a `ListView` is to have `TextView`s styled as `EditText`s, which, when click pops up a styled `Dialog` with the `EditText` in it. – FunkTheMonk Jan 30 '15 at 09:04
  • i am using EditText inside RecyclerView Item and on Taping second Item's EditText Exception is coming. – Vivek Pratap Singh Sep 22 '17 at 11:05
  • In case anyone reaches this question after experiencing this crash in an Android React Native App (which uses Google Ads), see the comment on this related [issue](https://github.com/facebook/react-native/issues/32649#issuecomment-990887288) where a workaround has been identified. – Jadent Dec 14 '21 at 12:21

18 Answers18

40

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);
                }
            }
        }
    });
Community
  • 1
  • 1
ndori
  • 1,934
  • 1
  • 18
  • 23
32

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

    }
Bruce
  • 2,146
  • 2
  • 26
  • 22
  • 1
    This worked for me. In my scenario, I had an ExpandableListView with an EditText set as a header, using android:windowSoftInputMode="adjustPan" in the manifest for the activity. Scrolling down the ExpandableListView whilst the IME keyboard was up reliably crashed the device. This fixed it, thanks. – Dororo Jan 14 '13 at 16:29
  • In mine, the issue was happening when I would remove a row from my list view when the focus was inside of that row's edit text. This answer led me to add `parent.clearChildFocus(parent.findFocus())` within my onClick listener for my remove click. I put it in a try-catch before my call to remove the item at that position. My onClick listener is in the getView of my adapter, with `parent` being `ViewGroup` – Mira_Cole Nov 29 '18 at 14:32
  • I have the same issue @Mira_Cole. Could you explain me a litle more please? – jvargas Apr 23 '20 at 15:26
24

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

Community
  • 1
  • 1
Bruce
  • 2,146
  • 2
  • 26
  • 22
  • 4
    Try adding some explanation of what this code does and how it solves the original problem. – Jake McGraw Oct 15 '12 at 03:13
  • 1
    I am not very sure this solution adapt this kind of problem. In my opinion, this problem is because of the conflict between listView and IME KeyBoard. When you put a EditText in the ListView, and try to edit it, the IME KeyBoard may show a hint view hover on the EditText, and when you slide the ListView until the focused EditText disappearing, the app crash. So i think the hint view is having some kind of conflict to the ListView. – Bruce Oct 31 '12 at 02:03
11

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.

dmon
  • 30,048
  • 8
  • 87
  • 96
  • 2
    I can reproduce this issue with ListView and ScrollView in my app, ListView crashes and ScrollView scrolls by it's own if I scroll the EditText fast invisible after clicking focus to it. – Niko Dec 02 '13 at 13:00
  • 1
    Could you please help with solution around this ? I can't reproduce the problem, but it crashes often with this stacktrace in production. I have a ListView for SearchFilter with dynamically populated (focusable) elements such as EditText, Spinners based on server's response. Do i need to get rid of my ListView and host them in some non-Adapter View ? – kiruwka Oct 31 '14 at 13:57
11

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!

Suraj Vaishnav
  • 7,777
  • 4
  • 43
  • 46
Hadi
  • 520
  • 9
  • 21
9

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 {

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

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.

SANAT
  • 8,489
  • 55
  • 66
Snow Albert
  • 547
  • 7
  • 15
6

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.

validcat
  • 6,158
  • 2
  • 29
  • 38
4

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

Community
  • 1
  • 1
Bassel Shmali
  • 207
  • 2
  • 15
  • I used View Binding in my fragment. It has a Recyclerview and I was submitting a updated list to an existing list where the app got crashed giving this error "parameter must a descendant of this view", happened after I entered value in EditText. Error was due to there was still focus present on the current edit text even after keyboard is hidden. The above solution worked like, telling the recyclerview that focus is no longer required. I've done it using `binding.getRoot().clearFocus()` – Deepak J Aug 26 '21 at 06:51
4

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);
Fenil
  • 1,194
  • 10
  • 11
4

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().

M.Waqas Pervez
  • 2,492
  • 2
  • 19
  • 33
  • This answer helped me know _where_ the problem was coming from. However, the suggestion did not fix the problem. [This answer](https://stackoverflow.com/a/12892317/8183283) led me to the solution which was to clear the focus before removing the row. +1 for assist in helping me fix this issue. Thanks! – Mira_Cole Nov 29 '18 at 14:35
  • How did you force the keyboard? I am facing the exact same issue here – jvargas Apr 23 '20 at 15:25
2

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();
        }
}
SANAT
  • 8,489
  • 55
  • 66
1

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.

Pelpotronic
  • 550
  • 6
  • 11
  • I am facing it on LG Nexus 6, so it's not device specific. – Harpreet Aug 12 '16 at 17:22
  • The thing is that this issue is an Android runtime error that has to do with "ViewGroup" and so there won't be one solution that will work for everyone depending on the exact reason why your code is throwing this issue in the first place. Now to be clear, I'm not suggesting this issue is always device specific (I start with "If none of the other solutions apply to you") but it certainly was in the specific instance of the ViewGroup issue I encountered. Maybe try the other solutions in your case if it is not device specific for you. – Pelpotronic Aug 13 '16 at 09:20
  • The problem is almost certainly NOT due to the keyboard, but due to the fact that for some reason Samsung makes some kind of significant change to the ListView source code. Every time I've debugged an app on a Samsung device the source code display goes wonky whenever execution enters a ListView, and it's because the source doesn't match. – j__m Aug 27 '16 at 01:44
1

I am getting the same issue when I used editText in recyclerview item. If edittext state is focused and softkeyboard is open. I am trying to close my current fragment and app is crashed so what I did is before calling finished I used

binding.root.clearFocus()

And same I did in adapter

holder.binding.root.clearFocus()
Dharman
  • 30,962
  • 25
  • 85
  • 135
Qamar khan
  • 179
  • 1
  • 7
0

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.

Ido Sofi
  • 180
  • 1
  • 1
  • 11
  • How would you do it for recyclerview? I think my problem is exactly what you are describing. But I've no idea how to detect if the row is being removed. Can you provide a sample code? Thanks... – Sam Aug 06 '17 at 11:48
  • Wow, blast from the past... too ancient to reply unfortunately. Can you minimize the problem to something that easily reproduces? I can take a look – Ido Sofi Aug 06 '17 at 16:18
0

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);
    }
}
fabiozo
  • 249
  • 3
  • 5
0

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
}
Lee Hounshell
  • 842
  • 9
  • 10
0

I replaced ScrollView with <android.support.v4.widget.NestedScrollView and it works fine now.

Dharman
  • 30,962
  • 25
  • 85
  • 135
Imran Vora
  • 47
  • 1
  • 3
0

I used a similar approach to Snow Albert's, but instead of catching the exception thrown in onSizeChanged(), I disabled children's focusability to prevent the crash:

class RestrictedFocusabilityScrollView : NestedScrollView {

constructor(context: Context) : this(context, null, 0)

constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)

constructor(context: Context, attrs: AttributeSet?, defStyle: Int) : super(
    context,
    attrs,
    defStyle
)

override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
    //Making children not focusable during layout to avoid the "parameter must be a descendant of this view" crash
    children.forEach { it.isFocusableInTouchMode = false }
    super.onSizeChanged(w, h, oldw, oldh)
    children.forEach { it.isFocusableInTouchMode = true }
}

}

marcinP
  • 106
  • 1
  • 4