Buggy Listview Makes Me Sad

Buggy ListView makes me sad

It sounds like ListViews aren't able to handle EditTexts well. I've done some research and the consensus seems to be "don't do that." So what I've resorted to is creating a simple layout file which is a ScrollView with a LinearLayout inside. In my onCreate method, I inflate the View I was using for my list item and add it to the LinearLayout. I'm also adding the View to an ArrayList so I can save the data in each View later on.

Does this sound reasonable?

ListView + EditText = bad keyboard behavior

ListView don't do well with editable views, because the inner views get redrawn once or mutiple times, causing multiple focus events.

This happens since '10, so I don't expect a fix any time soon. I work arounded the issue.

Buggy ListView makes me sad

EditText in Listview loses focus when pressed on Android 4.x

A classic hack for situations like this is to use a handler and postDelayed(). In your adapter:

private int lastFocussedPosition = -1;
private Handler handler = new Handler();

public View getView(final int position, View convertView, ViewGroup parent) {

// ...

edittext.setOnFocusChangeListener(new OnFocusChangeListener() {

@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
handler.postDelayed(new Runnable() {

@Override
public void run() {
if (lastFocussedPosition == -1 || lastFocussedPosition == position) {
lastFocussedPosition = position;
edittext.requestFocus();
}
}
}, 200);

} else {
lastFocussedPosition = -1;
}
}
});

return convertView;
}

This works on my device, but keep this code out of production. I also wouldn't be surprised if the focus bug manifests itself differently in different android versions or roms.

There are also many other problems with embedding an EditText within a ListView that have solutions that feel like a hack. See all of the other people struggling.

It's also very easy to have something like this happen:

like this.

After having gone down similar paths many times myself, I've mostly given up on trying to override any of the default keyboard behaviours or quirks. I would recommend trying to find alternative solution in your app if possible.

Have you considered having the ListView rows be just a styled TextView and then displaying a Dialog with an EditText when a row is clicked, updating the TextView as necessary?

Focusable EditText inside ListView

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

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.

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.

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

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.

When the soft keyboard appears, it makes my EditText field lose focus

Here is how I did it. The onFocusChangeListener() is called several times when you touch a EditText to type text into it. The sequence is:

  1. If focus was on a different view, then that view loses focus
  2. The target gains focus
  3. Soft keyboard pops up.
  4. This causes the target to lose focus
  5. The code detects this situation and calls target.requestFocus()
  6. The leftmost, topmost view gains focus, due to Android nonsense
  7. The leftmost view loses focus, due to requestFocus being called
  8. Target finally gains focus

    //////////////////////////////////////////////////////////////////
    private final int minDelta = 300; // threshold in ms
    private long focusTime = 0; // time of last touch
    private View focusTarget = null;

    View.OnFocusChangeListener onFocusChangeListener = new View.OnFocusChangeListener() {
    @Override
    public void onFocusChange(View view, boolean hasFocus) {
    long t = System.currentTimeMillis();
    long delta = t - focusTime;
    if (hasFocus) { // gained focus
    if (delta > minDelta) {
    focusTime = t;
    focusTarget = view;
    }
    }
    else { // lost focus
    if (delta <= minDelta && view == focusTarget) {
    focusTarget.post(new Runnable() { // reset focus to target
    public void run() {
    focusTarget.requestFocus();
    }
    });
    }
    }
    }
    };

The code above works well for the keyboard pop-ups. However, it does not detect the speech-to-text pop-up.

editable EditText in clickable listView

first of all, thank you everyone!

I tried all of the answer, but it didn't work...:(

Focusable EditText in the ListView and onItemClick

it works for me

my code ▼

public class subMyListAdapter extends BaseAdapter{
Context context;
LayoutInflater Inflater;
ArrayList<subMyItem> arraySrc;
int layout;
static int currentTheme = 0;
EditText tvItem;
RelativeLayout rl_inflate;
UserHolder holder;
public subMyListAdapter(Context context, int layout, ArrayList<subMyItem> arraySrc)
{
this.context = context;
this.layout = layout;
this.arraySrc = arraySrc;
Inflater = (LayoutInflater)context.getSystemService(context.LAYOUT_INFLATER_SERVICE);
}
public int getCount()
{
return arraySrc.size();
}
public String getItem(int position)
{
return arraySrc.get(position).list;
}
public long getItemId(int position)
{
return position;
}
public View getView(final int position, View conv, ViewGroup parent)
{
holder = null;
if (conv == null)
{
LayoutInflater inflater = ((Activity) context).getLayoutInflater();
conv = inflater.inflate(layout, parent, false);
holder = new UserHolder();
holder.tvItem = (EditText)conv.findViewById(R.id.tvItem);
conv.setTag(holder);
}
else
{
holder = (UserHolder) conv.getTag();
}
if(holder == null)
{
holder = new UserHolder();
holder.tvItem = (EditText)conv.findViewById(R.id.tvItem);
conv.setTag(holder);
}
subMyItem user = arraySrc.get(position);
holder.tvItem.setOnTouchListener(test);
conv.setOnTouchListener(test);
if(conv == null)
{
conv = conv;
}
tvItem = (EditText) conv.findViewById(R.id.tvItem);
user = arraySrc.get(position);
tvItem.setText(user.list);
tvItem.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
Toast.makeText(context, "tvItem button Clicked",
Toast.LENGTH_LONG).show();
}
});
return conv;
}
View.OnTouchListener test= new View.OnTouchListener()
{
@Override
public boolean onTouch(View view, MotionEvent event)
{
if (view instanceof EditText)
{
EditText editText = (EditText) view;
editText.setFocusable(true);
editText.setFocusableInTouchMode(true);
} else
{
UserHolder holder = (UserHolder) view.getTag();
holder.tvItem.setFocusable(false);
holder.tvItem.setFocusableInTouchMode(false);
}
return false;
}
};
static class UserHolder
{
EditText tvItem;
}
}


Related Topics



Leave a reply



Submit