web-dev-qa-db-ja.com

ListView内のフォーカス可能なEditText

これまでに約6時間を費やしましたが、障害物以外は何もヒットしていません。一般的な前提は、ListViewウィジェットとEditTextを含むButtonに(アダプターによって生成されるか、ヘッダービューとして追加されるかにかかわらず)行があることです。私がやりたいのは、ジョグボール/矢印を使用して、セレクタを通常のように個々の項目にナビゲートすることですが、特定の行に到達すると、行を明示的に識別する必要がある場合でも、フォーカス可能です子、セレクタで位置を示すのではなく、その子にフォーカスを当てたい。

私は多くの可能性を試しましたが、これまでのところ運がありませんでした。

レイアウト:

<ListView
    Android:id="@Android:id/list" 
    Android:layout_height="fill_parent" 
    Android:layout_width="fill_parent"
    />

ヘッダービュー:

EditText view = new EditText(this);
listView.addHeaderView(view, null, true);

アダプターに他のアイテムがあると仮定すると、矢印キーを使用すると、リスト内で選択が上下に移動します。ただし、ヘッダー行に到達すると、セレクターとともに表示され、ジョグボールを使用してEditTextにフォーカスする方法はありません。注:EditTextをタップするとwillその時点でフォーカスされますが、これは必須ではないタッチスクリーンに依存しています。

ListViewには、明らかに次の2つのモードがあります。
1。 setItemsCanFocus(true):セレクターは表示されませんが、EditTextは矢印を使用するとフォーカスを取得できます。フォーカス検索アルゴリズムは予測が難しく、選択された項目に関する視覚的なフィードバック(行にフォーカスできない子があるかどうか)はありません。どちらもユーザーに予期しないエクスペリエンスを提供します。
2。 setItemsCanFocus(false):セレクターは常に非タッチモードで描画され、EditTextはタップしてもフォーカスを取得できません。

さらに悪いことに、editTextView.requestFocus()を呼び出すとtrueが返されますが、実際にはEditTextフォーカスは与えられません。

私が想定しているのは基本的に1と2のハイブリッドで、そこではリスト設定ではなくallアイテムがフォーカス可能であるかどうかではなく、singleにフォーカス可能性を設定したいリスト内のアイテム。これにより、セレクターは、フォーカス不可能なアイテムの行全体の選択から、フォーカス可能な子を含むアイテムのフォーカスツリーの走査までシームレスに移行します。

受験者はいますか?

120
Joe

申し訳ありませんが、私自身の質問に答えました。それは最も正確でエレガントなソリューションではないかもしれませんが、私にとってはうまく機能し、かなり堅実なユーザーエクスペリエンスを提供します。 ListViewのコードを調べて、2つの動作が非常に異なる理由を確認し、ListView.Javaからこれを見つけました。

    public void setItemsCanFocus(boolean itemsCanFocus) {
        mItemsCanFocus = itemsCanFocus;
        if (!itemsCanFocus) {
            setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
        }
    }

したがって、setItemsCanFocus(false)を呼び出すときは、子がフォーカスを取得できないように子孫のフォーカス可能性も設定します。これは、ListViewがすべての子へのフォーカスをブロックしていたため、ListViewのOnItemSelectedListenerでmItemsCanFocusを切り替えることができなかった理由を説明しています。

私が今持っているもの:

<ListView
    Android:id="@Android:id/list" 
    Android:layout_height="match_parent" 
    Android:layout_width="match_parent"
    Android:descendantFocusability="beforeDescendants"
    />

セレクタはListView自体(子ではない)にフォーカスがある場合にのみ描画されるため、beforeDescendantsを使用します。したがって、デフォルトの動作では、ListViewが最初にフォーカスを取得してセレクタを描画する必要があります。

次に、OnItemSelectedListenerで、セレクターをオーバーライドするヘッダービューがわかっているため(特定の位置にフォーカス可能なビューが含まれているかどうかを動的に判断するにはさらに作業が必要です)、子孫のフォーカス可能性を変更し、EditTextにフォーカスを設定できます。そして、そのヘッダーから移動したら、元に戻します。

public void onItemSelected(AdapterView<?> listView, View view, int position, long id)
{
    if (position == 1)
    {
        // listView.setItemsCanFocus(true);

        // Use afterDescendants, because I don't want the ListView to steal focus
        listView.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
        myEditText.requestFocus();
    }
    else
    {
        if (!listView.isFocused())
        {
            // listView.setItemsCanFocus(false);

            // Use beforeDescendants so that the EditText doesn't re-take focus
            listView.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
            listView.requestFocus();
        }
    }
}

