121

到目前为止,我已经在这上面花了大约 6 个小时,除了路障之外什么都没碰到。一般的前提是 a 中有一些行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 的混合,而不是列表设置是否所有项目都可聚焦,我想为列表中的单个项目设置可聚焦性,以便选择器从选择无缝过渡整行用于不可聚焦的项目,并遍历包含可聚焦子项的项目的焦点树。

有接盘侠吗?

4

13 回答 13

102

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

<activity android:name= ".yourActivity" android:windowSoftInputMode="adjustPan"/>
于 2011-02-04T18:39:25.447 回答
101

对不起,回答了我自己的问题。它可能不是最正确或最优雅的解决方案,但它对我有用,并提供了非常可靠的用户体验。我查看了 ListView 的代码以了解为什么这两种行为如此不同,并从 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,是因为只有当 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 时,它再次开始绘制选择器。

于 2010-04-21T03:17:35.663 回答
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提供更好的用户体验恕我直言,只需在您的情况下进行测试。例如,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 设备上提供了可接受的用户体验。希望它能为人们节省一些时间,因为我至少花了几个小时才得出这个结论。

于 2013-04-04T18:42:16.583 回答
10

这救了我的命--->

  1. 设置这条线

    ListView.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);

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

    <activity android:windowSoftInputMode="adjustPan">

你通常的意图

于 2015-10-18T16:45:22.760 回答
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>

爪哇:

/**
 * 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();
        }
      }
    });
  }
}
于 2013-03-25T17:29:13.977 回答
4

这篇文章与我的关键字完全匹配。我有一个带有搜索 EditText 和一个搜索按钮的 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();
        }
    });

浪费了很多时间,这不是一个真正的解决办法。希望它可以帮助困难的人。

于 2010-10-21T09:13:09.583 回答
2

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

设置adjustPanFOCUS_AFTER_DESCENDANTS或手动记住焦点位置的变通方法确实只是变通方法。他们有极端情况(滚动+软键盘问题,EditText中插入符号的位置变化)。它们不会改变ListView在notifyDataSetChanged.

使用 RecyclerView,您可以通知单个插入、更新和删除。没有重新创建焦点视图,因此表单控件失去焦点没有问题。作为一个额外的好处,RecyclerView 动画列表项的插入和删除。

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

于 2016-02-03T14:09:33.680 回答
1

有时当您android:windowSoftInputMode="stateAlwaysHidden"在清单活动或 xml 中使用时,它会失去键盘焦点。因此,首先在您的 xml 和清单中检查该属性,如果它在那里,只需将其删除。在将这些选项添加到辅助活动中的清单文件android:windowSoftInputMode="adjustPan"并将此属性添加到 xml 中的列表视图之后android:descendantFocusability="beforeDescendants"

于 2015-11-25T05:57:40.813 回答
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 对于方向键导航也很重要。

有关更多详细信息,请查看其他出色的答案。

于 2012-12-04T20:11:06.663 回答
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();
            }
     });
于 2013-11-22T17:08:58.017 回答
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;

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

于 2011-10-04T09:15:29.030 回答
0

试试这个

android:windowSoftInputMode="adjustNothing"

在里面

活动

清单的一部分。是的,它会调整任何内容,这意味着当 IME 打开时,editText 将保持在原来的位置。但这只是一点不便,仍然完全解决了失去焦点的问题。

于 2016-02-20T14:46:22.440 回答
0

就我而言,列表视图中有 14 个输入编辑文本。我面临的问题是,当键盘打开时,编辑文本焦点丢失,滚动布局,并且一旦焦点视图对用户键盘不可见。这对用户体验不利。我不能使用 windowSoftInputMethod="adjustPan"。因此,经过大量搜索,我找到了一个链接,该链接通过使用 LinearLayout 和 scrollView 来扩展自定义布局并将视图上的数据设置为适配器,并且适用于我的情况。

于 2020-10-12T10:46:25.173 回答