60

いくつかのフォーカス可能なコンポーネントが内部にあるListViewがあります(ほとんどはEditTexts)。ええ、これが正確に推奨されているわけではないことは知っていますが、一般的に、ほとんどすべてが正常に機能しており、焦点は必要な場所に移動します(コーディングする必要があるいくつかの調整があります)。とにかく、私の問題は、指でリストをスクロールし、IMEキーボードが表示されているときに突然トラックボールを使用すると、奇妙な競合状態が発生することです。何かが範囲外に出てリサイクルされる必要があり、その時点でoffsetRectBetweenParentAndChild()メソッドが開始してをスローする必要がありIllegalArgumentExceptionます。

問題は、この例外が、try / catchを挿入できるブロックの外でスローされることです(私が知る限り)。したがって、この質問には2つの有効な解決策があります。

  1. 誰かがこの例外がスローされる理由とそれが起こらないようにする方法を知っています
  2. 誰かがtry/catchブロックをどこかに配置して、少なくとも私のアプリケーションを存続させる方法を知っています。私が知る限り、問題は焦点の問題であるため、アプリケーションを強制終了することは絶対にありません(これが実行していることです)。のメソッドをオーバーライドしようとしましViewGroupたが、これら2つの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)
4

16 に答える 16

38

ブルースの答えは問題を解決しますが、スクロールを実行するとすべてのビューのフォーカスがクリアされるため、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);
                }
            }
        }
    });
于 2016-11-17T16:05:54.727 に答える
33

申し訳ありませんが、以前の回答はこの問題を解決するための最も完璧な方法ではないことがわかりました。

だから私はこれを試してみます:
あなたのアクティビティに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();
                }
            }
        }

    }
于 2012-10-15T09:02:37.577 に答える
25

これを試して

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

編集:

参照:より良いソリューション

于 2012-10-15T03:05:14.227 に答える
12

それが価値があるもの(またはこれにつまずいた人)のために、私はこのアクティビティのListViewアプローチを放棄しました。ランダムなクラッシュを除けば、windowSoftInputMode="adjustPan"他のワームの缶を開くように設定しないと、フォーカスの動作を正しく取得することはほとんど不可能です。代わりに、私は「シンプルな」ScrollViewを選びましたが、それはうまく機能しています。

于 2012-04-24T17:19:11.863 に答える
9

ブルースの答えを少し調整して使用しました。

adjustResize代わりに自分のアクティビティで必要でしたadjustpanが、試してみるとエラーが再度発生しました。
に交換ScrollViewしましたが<android.support.v4.widget.NestedScrollView、正常に動作します。これが誰かに役立つことを願っています!

于 2016-10-30T11:07:49.533 に答える
8

私には最も単純ですが、良い解決策はありません。NestedScrollViewを拡張し、onSizeChangedメソッドをオーバーライドして、trycatchブロックを追加するだけです。

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

私の場合、2つのレイヤービューがあり、上のレイヤーはlistView、下のレイヤーはNestedScrollViewです。レイヤーを切り替えるとエラーが発生します。ListeViewアイテム(ボタン)がフォーカスを取ります。

そのため、ボタンのフォーカスを失うことはできません。次に、最良の解決策はNestedScrollViewを拡張することです。

于 2018-08-22T09:11:59.947 に答える
7

私は同じ問題に直面し、この解決策を見つけました-私はフォーカスをクリアし、OnGroupCollapseListener/OnGroupExpandListener強制キーボードを非表示にします。また、あなたの活動に着手することを忘れないでください:OnScrollListenerExpandableListViewmanifestwindowSoftInputMode="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、役に立たない可能性があります。

于 2014-05-28T08:53:59.927 に答える
4

私もその問題に直面し、validcatによる解決策は私のために働いたが、私は電話しなければならなかったgetWindow().getCurrentFocus().clearFocus()

于 2015-09-08T21:37:55.253 に答える
4

拡張可能なリストビューの場合、子アイテムに編集テキストがある場合は、拡張可能なリストビューの子孫の前にフォーカス可能性を変更する必要があります

