防止/捕获"IllegalArgumentException:参数必须是此视图的后代"错误

dmo*_*mon 54 android illegalargumentexception viewgroup viewroot

我有一个ListView,里面有一些可聚焦的组件(主要是EditTexts).是的,我知道这不是完全推荐的,但总的来说,几乎所有东西都工作正常,重点放在必须去的地方(我需要进行一些调整代码).无论如何,我的问题是,当用手指滚动列表然后在显示IME键盘时突然使用轨迹球,存在一种奇怪的竞争条件.有些东西必须超出范围并得到回收,此时该offsetRectBetweenParentAndChild()方法必须启动并抛出IllegalArgumentException.

问题是这个异常被抛出我可以插入try/catch的任何块之外(据我所知).所以这个问题有两个有效的解决方案:

  1. 有人知道为什么抛出这个异常以及如何阻止它发生
  2. 有人知道如何在某个地方放置一个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)
Run Code Online (Sandbox Code Playgroud)

Bru*_*uce 31

很抱歉告诉你,我发现我之前的答案不是解决这个问题的最完美方式.

所以我试试这个:
将一个ScrollListener附加到Activity,当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();
                }
            }
        }

    }
Run Code Online (Sandbox Code Playgroud)


ndo*_*ori 29

虽然布鲁斯的答案确实解决了这个问题,但它却以非常残酷的方式解决了这个问题,因为一旦我们做了滚动,它就会清除每个视图的焦点.

它处理问题的症状,但它没有解决实际原因.

如何重现问题:

您的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);
                }
            }
        }
    });
Run Code Online (Sandbox Code Playgroud)

  • 我在使用 EditText RecyclerView 时遇到了同样的异常。有没有人在回收商的观点上发现面临的问题? (6认同)

Bru*_*uce 24

试试这个

 @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
}
Run Code Online (Sandbox Code Playgroud)

编辑:

另见:更好的解决方案

  • 尝试添加一些解释此代码的作用以及它如何解决原始问题. (4认同)

dmo*_*mon 10

对于它的价值(或者偶然发现了什么),我已经放弃了此活动的ListView方法.除了随机崩溃之外,几乎不可能正确地获得焦点行为而不设置windowSoftInputMode="adjustPan"打开一堆其他的蠕虫.相反,我只是去了一个"简单"的ScrollView,它一直很好用.

  • 我可以在我的应用程序中使用 ListView 和 ScrollView 重现这个问题,如果我在单击焦点后快速滚动 EditText 不可见,ListView 会崩溃并且 ScrollView 会自行滚动。 (2认同)

小智 8

我稍微调整了布鲁斯的答案.

我需要adjustResize在我的活动中而不是adjustpan在我尝试时再次发生错误.
我换ScrollView<android.support.v4.widget.NestedScrollView,现在工作正常.希望这有助于某人!


Sno*_*ert 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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

就我而言,我有两个层视图,顶层是 listView,底层是 NestedScrollView。切换图层时发生错误。焦点由 ListeView 项目(按钮)获取。

所以我不能让按钮失去焦点。那么最好的解决方案是扩展 NestedScrollView。


val*_*cat 7

我面临着同样的问题,找到了这个解决方案-以OnGroupCollapseListener/OnGroupExpandListenerOnScrollListenerExpandableListView我明确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) {}

    });
Run Code Online (Sandbox Code Playgroud)

我不知道确切OnGroupExpandListener需要与否,它可能毫无用处.


Fen*_*nil 5

在可扩展列表视图的情况下,如果您的子项目具有编辑文本,那么您需要在可扩展列表视图的后代之前更改可聚焦性

expandableListView.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
Run Code Online (Sandbox Code Playgroud)