public void onNothingSelected(AdapterView<?> listView)
{
    // This happens when you start scrolling, so we need to prevent it from staying
    // in the afterDescendants mode if the EditText was focused 
    listView.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
}

コメントアウトされたsetItemsCanFocus呼び出しに注意してください。これらの呼び出しで正しい動作が得られましたが、setItemsCanFocus(false)により、フォーカスがEditTextからListViewの外部の別のウィジェットにジャンプし、ListViewに戻り、次の選択されたアイテムのセレクターが表示され、そのフォーカスがジャンプします気を散らしていました。 ItemsCanFocusの変更を削除し、子孫のフォーカス可能性を切り替えるだけで、望ましい動作が得られました。すべてのアイテムは通常どおりセレクターを描画しますが、EditTextで行に到達すると、代わりにテキストフィールドにフォーカスします。その後、そのEditTextを続行すると、セレクターの描画が再開されます。

101
Joe

これは私を助けました。
マニフェスト内:

<activity Android:name= ".yourActivity" Android:windowSoftInputMode="adjustPan"/>
97
logan

私のタスクは、クリックすると展開されるListViewを実装することでした。追加のスペースにはEditTextが表示され、ここにテキストを入力できます。アプリは2.2以降で機能する必要があります(これを書いている時点では4.2.2まで)

私はこの投稿や他の見つけられる他の投稿から多くの解決策を試しました。 2.2から4.2.2までのデバイスでテストしました。 2.2以上のすべてのデバイスで満足できるソリューションはなく、各ソリューションにはさまざまな問題がありました。

最終的な解決策を共有したい:

  1. リストビューをAndroid:descendantFocusability="afterDescendants"に設定します
  2. リストビューをsetItemsCanFocus(true);に設定します
  3. アクティビティをAndroid:windowSoftInputMode="adjustResize"に設定します多くの人がadjustPanを提案しますが、adjustResizeのほうがはるかに優れたux imhoを提供します。 adjustPanを使用すると、たとえば下のリスト項目が見えなくなります。文書によると(「これは通常、サイズ変更よりも望ましくありません」)。また、ユーザーがソフトキーボードで入力を開始した後の4.0.4では、画面が上にパンします。
  4. 4.2.2でadjustResizeを使用すると、EditTextフォーカスにいくつかの問題があります。解決策は、このスレッドからrjrjrソリューションを適用することです。怖いですが、そうではありません。そしてそれは動作します。やってみなよ。

追加5。EditTextがHoneyCombの以前のバージョンに焦点を合わせたときにアダプターが更新されるため(ビューのサイズ変更のため)、逆ビューで問題が見つかりました: リストビューアイテムのビューを取得/ 2.2の逆順。 4.0.3で動作します

いくつかのアニメーションを実行している場合、サイズ変更が実行されず、アダプターがビューを更新しないように、ハニカムバージョン以前の動作をadjustPanに変更することができます。このようなものを追加するだけです

if(Android.os.Build.VERSION.SDK_INT < Android.os.Build.VERSION_CODES.HONEYCOMB)
        getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);

これはすべて、2.2〜4.2.2デバイスで許容可能なUXを提供します。この結論に至るまでに少なくとも数時間かかったので、時間を節約できることを願っています。

17
AndroidGecko

これは私の命を救った--->

  1. この行を設定

    ListView.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);

  2. 次に、アクティビティタグのマニフェストにthis->と入力します

    <activity Android:windowSoftInputMode="adjustPan">

あなたのいつもの意図

10
ravi ranjan

ビューのリサイクルを行わない短いリストでこれを試しています。ここまでは順調ですね。

XML:

<RitalinLayout
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    >
  <ListView
      Android:id="@+id/cart_list"
      Android:layout_width="match_parent"
      Android:layout_height="match_parent"
      Android:scrollbarStyle="outsideOverlay"
      />
</RitalinLayout>

Java:

/**
 * It helps you keep focused.
 *
 * For use as a parent of {@link Android.widget.ListView}s that need to use EditText
 * children for inline editing.
 */
public class RitalinLayout extends FrameLayout {
  View sticky;

  public RitalinLayout(Context context, AttributeSet attrs) {
    super(context, attrs);

    ViewTreeObserver vto = getViewTreeObserver();

    vto.addOnGlobalFocusChangeListener(new ViewTreeObserver.OnGlobalFocusChangeListener() {
      @Override public void onGlobalFocusChanged(View oldFocus, View newFocus) {
        if (newFocus == null) return;

        View baby = getChildAt(0);

        if (newFocus != baby) {
          ViewParent parent = newFocus.getParent();
          while (parent != null && parent != parent.getParent()) {
            if (parent == baby) {
              sticky = newFocus;
              break;
            }
            parent = parent.getParent();
          }
        }
      }
    });

    vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
      @Override public void onGlobalLayout() {
        if (sticky != null) {
          sticky.requestFocus();
        }
      }
    });
  }
}
7
rjrjr

