いくつかのフォーカス可能なコンポーネント(ほとんどEditText
s)を持つListViewがあります。ええ、これは厳密には推奨されていませんが、一般的には、ほとんどすべてがうまく機能していて、焦点を当てなければなりません(いくつかの調整が必要でした)。とにかく、私の問題は、指でリストをスクロールしてから突然トラックボールを使用するときに奇妙な競合状態があることですIMEキーボードが表示されているとき。 offsetRectBetweenParentAndChild()
メソッドが開始され、IllegalArgumentException
をスローする必要がある時点で、何かが範囲外に出てリサイクルされなければなりません。
問題は、この例外がtry/catchを挿入できるブロックの外にスローされることです(私の知る限り)。したがって、この質問には2つの有効な解決策があります。
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)
価値のあるもの(またはこれにつまずいた人)のために、このアクティビティのListViewアプローチを放棄しました。ランダムなクラッシュは別として、windowSoftInputMode="adjustPan"
を設定せずに他の多くのワームの缶を開くことなく、フォーカス動作を正しく取得することはほとんど不可能です。代わりに、私は「単純な」ScrollViewを選びましたが、それは非常にうまく機能しています。
申し訳ありませんが、以前の回答はこの問題を解決する最も完璧な方法ではないことがわかりました。
だから私はこれを試してください:
listViewがスクロールを開始したら、ScrollListenerをアクティビティに追加し、現在のフォーカスをクリアします。
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();
}
}
}
}
私は同じ問題に直面し、この解決策を見つけました-OnGroupCollapseListener/OnGroupExpandListener
およびOnScrollListener
for ExpandableListView
フォーカスをクリアし、強制キーボードを非表示にします。また、アクティビティの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) {}
});
OnGroupExpandListener
が必要かどうかは正確にはわかりません。役に立たないかもしれません。
ブルースの答えを少し調整して使用しました。
アクティビティにはadjustResize
の代わりにadjustpan
が必要でしたが、試してみると再びエラーが発生しました。ScrollView
を<Android.support.v4.widget.NestedScrollView
に置き換えましたが、現在は正常に動作します。これが誰かを助けることを願っています!
私は最も簡単ですが良い解決策はありません。 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();
}
}
}
私の場合、2つのレイヤービューがあり、上のレイヤーはlistView、下のレイヤーはNestedScrollViewです。レイヤーを切り替えるとエラーが発生します。 ListeViewアイテム(ボタン)がフォーカスを取得します。
そのため、ボタンのフォーカスを失うことはできません。次に、最適なソリューションはNestedScrollViewを拡張することです。
私もその問題に直面し、 validcatによる解決策 はうまくいきましたが、getWindow().getCurrentFocus().clearFocus()
を呼び出さなければなりませんでした。
EditText
でRecyclerview
を使用すると、同じ問題に直面しました。多くの苦労と別のオプションを試した後、キーボードを開いたときに行を削除した後にこの問題が発生することがわかりました。キーボードを強制的に閉じ、notifyItemRemoved(position)
をnotifyDataSetChanged()
に変更することで解決しました。
展開可能なリストビューの場合、子アイテムに編集テキストがある場合、展開可能なリストビューの子孫の前にフォーカス可能性を変更する必要があります
expandableListView.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
ここで提案した解決策がどれも当てはまらない場合...
同様のエラーが発生し、ユーザーのデバイスから(クラッシュ後)原因が明確に説明されずに報告されていることに気付きました(質問に表示されるログと同じ)-より具体的には、問題はSamsungでのみ発生しましたGalaxy(S6を含む)デバイス(ただし、Nexusデバイスなどではそうではありません。そのため、最初のテストでは問題を明らかにできませんでした)。そのため、まず、問題がデバイス固有かどうかを確認する価値があります。
後で見つけたのは、Samsungの仮想キーボードがテキストフィールドに表示されているときに戻るボタンを押すと、アプリケーションがこのエラーをスローしてクラッシュすることですが、常にではありません!
実際、クラッシュの原因となったテキストフィールドは、fillViewPort = "true"を有効にしてスクロールビュー内に表示されることもありました。
私が見つけたのは、スクロールビューからfillViewPortオプションを削除しても、表示/非表示されているSamsungキーボードと競合しないことです。この問題の一部は、Samsungキーボードが通常のNexusキーボードとは異なる仮想キーボードであるためであり、ユーザーの一部のみが問題を経験し、デバイスでのみクラッシュするのはこのためです。
原則として、ここで提案されている他の解決策がどれも当てはまらない場合は、問題がデバイス固有であるかどうかを確認し、「犯人コンポーネント」(コンポーネントクラッシュログに報告されていなかったことを追加する必要があります-したがって、偶然に問題の原因となっている特定のビューにつまずいただけです!).
詳細を特定することはできませんが、同様の、しかし説明されていない問題が発生した場合は、これがさらなる調査の指針となることを願っています。
私の場合、リスト要素(ヘッダービュー)のwindowSoftInputMode="adjustPan"
、listViewおよびeditTextに関連していました。
それを修正するために、アクティビティが終了する前に非表示のソフトキーボードメソッドを呼び出します。
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);
}
}
私の答えはここのほとんどの答えに関連していますが、私の場合は、現在フォーカスがある編集テキストを含む行を削除したためにこのクラッシュが発生したことを付け加えました。
そのため、アダプターのremoveメソッドをオーバーライドし、削除された行に現在のフォーカス編集が含まれているかどうかを照会し、含まれている場合はフォーカスをクリアするだけでした。
それで解決しました。
@Bruceの回答に基づいて、recyclerviewで次のようなエラーを解決できます。
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View currentFocus = ((Activity)context).getCurrentFocus();
if (currentFocus != null) {
currentFocus.clearFocus();
}
}
私は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(int position)」のオーバーライドを実行して、次のように新しいクリーンアップコードを呼び出します。
// TabFragment code
@Override
public void removeAt(int position) {
mAdapter.onItemDismiss(position);
mAdapter.notifyItemRemoved(position); // <--- I put an extra notify here too
}