Skip to main content

Focusable EditText inside ListView



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.





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 will focus it at that point, however that relies on a touchscreen, which should not be a requirement.





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.





To make matters worse, calling editTextView.requestFocus() returns true, but in fact does not give the EditText focus.





What I'm envisioning is basically a hybrid of 1 & 2, where rather than the list setting if all items are focusable or not, I want to set focusability for a single item 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.





Any takers?


Comments

  1. 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="fill_parent"
    android:layout_width="fill_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.

    ReplyDelete
  2. This helped me.

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

    ReplyDelete
  3. this post was matching exactly my keywords. I have a ListView header with a search EditText and a search Button.

    In order to give focus to the EditText after loosing the initial focus the only HACK that i found is:

    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.

    ReplyDelete
  4. Another simple solution is to define your onClickListener, in the getView(..) method, of your ListAdapter.

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

    ReplyDelete

Post a Comment

Popular posts from this blog

[韓日関係] 首相含む大幅な内閣改造の可能性…早ければ来月10日ごろ=韓国

div not scrolling properly with slimScroll plugin

I am using the slimScroll plugin for jQuery by Piotr Rochala Which is a great plugin for nice scrollbars on most browsers but I am stuck because I am using it for a chat box and whenever the user appends new text to the boxit does scroll using the .scrollTop() method however the plugin's scrollbar doesnt scroll with it and when the user wants to look though the chat history it will start scrolling from near the top. I have made a quick demo of my situation http://jsfiddle.net/DY9CT/2/ Does anyone know how to solve this problem?

Why does this javascript based printing cause Safari to refresh the page?

The page I am working on has a javascript function executed to print parts of the page. For some reason, printing in Safari, causes the window to somehow update. I say somehow, because it does not really refresh as in reload the page, but rather it starts the "rendering" of the page from start, i.e. scroll to top, flash animations start from 0, and so forth. The effect is reproduced by this fiddle: http://jsfiddle.net/fYmnB/ Clicking the print button and finishing or cancelling a print in Safari causes the screen to "go white" for a sec, which in my real website manifests itself as something "like" a reload. While running print button with, let's say, Firefox, just opens and closes the print dialogue without affecting the fiddle page in any way. Is there something with my way of calling the browsers print method that causes this, or how can it be explained - and preferably, avoided? P.S.: On my real site the same occurs with Chrome. In the ex