ListView内的Focusable EditText

目前为止,我已经花了大约6个小时的时间,而且一直没有遇到障碍。 一般的前提是ListView有一行(无论是由适配器生成,还是作为标题视图添加),其中包含一个EditText小部件和一个Button 。 我所要做的就是能够使用jogball /箭头来将select器导航到正常的单个项目,但是当我到达特定的行时(即使我必须明确标识行)孩子,我希望那个孩子不要用select器来指示位置,而是集中注意力。

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

布局:

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

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

ListView显然在这方面有两种模式:
1. setItemsCanFocus(true) :从不显示select器,但是使用箭头时EditText可以获得焦点。 焦点searchalgorithm很难预测,并且没有视觉反馈(在任何行上:有或没有可聚焦的孩子)select哪个项目,这两者都可以给用户一个意想不到的体验。
2. setItemsCanFocus(false) :select器总是以非触摸模式绘制,即使点击它, EditText也不会获得焦点。

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

我所设想的基本上是1和2的混合,而不是列表设置,如果所有的项目都是可以聚焦的,我想为列表中的单个项目设置可聚焦性,以便select器无缝地从select整行用于非可聚焦项目,遍历包含可聚焦子项目的聚焦树。

任何接受者?

对不起,回答我自己的问题。 这可能不是最正确或最优雅的解决scheme,但它对我来说很有用,并给予了非常可靠的用户体验。 我查看了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是因为select器只会在ListView本身(而不是子对象)具有焦点时绘制,所以默认行为需要ListView首先获取焦点并绘制select器。

然后在OnItemSelectedListener中,因为我知道哪个头部视图要重写select器(需要更多的工作来dynamic确定任何给定的位置是否包含可聚焦的视图),我可以改变后代的可聚焦性,并将焦点设置在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并在下一个选定项目上显示select器,而跳跃焦点是分心。 删除ItemsCanFocus改变,只是切换后代的可聚焦性让我想要的行为。 所有的项目都是按照正常的方式绘制select器,但是当使用EditText到达该行时,它会将焦点放在文本字段上。 然后,当继续EditText,它开始再次绘制select器。

这帮助了我。
在你的清单中:

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

我的任务是实现ListView ,单击时展开。 额外的空间显示EditText ,您可以在其中input一些文本。 应用程序应该在2.2以上的function(在写本文时为4.2.2)

我尝试了这个post和其他我能find的许多解决scheme; 在2.2至4.2.2的设备上testing它们。 没有任何解决scheme在所有设备2.2+上都满意,每个解决scheme都带有不同的问题。

我想分享我的最终解决scheme:

  1. 设置列表视图为android:descendantFocusability="afterDescendants"
  2. 将listview设置为setItemsCanFocus(true);
  3. 设置你的活动到android:windowSoftInputMode="adjustResize"许多人build议adjustPanadjustResize提供更好的adjustResize ,只是在你的情况下testing。 用adjustPan你会得到底部列表项目遮蔽例如。 文档build议(“这通常不如resize)”。 在用户开始在软键盘上打字之后,在4.0.4上,屏幕平移到顶部。
  4. 在4.2.2用adjustResize有一些EditText的焦点问题。 解决的办法是从这个线程应用rjrjr解决scheme。 它看起来很尴尬,但事实并非如此。 它工作。 去尝试一下。

附加5.由于适配器被刷新(因为视图resize),当EditText获得前HoneyComb版本的焦点时,我发现一个问题与颠倒的看法: 获取视图的ListView项目/相反顺序2.2; 适用于4.0.3

如果您正在制作一些animation,则可能需要将行为更改为针对预蜂巢版本的adjustPan ,以便resize不会触发,并且适配器不会刷新视图。 你只需要添加这样的东西

 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。 希望它能省一些时间,因为我花了至less几个小时才能得出这个结论。

这救了我的生命—>

  1. 设置这一行

    ListView.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);

  2. 然后在活动标签的清单中inputthis – >

    <activity android:windowSoftInputMode="adjustPan">

你平常的意图

我们正在尝试这个短列表,不做任何视图回收。 到现在为止还挺好。

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标题searchEditText和searchbutton。

为了给失去初始焦点后的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(); } }); 

失去了很多小时,这不是一个真正的修复。 希望它能帮助别人强硬。

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

设置adjustPanFOCUS_AFTER_DESCENDANTS或手动记住焦点位置的解决方法实际上只是解决方法。 他们有angular落的情况下(滚动+软键盘问题,插入改变EditText位置)。 它们不会改变ListView在notifyDataSetChanged期间创build/销毁视图的notifyDataSetChanged

通过RecyclerView,您可以通知有关个别插入,更新和删除。 重点的观点不被重新创build,所以没有问题与表单控件失去焦点。 作为额外的好处,RecyclerViewanimation列表项插入和清除。

以下是关于如何开始使用RecyclerView的官方文档的示例: http : //developer.android.com/training/material/lists-cards.html

有些时候,在清单活动或xml中使用android:windowSoftInputMode="stateAlwaysHidden"时,会失去键盘焦点。 所以首先检查你的XML和清单中的属性,如果它在那里只是删除它。 添加这些选项来清单文件中的侧面活动android:windowSoftInputMode="adjustPan" ,并将此属性添加到xml中的列表视图android:descendantFocusability="beforeDescendants"

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

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

最重要的部分是让焦点为列表单元格工作。 特别是对于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导航也很重要。

有关更多详细信息,请查看其他重要答案。

我刚刚find另一个解决scheme 我相信这是一个比解决scheme更多的黑客攻击,但它适用于Android 2.3.7和Android 4.3(我甚至testing过这个老式的D-pad)

像往常一样初始化你的webview并join:(谢谢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(); } }); 

试试这个

 android:windowSoftInputMode="adjustNothing" 

在里面

活动

你的舱单部分。 是的,它会调整nothings,这意味着editText将保留在IME打开时的位置。 但这只是一点点不便,还是完全解决了失焦的问题。