ListView中的Focusable EditText

Joe*_*Joe 120 android android-layout android-listview android-edittext

到目前为止,我已经花了大约6个小时的时间,除了路障之外什么都没打.一般的前提是a中有一些行ListView(无论是由适配器生成,还是作为标题视图添加)包含一个EditText小部件和一个Button.我想要做的就是能够使用jogball /箭头,将选择器导航到正常的单个项目,但是当我到达特定行时 - 即使我必须明确地识别行 - 它具有可聚焦孩子,我希望那个孩子采取焦点而不是用选择器指示位置.

我已经尝试了很多可能性,到目前为止还没有运气.

布局:

<ListView
    android:id="@android:id/list" 
    android:layout_height="fill_parent" 
    android:layout_width="fill_parent"
    />
Run Code Online (Sandbox Code Playgroud)

标题视图:

EditText view = new EditText(this);
listView.addHeaderView(view, null, true);
Run Code Online (Sandbox Code Playgroud)

假设适配器中还有其他项目,使用箭头键将按预期在列表中上/下移动选择; 但是当到达标题行时,它也会与选择器一起显示,并且无法专注于EditText使用jogball.注意:点击它EditText 会将焦点集中在那一点,但是它依赖于触摸屏,这不是必需的.

ListView在这方面显然有两种模式:
1 setItemsCanFocus(true).:从不显示选择器,但EditText使用箭头时可以获得焦点.焦点搜索算法难以预测,并且没有选择哪个项目的视觉反馈(在任何行上:具有可聚焦儿童或不具有可聚焦儿童),这两者都可以给用户带来意想不到的体验.
2 setItemsCanFocus(false).:选择器始终以非触摸模式绘制,并且EditText永远无法获得焦点 - 即使您点击它也是如此.

更糟糕的是,调用editTextView.requestFocus()返回true,但事实上并没有给EditText带来焦点.

我想象的基本上是1和2的混合,而不是列表设置,如果所有项目都是可聚焦的,我想为列表中的单个项目设置可聚焦性,以便选择器从选择无缝转换不可聚焦项目的整行,以及遍历包含可聚焦子项的项目的焦点树.

任何接受者?

Joe*_*Joe 102

对不起,回答了我自己的问题.它可能不是最正确或最优雅的解决方案,但它适用于我,并提供非常可靠的用户体验.我查看了ListView的代码,看看为什么这两个行为如此不同,并从ListView.java中看到了这个:

    public void setItemsCanFocus(boolean itemsCanFocus) {
        mItemsCanFocus = itemsCanFocus;
        if (!itemsCanFocus) {
            setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
        }
    }
Run Code Online (Sandbox Code Playgroud)

因此,在呼叫时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"
    />
Run Code Online (Sandbox Code Playgroud)

我使用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);
}
Run Code Online (Sandbox Code Playgroud)

请注意已注释掉的setItemsCanFocus电话.通过这些调用,我获得了正确的行为,但setItemsCanFocus(false)导致焦点从EditText跳转到ListView外的另一个小部件,返回到ListView并在下一个选定项目上显示选择器,并且跳跃焦点分散了注意力.删除ItemsCanFocus更改,只是切换后代可聚焦性让我得到了所需的行为.所有项目都正常绘制选择器,但是当使用EditText到达行时,它会聚焦于文本字段.然后当继续使用EditText时,它再次开始绘制选择器.

  • 只需要android:descendantFocusability ="afterDescendants" - 无论如何+1 (13认同)
  • @kellogs:是的,`descendantFocusability ="afterDescendants"`将允许你的EditText将焦点集中在ListView中,但是当你使用dpad导航时你得到*no*list item selector.我的任务是在除了EditText的行之外的所有行上都有列表项选择器.很高兴它有所帮助.FWIW,我们最终重新评估了这个实现,并认为ListView中的焦点不是惯用的Android UI设计,所以我们取消了这个想法,转而采用更加Android友好的方法. (5认同)
  • 这是在冰淇淋三明治上测试过的吗?我无法让它发挥作用.谢谢. (5认同)

小智 99

这对我有帮助.
在你的清单中:

<activity android:name= ".yourActivity" android:windowSoftInputMode="adjustPan"/>
Run Code Online (Sandbox Code Playgroud)

  • 使用这个加上Joe的`android:descendantFocusability`属性使我的`EditText'在一个`ListView`里面正确地解析了键盘,同时支持了两者.`android:descendantFocusability`本身并不能解决这个问题,而且对于我必须处理的所有14个`EditText`的@ Overriding``onItemSelected`我并不是很热心.:) 谢谢! (8认同)
  • 我不确定我是否看到相关性.设置windowSoftInputMode只是改变了IME在打开时调整其余窗口内容的方式.它不允许您有选择地更改ListView的焦点类型.你能解释一下,这与初始用例有什么关系? (3认同)
  • @Gubbel:确实,它根本不会改变,因为最初的问题是关于完全不同的东西:)高兴的logan修复工作适用于你所寻找的东西,但它甚至与问题无关. (2认同)

And*_*cko 17

我的任务是实现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" 很多人建议,adjustPanadjustResize给你更好的ux imho,只需在你的情况下测试.例如,adjustPan您将获得隐藏的底部列表项目.Docs建议("这通常不如调整大小").同样在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);
Run Code Online (Sandbox Code Playgroud)

所有这些都为2.2 - 4.2.2设备提供了可接受的ux.希望它能节省一些时间,因为我花了至少几个小时来得出这个结论.


小智 10

这拯救了我的生命--->

  1. 设置这一行

    ListView.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);

  2. 然后在您的活动标签清单中输入 - >

    <activity android:windowSoftInputMode="adjustPan">

你通常的意图


rjr*_*rjr 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>
Run Code Online (Sandbox Code Playgroud)

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();
        }
      }
    });
  }
}
Run Code Online (Sandbox Code Playgroud)