Android ListView 中的可聚焦 EditText
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2679948/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Focusable EditText inside ListView
提问by Joe
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.
到目前为止,我已经在这方面花费了大约 6 个小时,除了遇到障碍之外什么也没遇到。一般前提是 a 中有一些行ListView
(无论它是由适配器生成的,还是作为标题视图添加的),其中包含一个EditText
小部件和一个Button
. 我想要做的就是能够使用滚球/箭头,像平常一样将选择器导航到单个项目,但是当我到达特定行时——即使我必须明确标识该行——它具有可聚焦孩子,我希望那个孩子获得焦点而不是用选择器指示位置。
I've tried many possibilities, and have so far had no luck.
我已经尝试了很多可能性,但到目前为止还没有运气。
layout:
布局:
<ListView
android:id="@android:id/list"
android:layout_height="fill_parent"
android:layout_width="fill_parent"
/>
Header view:
标题视图:
EditText view = new EditText(this);
listView.addHeaderView(view, null, true);
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
willfocus it at that point, however that relies on a touchscreen, which should not be a requirement.
假设适配器中有其他项目,使用箭头键将按预期在列表中向上/向下移动选择;但是当到达标题行时,它也与选择器一起显示,并且无法EditText
使用jogball聚焦。注意:点击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.
ListView
显然在这方面有两种模式:
1. setItemsCanFocus(true)
:选择器从不显示,但EditText
使用箭头时可以获得焦点。焦点搜索算法很难预测,并且没有视觉反馈(在任何行上:是否有焦点孩子)选择了哪个项目,这两者都会给用户带来意想不到的体验。
2. setItemsCanFocus(false)
: 选择器总是在非触摸模式下绘制,EditText
永远无法获得焦点——即使你点击它。
To make matters worse, calling editTextView.requestFocus()
returns true, but in fact does not give the EditText focus.
更糟糕的是,调用editTextView.requestFocus()
返回 true,但实际上并没有给 EditText 焦点。
What I'm envisioning is basically a hybrid of 1 & 2, where rather than the list setting if allitems are focusable or not, I want to set focusability for a singleitem 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.
我所设想的基本上是 1 和 2 的混合,如果所有项目都可聚焦与否,我想为列表中的单个项目设置可聚焦性,而不是列表设置,以便选择器从选择不可聚焦项目的整行,并遍历包含可聚焦子项的项目的焦点树。
Any takers?
有接班人吗?
采纳答案by Joe
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:
对不起,回答了我自己的问题。它可能不是最正确或最优雅的解决方案,但它对我有用,并提供了非常可靠的用户体验。我查看了 ListView 的代码以了解为什么这两种行为如此不同,并从 ListView.java 中发现了这一点:
public void setItemsCanFocus(boolean itemsCanFocus) {
mItemsCanFocus = itemsCanFocus;
if (!itemsCanFocus) {
setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
}
}
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.
因此,在调用 时setItemsCanFocus(false)
,它还设置了后代的可聚焦性,以便没有孩子能够获得焦点。这解释了为什么我不能只mItemsCanFocus
在 ListView 的 OnItemSelectedListener 中进行切换——因为 ListView 然后阻止了对所有子项的焦点。
What I have now:
我现在所拥有的:
<ListView
android:id="@android:id/list"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:descendantFocusability="beforeDescendants"
/>
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.
我使用beforeDescendants
是因为只有当 ListView 本身(不是子视图)有焦点时才会绘制选择器,所以默认行为需要是 ListView 首先获取焦点并绘制选择器。
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.
然后在 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);
}
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.
注意注释掉的setItemsCanFocus
调用。通过这些调用,我得到了正确的行为,但setItemsCanFocus(false)
导致焦点从 EditText 跳转到 ListView 之外的另一个小部件,再回到 ListView 并在下一个选定项目上显示选择器,而这种跳转焦点会分散注意力。删除 ItemsCanFocus 更改,只需切换后代可聚焦性即可获得所需的行为。所有项目都像往常一样绘制选择器,但是当到达带有 EditText 的行时,它会专注于文本字段。然后当继续离开那个 EditText 时,它再次开始绘制选择器。
回答by logan
This helped me.
In your manifest :
这对我有帮助。
在您的清单中:
<activity android:name= ".yourActivity" android:windowSoftInputMode="adjustPan"/>
回答by AndroidGecko
My task was to implement ListView
which expands when clicked. The additional space shows EditText
where you can input some text. App should be functional on 2.2+ (up to 4.2.2 at time of writing this)
我的任务是实现ListView
单击时扩展。额外的空间显示EditText
您可以输入一些文本的位置。应用程序应该在 2.2+ 上运行(在撰写本文时最高为 4.2.2)
I tried numerous solutions from this post and others I could find; tested them on 2.2 up to 4.2.2 devices. None of solutions was satisfactionary on all devices 2.2+, each solution presented with different problems.
我从这篇文章和我能找到的其他人中尝试了许多解决方案;在 2.2 到 4.2.2 设备上测试它们。没有一个解决方案在所有 2.2+ 设备上都令人满意,每个解决方案都存在不同的问题。
I wanted to share my final solution :
我想分享我的最终解决方案:
- set listview to
android:descendantFocusability="afterDescendants"
- set listview to
setItemsCanFocus(true);
- set your activity to
android:windowSoftInputMode="adjustResize"
Many people suggestadjustPan
butadjustResize
gives much better ux imho, just test this in your case. WithadjustPan
you will get bottom listitems obscured for instance. Docs suggest that ("This is generally less desirable than resizing"). Also on 4.0.4 after user starts typing on soft keyboard the screen pans to the top. - on 4.2.2 with
adjustResize
there are some problems with EditText focus. The solution is to apply rjrjr solution from this thread. It looks scarry but it is not. And it works. Just try it.
- 将列表视图设置为
android:descendantFocusability="afterDescendants"
- 将列表视图设置为
setItemsCanFocus(true);
- 将您的活动设置为
android:windowSoftInputMode="adjustResize"
许多人建议adjustPan
但adjustResize
提供更好的用户体验恕我直言,只需在您的情况下进行测试即可。例如,adjustPan
你会得到底部列表项的模糊。文档建议(“这通常不如调整大小”)。同样在 4.0.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:
getting View for ListView item / reverse order on 2.2; works on 4.0.3
附加 5.由于在EditText
专注于蜂窝前版本时刷新适配器(由于视图调整大小),我发现反向视图存在问题:
在 2.2 上获取 ListView 项目的视图/反向顺序;适用于 4.0.3
If you are doing some animations you might want to change behaviour to adjustPan
for pre-honeycomb versions so that resize doesnt fire and adapter doesn't refresh the views. You just need to add something like this
如果您正在制作一些动画,您可能希望将行为更改adjustPan
为蜂窝前版本,以便调整大小不会触发并且适配器不会刷新视图。你只需要添加这样的东西
if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB)
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
All this gives acceptable ux on 2.2 - 4.2.2 devices. Hope it will save people some time as it took me at least several hours to come to this conclusion.
所有这些都在 2.2 - 4.2.2 设备上提供了可接受的用户体验。希望它可以为人们节省一些时间,因为我至少花了几个小时才得出这个结论。
回答by ravi ranjan
This saved my life--->
这救了我的命--->
set this line
ListView.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
Then in your manifest in activity tag type this-->
<activity android:windowSoftInputMode="adjustPan">
设置这条线
ListView.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
然后在您的清单中的活动标签中输入 this-->
<activity android:windowSoftInputMode="adjustPan">
Your usual intent
你通常的意图
回答by rjrjr
We're trying this on a short list that does not do any view recycling. So far so good.
我们正在一个不做任何视图回收的短列表上尝试这个。到现在为止还挺好。
XML:
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();
}
}
});
}
}
回答by Rafael Sanches
this post was matching exactly my keywords. I have a ListView header with a search EditText and a search Button.
这篇文章与我的关键字完全匹配。我有一个带有搜索 EditText 和搜索按钮的 ListView 标题。
In order to give focus to the EditText after loosing the initial focus the only HACK that i found is:
为了在失去初始焦点后将焦点放在 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();
}
});
Lost lots of hours and it's not a real fix. Hope it helps someone tough.
失去了很多小时,这不是真正的修复。希望它可以帮助困难的人。
回答by Pēteris Caune
If the list is dynamic and contains focusable widgets, then the right option is to use RecyclerViewinstead of ListView IMO.
如果列表是动态的并且包含可聚焦的小部件,那么正确的选择是使用RecyclerView而不是 ListView IMO。
The workarounds that set adjustPan
, FOCUS_AFTER_DESCENDANTS
, or manually remember focused position, are indeed just workarounds. They have corner cases (scrolling + soft keyboard issues, caret changing position in EditText). They don't change the fact that ListView creates/destroys views en masseduring notifyDataSetChanged
.
设置adjustPan
、FOCUS_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,您可以通知单个插入、更新和删除。没有重新创建聚焦视图,因此表单控件失去焦点没有问题。作为一个额外的好处,RecyclerView 动画列表项的插入和删除。
Here's an example from official docs on how to get started with RecyclerView
: Developer guide - Create a List with RecyclerView
以下是官方文档中有关如何开始使用的示例RecyclerView
:开发人员指南 - 使用 RecyclerView 创建列表
回答by Pēteris Caune
some times when you use android:windowSoftInputMode="stateAlwaysHidden"
in manifest activity or xml, that time it will lose keyboard focus. So first check for that property in your xml and manifest,if it is there just remove it. After add these option to manifest file in side activity android:windowSoftInputMode="adjustPan"
and add this property to listview in xml android:descendantFocusability="beforeDescendants"
有时当您android:windowSoftInputMode="stateAlwaysHidden"
在清单活动或 xml 中使用时,它会失去键盘焦点。因此,首先检查您的 xml 和清单中的该属性,如果存在,则将其删除。将这些选项添加到侧面活动中的清单文件后,android:windowSoftInputMode="adjustPan"
将此属性添加到 xml 中的列表视图android:descendantFocusability="beforeDescendants"
回答by alaeri
I just found another solution. I believe it's more a hack than a solution but it works on android 2.3.7 and android 4.3 (I've even tested that good old D-pad)
我刚刚找到了另一个解决方案。我相信它更像是一种破解而不是解决方案,但它适用于 android 2.3.7 和 android 4.3(我什至测试过那个很好的旧 D-pad)
init your webview as usual and add this: (thanks Michael Bierman)
像往常一样初始化您的 webview 并添加以下内容:(感谢 Michael Bierman)
listView.setItemsCanFocus(true);
During the getView call:
在 getView 调用期间:
editText.setOnFocusChangeListener(
new OnFocusChangeListener(View view,boolean hasFocus){
view.post(new Runnable() {
@Override
public void run() {
view.requestFocus();
view.requestFocusFromTouch();
}
});
回答by Weber Antoine
Another simple solution is to define your onClickListener, in the getView(..) method, of your ListAdapter.
另一个简单的解决方案是在 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;
That way your row are clickable, and your inner view too :)
这样你的行是可点击的,你的内部视图也是:)