expandableListView.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
于 2017-04-10T08:40:59.523 に答える
4

EditTextで使用するときに同じ問題に直面しましたRecyclerview。多くの苦労と別のオプションを試した後、キーボードを開いたときに行を削除した後、この問題が発生することがわかりました。キーボードを強制的に閉じて、で変更することで解決しましnotifyItemRemoved(position)notifyDataSetChanged()

于 2018-02-20T11:45:27.337 に答える
2

@Bruceの回答に基づいて、次のようなrecyclerviewでエラーを解決できます。

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

        View currentFocus = ((Activity)context).getCurrentFocus();
        if (currentFocus != null) {
            currentFocus.clearFocus();
        }
}
于 2016-06-22T06:50:47.727 に答える
1

私の場合、それはwindowSoftInputMode="adjustPan"リスト要素(ヘッダービュー)のlistViewとeditTextに関連していました。

これを修正するために、アクティビティが終了する前にhidesoftkeyboardメソッドを呼び出します。

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);
    }
}
于 2015-05-15T08:51:53.533 に答える
1

ここで提案された解決策のどれもあなたに当てはまらない場合...

私は同様のエラーを経験し、それが原因の明確な説明なしに(クラッシュ後)ユーザーのデバイスによって報告されたことに気づきました(質問に示されているログと同じ)-より具体的には、問題はSamsungでのみ発生しましたGalaxy(S6を含む)デバイス(ただし、Nexusデバイスなどではないため、最初にテストで問題を明らかにできませんでした)。したがって、最初に、問題がデバイス固有であるかどうかを確認する価値があります。

後で見つけたのは、Samsung仮想キーボードがテキストフィールドに表示されているときに戻るボタンを押すと、アプリケーションがクラッシュしてこのエラーが発生することですが、常にそうとは限りません。

実際、クラッシュの原因となったテキストフィールドも、fillViewPort="true"が有効になっているスクロールビュー内に表示されていました。

私が見つけたのは、スクロールビューからfillViewPortオプションを削除しても、表示/非表示になっているSamsungキーボードと競合しないということです。この問題の原因の一部は、Samsungキーボードが標準のNexusキーボードとは異なるという事実にあると思われます。そのため、一部のユーザーのみが問題を経験し、デバイスでのみクラッシュしていました。

原則として、ここで提案されている他の解決策のいずれも当てはまらない場合は、問題がデバイス固有であるかどうかを確認し、「原因のコンポーネント」(コンポーネント)が見つかるまで、作業中のビューを単純化しようとします。そして、追加する必要があるのは、クラッシュログに報告されなかったということです。そのため、偶然に問題を引き起こした特定のビューに遭遇しただけです!)。

申し訳ありませんが、これ以上具体的にすることはできませんが、誰かが同様の、しかし説明のつかない問題を経験した場合、これがさらなる調査のためのいくつかの指針となることを願っています。

于 2016-07-11T19:58:55.793 に答える
0

私の答えはここにあるほとんどの答えに関連していますが、私の場合、このクラッシュは、現在フォーカスされている編集テキストの行を削除したために発生しました。

したがって、私が行ったのは、アダプターのremoveメソッドをオーバーライドし、削除された行に現在のフォーカス編集が含まれているかどうかを照会し、含まれている場合はフォーカスをクリアすることだけでした。

それは私にとってそれを解決しました。

于 2014-07-11T11:41:52.227 に答える
0

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

また、TabFragmentで「removeAt(intposition)」のオーバーライドを実行して、次のように新しいクリーンアップコードを呼び出します。

// TabFragment code
@Override
public void removeAt(int position) {
    mAdapter.onItemDismiss(position);
    mAdapter.notifyItemRemoved(position); // <--- I put an extra notify here too
}
于 2016-12-18T19:40:51.863 に答える
0

ScrollViewを<android.support.v4.widget.NestedScrollViewに置き換えましたが、正常に動作するようになりました。

于 2021-03-17T13:43:14.440 に答える