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在这方面显然有两种模式:
1 setItemsCanFocus(true).:从不显示选择器,但是EditText使用箭头时可以获取焦点。焦点搜索算法很难预测,并且没有视觉反馈(在任何行上:是否有可聚焦的子对象)都没有选择哪个项目,这两者都可以为用户带来意想不到的体验。
2 setItemsCanFocus(false).:选择器始终以非触摸模式绘制,EditText即使单击它也永远不会获得焦点。

更糟的是,调用editTextView.requestFocus()返回true,但实际上并没有给予EditText焦点。

我所设想的基本上是1和2的混合形式,而不是列表设置(如果所有项目都可聚焦),我想为列表中的单个项目设置聚焦性,以便选择器从选择整个行用于非焦点项目,并遍历焦点树以查找包含可焦点子项的项目。

有参加者吗?

Answers:


101

对不起,回答了我自己的问题。它可能不是最正确或最优雅的解决方案,但它对我有用,并提供了非常可靠的用户体验。我查看了ListView的代码,以了解两个行为为何如此不同的原因,并从ListView.java中发现了这一点:

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

因此,在调用时setItemsCanFocus(false),还会设置后代的可聚焦性,以使任何孩子都无法获得聚焦。这就解释了为什么我不能只切换mItemsCanFocusListView的OnItemSelectedListener,因为ListView当时将焦点限制在所有子项上。

我现在所拥有的:

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

我使用beforeDescendants是因为选择器仅在ListView本身(而不是子级)具有焦点时才会绘制,因此默认行为是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时,它再次开始绘制选择器。


非常酷,还没有测试。您是否在1.5、1.6和3.0上进行了测试?
拉斐尔·桑切斯

拉斐尔·桑切斯(Rafael Sanches):我从2.1开始就没有接触过该项目,但当时确认可以在1.5、1.6和2.1中工作。我不保证它仍然可以在2.2或更高版本中使用。

13
只需要android:descendantFocusability =“ afterDescendants”-无论如何+1
kellogs 2011年

5
@kellogs:是的,descendantFocusability="afterDescendants"将允许您的EditText集中在ListView内,但是使用dpad 导航时,您将没有列表项选择器。我的任务是在除具有EditText的行之外的所有行上都具有列表项选择器。很高兴它有所帮助。FWIW,我们最终重新评估了此实现,并确定ListView内的可聚焦对象不是惯用的Android UI设计,因此我们放弃了这一想法,转而采用对Android更友好的方法。

5
此冰淇淋三明治是否经过测试?我无法正常工作。谢谢。
Rajat Anantharam 2013年

99

这对我有帮助。
在您的清单中:

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

3
我不确定我是否知道相关性。设置windowSoftInputMode只是更改了IME在打开时调整其余窗口内容的方式。它不允许您有选择地更改ListView的焦点类型。您能否再解释一下,这与初始用例有何关系?

1
@Joe打开IME时,光标-可能还有焦点-都在屏幕上跳来跳去,无法输入文本。你OnItemSelectedListener没有改变。但是,Iogan的简单解决方案就像一个魅力,谢谢!
古柏(Gubbel)

2
@Gubbel:的确,这根本不会改变,因为最初的问题是关于完全不同的问题:) Glad logan的修复程序可以满足您的需求,但它根本与问题无关。

8
使用此加号,Joe的android:descendantFocusability属性使我的EditTexts可以ListView正确地解决键盘问题,并且对两者都表示赞同。 android:descendantFocusability本身并不能解决问题,而且我对要处理的@Overriding onItemSelected所有14 EditText秒钟的工作都不是很热衷。:) 谢谢!
汤姆森·科默

这对我有用,但是请注意,如果您使用的是TabHost或TabActivity,则必须在清单中为TabActivity定义设置android:windowSoftInputMode =“ adjustPan”。
kiduxa

18

我的任务是实现ListView单击时扩展。额外的空格显示EditText您可以在其中输入一些文本的位置。应用程式必须在2.2以上版本上运作(撰写本文时,最高为4.2.2)

我尝试了这篇文章和其他我能找到的解决方案。在2.2到4.2.2的设备上对其进行了测试。在所有2.2+设备上,没有一个解决方案令人满意,每个解决方案都存在不同的问题。

我想分享我的最终解决方案:

  1. 将listview设置为 android:descendantFocusability="afterDescendants"
  2. 将listview设置为 setItemsCanFocus(true);
  3. 将您的活动设置为android:windowSoftInputMode="adjustResize" 许多人建议,adjustPan但可以adjustResize提供更好的用户体验,只需在您的情况下进行测试即可。有了它,adjustPan您将得到例如被遮盖的底部列表项。文件建议(“通常不如调整大小理想”)。同样在用户开始在软键盘上键入后的4.0.4上,屏幕将平移到顶部。
  4. 在4.2.2上adjustResize,EditText焦点存在一些问题。解决方案是从该线程应用rjrjr解决方案。它看起来很吓人,但事实并非如此。而且有效。去尝试一下。

附加5。由于在EditText关注HoneyComb之前的版本时适配器被刷新(由于视图调整大小),我发现反向视图存在问题: 在2.2上获取ListView项的视图/反向顺序;适用于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
就我而言,仅第一步和第二步就足够了
Kalpesh Lakhani,2015年

与我的ArrayAdapter中的xElement.setOnClickListener(..)和ListView.setOnItemClickListener(...)完美配合-终于!大哥
Javatar

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;}!
克里斯·范·德Steeg的

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

如果列表是动态的并且包含可聚焦的小部件,那么正确的选择是使用RecyclerView而不是ListView IMO。

设置adjustPanFOCUS_AFTER_DESCENDANTS或手动记住焦点位置的解决方法实际上只是解决方法。他们有极端的情况(滚动+软键盘问题,插入符号更改EditText中的位置)。他们不改变的ListView创建/销毁意见的事实集体notifyDataSetChanged

使用RecyclerView,您可以通知有关单个插入,更新和删除的信息。不会重新创建焦点视图,因此不会出现表单控件失去焦点的问题。作为额外的奖励,RecyclerView可以对列表项的插入和删除进行动画处理。

以下是官方文档中有关如何开始使用的示例RecyclerView开发人员指南-使用RecyclerView创建列表


1

有时android:windowSoftInputMode="stateAlwaysHidden"在清单活动或xml中使用时,它将失去键盘焦点。因此,首先在xml和清单中检查该属性,如果存在,则将其删除。在将这些选项添加到辅助活动中的清单文件android:windowSoftInputMode="adjustPan"并将此属性添加到xml中的listview之后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
问题与焦点有关,而不是可点击性。

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

我刚刚找到了另一个解决方案。我认为这不是解决方案,而是更多技巧,但是它可以在android 2.3.7和android 4.3上运行(我什至已经测试了那个好的旧式D-pad)

像往常一样初始化Webview并添加以下内容:(感谢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();
            }
     });

view.requestFocus中的视图是什么?
Narendra Singh

这是一个旧答案,但它引用了我认为是OnFocusChangeListener的自变量给出的范围内的视图
alaeri 2015年

1
它导致焦点改变的循环。
Morteza Rastgoo '16

0

试试这个

android:windowSoftInputMode="adjustNothing"

在里面

活动

清单的一部分。是的,它没有任何调整,这意味着当IME打开时,editText将保留在原处。但这只是一点不便,仍然可以完全解决失去焦点的问题。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.