ListView 内的可聚焦 EditText

Focusable EditText inside ListView(ListView 内的可聚焦 EditText)

本文介绍了ListView 内的可聚焦 EditText的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

到目前为止,我已经在这方面花费了大约 6 个小时,并且遇到的只是障碍.一般的前提是 ListView 中有一些行(无论它是由适配器生成的,还是作为标题视图添加的)包含一个 EditText 小部件和一个 按钮.我想要做的就是能够使用慢跑球/箭头,将选择器导航到像正常一样的单个项目,但是当我到达特定行时——即使我必须明确标识该行——具有焦点孩子,我希望那个孩子获得焦点,而不是用选择器指示位置.

I've spent about 6 hours on this so far, and been hitting nothing but roadblocks. The general premise is that there is some row in a ListView (whether it's generated by the adapter, or added as a header view) that contains an EditText widget and a Button. All I want to do is be able to use the jogball/arrows, to navigate the selector to individual items like normal, but when I get to a particular row -- even if I have to explicitly identify the row -- that has a focusable child, I want that child to take focus instead of indicating the position with the selector.

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

I've tried many possibilities, and have so far had no luck.

布局:

<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 会将其聚焦在该点上,但这依赖于触摸屏,这不是必需的.

Assuming there are other items in the adapter, using the arrow keys will move the selection up/down in the list, as expected; but when getting to the header row, it is also displayed with the selector, and no way to focus into the EditText using the jogball. Note: tapping on the EditText will focus it at that point, however that relies on a touchscreen, which should not be a requirement.

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

ListView apparently has two modes in this regard:
1. setItemsCanFocus(true): selector is never displayed, but the EditText can get focus when using the arrows. Focus search algorithm is hard to predict, and no visual feedback (on any rows: having focusable children or not) on which item is selected, both of which can give the user an unexpected experience.
2. setItemsCanFocus(false): selector is always drawn in non-touch-mode, and EditText can never get focus -- even if you tap on it.

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

To make matters worse, calling editTextView.requestFocus() returns true, but in fact does not give the EditText focus.

我所设想的基本上是 1 和 1 的混合体.2,如果 all 项目是否可聚焦,而不是列表设置,我想为列表中的 single 项目设置可聚焦性,以便选择器从为不可聚焦的项选择整行,并遍历包含可聚焦子项的焦点树.

What I'm envisioning is basically a hybrid of 1 & 2, where rather than the list setting if all items are focusable or not, I want to set focusability for a single item in the list, so that the selector seamlessly transitions from selecting the entire row for non-focusable items, and traversing the focus tree for items that contain focusable children.

有接受者吗?

推荐答案

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

Sorry, answered my own question. It may not be the most correct or most elegant solution, but it works for me, and gives a pretty solid user experience. I looked into the code for ListView to see why the two behaviors are so different, and came across this from ListView.java:

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

所以,当调用 setItemsCanFocus(false) 时,它也设置了后代的可聚焦性,使得没有孩子可以得到焦点.这就解释了为什么我不能只在 ListView 的 OnItemSelectedListener 中切换 mItemsCanFocus - 因为 ListView 会阻止所有子级的焦点.

So, when calling setItemsCanFocus(false), it's also setting descendant focusability such that no child can get focus. This explains why I couldn't just toggle mItemsCanFocus in the ListView's OnItemSelectedListener -- because the ListView was then blocking focus to all children.

我现在拥有的:

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

我使用 beforeDescendants 是因为只有当 ListView 本身(不是子项)具有焦点时才会绘制选择器,因此默认行为需要是 ListView 先获得焦点并绘制选择器.

I use beforeDescendants because the selector will only be drawn when the ListView itself (not a child) has focus, so the default behavior needs to be that the ListView takes focus first and draws selectors.

然后在 OnItemSelectedListener 中,因为我知道要覆盖选择器的标题视图(需要更多的工作来动态确定任何给定位置是否包含可聚焦视图),我可以更改后代的可聚焦性,并将焦点设置在 EditText.当我离开该标题时,再次将其改回.

Then in the OnItemSelectedListener, since I know which header view I want to override the selector (would take more work to dynamically determine if any given position contains a focusable view), I can change descendant focusability, and set focus on the EditText. And when I navigate out of that header, change it back it again.

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 时,它又开始绘制选择器.

Note the commented-out setItemsCanFocus calls. With those calls, I got the correct behavior, but setItemsCanFocus(false) caused focus to jump from the EditText, to another widget outside of the ListView, back to the ListView and displayed the selector on the next selected item, and that jumping focus was distracting. Removing the ItemsCanFocus change, and just toggling descendant focusability got me the desired behavior. All items draw the selector as normal, but when getting to the row with the EditText, it focused on the text field instead. Then when continuing out of that EditText, it started drawing the selector again.

这篇关于ListView 内的可聚焦 EditText的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本文标题为:ListView 内的可聚焦 EditText

基础教程推荐