この投稿は私のキーワードと完全に一致していました。検索EditTextと検索ボタンを持つListViewヘッダーがあります。

最初のフォーカスを失った後にEditTextにフォーカスを与えるために私が見つけた唯一のHACKは次のとおりです。

    searchText.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View arg0) {
            // LOTS OF HACKS TO MAKE THIS WORK.. UFF...
            searchButton.requestFocusFromTouch();
            searchText.requestFocus();
        }
    });

多くの時間を失い、それは本当の修正ではありません。それが誰かの助けになることを願っています。

4
Rafael Sanches

リストが動的でフォーカス可能なウィジェットを含む場合、正しいオプションはListView IMOの代わりにRecyclerViewを使用することです。

adjustPanFOCUS_AFTER_DESCENDANTSを設定する回避策、またはフォーカスされた位置を手動で記憶する回避策は、まさに回避策です。コーナーケースがあります(スクロール+ソフトキーボードの問題、EditTextでのキャレットの変更)。彼らは、notifyDataSetChanged中にListViewがビューを作成/破棄するという事実を変更しませんen masse

RecyclerViewでは、個々の挿入、更新、削除について通知します。フォーカスされたビューは再作成されていないため、フォームコントロールがフォーカスを失う問題はありません。追加のボーナスとして、RecyclerViewはリストアイテムの挿入と削除をアニメーション化します。

以下は、RecyclerViewの使用を開始する方法に関する公式ドキュメントの例です。 開発者ガイド-RecyclerViewでリストを作成する

2
Pēteris Caune

Android:windowSoftInputMode="stateAlwaysHidden"inマニフェストアクティビティまたはxmlを使用すると、キーボードフォーカスが失われる場合があります。そのため、最初にxmlおよびマニフェストでそのプロパティを確認します(存在する場合は削除します)。サイドアクティビティAndroid:windowSoftInputMode="adjustPan"のマニフェストファイルにこれらのオプションを追加し、xmlのリストビューにこのプロパティを追加した後Android:descendantFocusability="beforeDescendants"

1
user4715375

別の解決策を見つけました。私はそれが解決策というよりもハッキングであると信じていますが、それはAndroid 2.3.7とAndroid 4.3で動作します(私はその古き良きD-padさえテストしました)

通常どおりウェブビューを初期化し、これを追加します:(Michael Biermanに感謝)

listView.setItemsCanFocus(true);

GetView呼び出し中:

editText.setOnFocusChangeListener(
    new OnFocusChangeListener(View view,boolean hasFocus){
        view.post(new Runnable() {
            @Override
            public void run() {
                view.requestFocus();
                view.requestFocusFromTouch();
            }
     });
0
alaeri

これを試してみてください

Android:windowSoftInputMode="adjustNothing"

の中に

アクティビティ

マニフェストのセクション。はい、何も調整しません。つまり、IMEが開いているとき、editTextは元の場所にとどまります。しかし、それは焦点を失うという問題をまだ完全に解決する少し不便です。

0
Tor_Gash

最も重要な部分は、リストセルでfocusを機能させることです。特にGoogle TVのリストでは、これが不可欠です。

setItemsCanFocusリストビューのメソッドはトリックを行います:

...
mPuzzleList = (ListView) mGameprogressView.findViewById(R.id.gameprogress_puzzlelist);
mPuzzleList.setItemsCanFocus(true);
mPuzzleList.setAdapter(new PuzzleListAdapter(ctx,PuzzleGenerator.getPuzzles(ctx, getResources(), version_lite)));
...

リストセルxmlは次のように始まります。

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
             Android:id="@+id/puzzleDetailFrame"
             Android:focusable="true"
             Android:nextFocusLeft="@+id/gameprogress_lessDetails"
             Android:nextFocusRight="@+id/gameprogress_reset"
...

nextFocusLeft/RightもD-Padナビゲーションにとって重要です。

詳細については、他のすばらしい回答をご覧ください。

0

もう1つの簡単な解決策は、ListAdapterのgetView(..)メソッドでonClickListenerを定義することです。

public View getView(final int position, View convertView, ViewGroup parent){
    //initialise your view
    ...
    View row = context.getLayoutInflater().inflate(R.layout.list_item, null);
    ...

    //define your listener on inner items

    //define your global listener
    row.setOnClickListener(new OnClickListener(){
        public void onClick(View v) {
            doSomethingWithViewAndPosition(v,position);
        }
    });

    return row;

そうすれば、行がクリック可能になり、内部ビューも:)

0
Weber Antoine