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


121

私はこれまでにこれに約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 するその時点でフォーカスされますが、これはタッチスクリーンに依存しているため、必須ではありません。

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

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

私は、ビジョン化は基本的に1&2のハイブリッドではなく、リストの設定場合ですよ何すべての項目がフォーカス可能かではありません、私はセットフォーカス特性にしたい単一、リスト内の項目ように選択からセレクタをシームレスに移行することフォーカスできない項目の行全体、およびフォーカス可能な子を含む項目のフォーカスツリーをたどります。

テイカーはいますか?

回答:


101

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

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

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

私が今持っているもの:

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

私が使用してbeforeDescendantsデフォルトの動作は、リストビューが最初にフォーカスを受け取り、セレクタを描くことにする必要があるので、リストビュー自体(ない子供が)、フォーカスがあるとき、セレクタにのみ描画されますので。

次に、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を続行すると、セレクターの描画が再開されました。


とてもクールで、まだテストされていません。1.5、1.6、3.0でテストしましたか?
Rafael Sanches、

Rafael Sanches:2.1以降、このプロジェクトには触れていませんが、その時点で、1.5、1.6、および2.1で動作することが確認されています。2.2以降でも動作することを保証しません。
ジョー

13
android:descendantFocusability = "afterDescendants"のみ必要-とにかく+1
ケルログ

5
@kellogs:そうです、descendantFocusability="afterDescendants"EditTextがListView内でフォーカスを取得できるようにしますが、dpadでナビゲートしている間リストアイテムセレクターを取得できません。私の仕事は、EditTextを含む行を除くすべての行にリストアイテムセレクターを配置することでした。それが助けてくれてうれしいです。FWIW、私たちはこの実装を再評価することになり、ListView内のfocusableは単なる慣用​​的なAndroid UIデザインではないと判断したため、よりAndroidにやさしいアプローチを優先してアイデアを破棄しました。
Joe、

5
これはアイスクリームサンドイッチでテストされましたか?動作しません。ありがとう。
Rajat Anantharam 2013年

99

これは私を助けました。
あなたのマニフェストで:

<activity android:name= ".yourActivity" android:windowSoftInputMode="adjustPan"/>

3
関連性があるかわかりません。windowSoftInputModeを設定すると、ウィンドウが開いているときにIMEがウィンドウの残りのコンテンツを調整する方法が変更されます。ListViewのフォーカスタイプを選択的に変更することはできません。もう少し詳しく説明してもらえますか、これが最初のユースケースとどのように関係しているのですか?
ジョー

1
@Joe IMEを開くと、カーソル(おそらくフォーカスも)が画面内を移動するだけで、テキストを入力できなくなります。あなたOnItemSelectedListenerはそれを変えません。しかし、アイオガンのシンプルなソリューションは魅力のように機能します、ありがとう!
Gubbel、

2
@Gubbel:確かに、元の質問はまったく異なるものに関するものだったので、それはまったく変わりません:)嬉しいローガンの修正はあなたが探していたものに対して機能しますが、それは単にリモートで質問に関連していません。
ジョー

8
これに加えてJoeのandroid:descendantFocusabilityプロパティを使用すると、キーボードが適切に解決されるように私EditTextのs が取得ListViewされ、両方が賛成されました。 android:descendantFocusabilityそれだけではうまくいかず、対処しなければならない@Overriding onItemSelectedすべての14 EditText秒間、私はそれほど熱狂的ではありませんでした。:)ありがとう!
Thomson Comer

これは私にとってはうまくいきましたが、TabHostまたはTabActivityを使用している場合は、マニフェストのTabActivity定義にandroid:windowSoftInputMode =” adjustPan”を設定する必要があることに注意してください。
kiduxa 2013年

18

私の仕事は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" 多くの人が提案するようにあなたの活動を設定しますadjustPanadjustResize、より良いux imhoを与えます、あなたのケースでこれをテストしてください。ではadjustPan、あなたは下のリスト項目は、例えば隠されます。ドキュメントはそれを示唆しています(「これは一般的にサイズ変更より望ましくありません」)。また、4.0.4では、ユーザーがソフトキーボードで入力を開始すると、画面が上に移動します。
  4. 4.2.2ではadjustResize、EditTextフォーカスにいくつかの問題があります。解決策は、このスレッドからrjrjrソリューションを適用することです。怖いように見えますが、そうではありません。そしてそれは機能します。やってみなよ。

追加5.EditText HoneyCombより前のバージョンに焦点を合わせると(ビューのサイズ変更のため)アダプターが更新されるため、ビューの反転に関する問題が見つかりました 。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が提供されます。私がこの結論に至るまでに少なくとも数時間かかったので、それが人々の時間を節約することを願っています。


1
私の場合は、第1ステップと第2ステップで十分です
。KalpeshLakhani、2015

ArrayAdapterのxElement.setOnClickListener(..)とListView.setOnItemClickListener(...)で完全に動作します-最後に!! ビッグTHX。
Javatar 2015

10

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

  1. この行を設定

    ListView.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);

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

    <activity android:windowSoftInputMode="adjustPan">

あなたのいつもの意図


7

私たちはこれを、リサイクルを考えていない短いリストで試しています。ここまでは順調ですね。

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

わずかに変更され、この溶液を、2日後に私の作品(@# !(スティッキー= null)の場合は{sticky.RequestFocus(); sticky.RequestFocusFromTouch();粘着性= NULL;}
クリス・バン・デ・シュテーク

4

この投稿は私のキーワードと完全に一致していました。検索EditTextと検索Buttonを含む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();
        }
    });

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


2

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

セットというの回避策adjustPanFOCUS_AFTER_DESCENDANTSまたは手動で焦点位置を覚えているが、確かにただの回避策です。それらにはコーナーケースがあります(スクロール+ソフトキーボードの問題、EditTextでのキャレットの変更位置)。ListViewが中に一括してビューを作成/破棄するという事実は変わりませんnotifyDataSetChanged

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

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


1

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


0

別の簡単な解決策は、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;

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


1
問題はクリックに関するものではなく、フォーカスに関するものです。
Joe

0

最も重要な部分は、フォーカスを取得することです、リストのセルにことです。特に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

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

いつものようにあなたのウェブビューを初期化し、これを追加してください:(ありがとうマイケル・ビアマン)

listView.setItemsCanFocus(true);

getView呼び出し中:

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

view.requestFocusのビューとは何ですか?
Narendra Singh

これは古い答えですが、OnFocusChangeListenerへの引数として与えられたスコープ内のビューを参照していました
alaeri

1
フォーカスの変更でループが発生します。
Morteza Rastgoo

0

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

android:windowSoftInputMode="adjustNothing"

の中に

アクティビティ

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.