ListView 中的可聚焦编辑文本

到目前为止,我已经在这上面花了6个小时,除了路障什么也没碰到。一般前提是,ListView中有一些行(无论是由适配器生成的,还是作为头视图添加的)包含一个 EditText小部件和一个 Button。我所要做的就是能够使用 jogball/Arrow,像平常一样导航选择器到单独的项目,但是当我到达一个特定的行——即使我必须明确地标识该行——有一个可定焦的子行时,我希望这个子行能够获得焦点,而不是用选择器指示位置。

我试过很多种可能性,但到目前为止都没有结果。

布局:

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

假设适配器中还有其他条目,使用箭头键将按照预期将选择项向上/向下移动到列表中; 但是当到达头行时,它也会显示在选择器中,无法使用 jogball 将焦点集中到 EditText中。注意: 点击 EditText 威尔在这一点上的焦点,但这依赖于一个触摸屏,这不应该是一个要求。

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

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

我所设想的基本上是1和2的混合体,在这里我不想设置 所有条目是否可聚焦,而是想为列表中的 单身条目设置可聚焦性,这样选择器就可以无缝地从选择不可聚焦条目的整行过渡到选择包含可聚焦子条目的聚焦树。

Any takers?

110616 次浏览

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

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

因此,当调用 setItemsCanFocus(false)时,它也设置了子代可聚焦性,使得没有孩子能够获得焦点。这就解释了为什么我不能在 ListView 的 OnItemSelectedListener 中切换 mItemsCanFocus——因为 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 中出来时,它又开始绘制选择器。

这篇文章完全符合我的关键词。我有一个 ListView 头,搜索 EditText 和搜索按钮。

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

浪费了很多时间,这不是真正的补救。希望它能帮助某个坚强的人。

这对我有帮助。
在你的货单上:

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

另一个简单的解决方案是在 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;

这样,你的行可以点击,你的内部视图也可以点击:)

最重要的部分是让 集中注意力为列表单元格工作。 特别是对于谷歌电视上的列表来说,这是必不可少的:

列表视图的 setItemsCanFocus 方法起到了这样的作用:

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

我的 list cell 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 导航也很重要。

要了解更多细节,请查看其他答案。

我们正在尝试这个短名单,不做任何视图回收。到目前为止一切顺利。

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

我的任务是实现 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提供了更好的 ux imho,在您的案例中测试一下就可以了。例如,使用 adjustPan,您将得到模糊的底部列表。医生建议(“这通常不如调整大小可取”)。同样在4.0.4上,当用户开始在软键盘上输入时,屏幕会移动到顶部。
  4. 在4.2.2上使用 adjustResize时,EditText 焦点有一些问题。解决方案是从这个线程应用 rjrjr 解决方案。它看起来可怕,但它不是。而且成功了。试试吧。

Additional 5. Due to adapter being refreshed (because of view resize) when EditText gains focus on pre HoneyComb versions I found an issue with reversed views: 在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。 希望它能节省人们一些时间,因为我花了至少几个小时才得出这个结论。

我刚找到了另一个解决办法。我相信这更像是一种黑客技术,而不是解决方案,但它适用于 android 2.3.7和 android 4.3(我甚至测试过那个好用的老 D-pad)

像往常一样初始化你的 webview,并添加以下内容: (感谢迈克尔 · 比尔曼)

listView.setItemsCanFocus(true);

在 getView 调用期间:

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

这救了我的命—— >

  1. 设置这条线

    ListView.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);

  2. 然后在您的活动清单标记中键入以下内容—— >

    <activity android:windowSoftInputMode="adjustPan">

你一贯的目的

有时当您使用 android:windowSoftInputMode="stateAlwaysHidden"in 清单活动或 xml 时,它将失去键盘焦点。因此,首先检查 xml 和清单中是否存在该属性,如果存在,则删除它。在将这些选项添加到副活动 android:windowSoftInputMode="adjustPan"中的清单文件,并将此属性添加到 xml android:descendantFocusability="beforeDescendants"中的 listview 中

如果列表是动态的并且包含可定焦的小部件,那么正确的选择是使用 回收视图而不是 ListViewIMO。

设置 adjustPanFOCUS_AFTER_DESCENDANTS或手动记住焦点位置的变通方法实际上只是变通方法。它们有边框(滚动 + 软键盘问题,在 EditText 中插入符号更改位置)。它们不会改变 ListView 在 notifyDataSetChanged期间创建/销毁视图 大家一起的事实。

With RecyclerView, you notify about individual inserts, updates, and deletes. The focused view is not being recreated so no issues with form controls losing focus. As an added bonus, RecyclerView animates the list item insertions and removals.

下面是官方文档中关于如何开始使用 RecyclerView: 开发人员指南-使用回收视图创建列表的一个例子

试试这个

android:windowSoftInputMode="adjustNothing"

活动

清单的一部分。 是的,它不会调整任何东西,这意味着当 IME 打开时,edit Text 将停留在原来的位置。但这只是一个小小的不便,仍然完全解决了失去注意力的问题。

在我的示例中,列表视图中有14个输入编辑文本。我所面临的问题是,当键盘打开时,编辑文本焦点丢失,滚动布局,并且一旦焦点视图不可见,用户键盘就向下。它不利于用户体验。我不能使用 windowSoftInputMethod = “调整盘”。因此,经过这么多的搜索,我找到了一个 链接,它通过使用 LinearLayout 和 scrollView 来充实自定义布局,并将数据设置为视图适配器,这对我的案例很有帮助。