106

I like my UIs to be intuitive; each screen should naturally and unobtrusively guide the user on to the next step in the app. Barring that, I strive to make things as confusing and confounding as possible.

Just kidding :-)

I've got three TableRows, each containing a read-only and non-focusable EditText control and then a button to its right. Each button starts the same activity but with a different argument. The user makes a selection there and the sub-activity finishes, populating the appropriate EditText with the user's selection.

It's the classic cascading values mechanism; each selection narrows the available options for the next selection, etc. Thus I'm disabling both controls on each of the next rows until the EditText on the current row contains a value.

I need to do one of two things, in this order of preference:

  1. When a button is clicked, immediately remove focus without setting focus to a different button
  2. Set focus to the first button when the activity starts

The problem manifests after the sub-activity returns; the button that was clicked retains focus.

Re: #1 above - There doesn't appear to be a removeFocus() method, or something similar

Re: #2 above - I can use requestFocus() to set focus to the button on the next row, and that works after the sub-activity returns, but for some reason it doesn't work in the parent activity's onCreate().

I need UI consistency in either direction--either no buttons have focus after the sub-activity finishes or each button receives focus depending on its place in the logic flow, including the very first (and only) active button prior to any selection.

InteXX
  • 5,804
  • 5
  • 33
  • 54

12 Answers12

183

Using clearFocus() didn't seem to be working for me either as you found (saw in comments to another answer), but what worked for me in the end was adding:

<LinearLayout 
    android:id="@+id/my_layout" 
    android:focusable="true" 
    android:focusableInTouchMode="true" ...>

to my very top level Layout View (a linear layout). To remove focus from all Buttons/EditTexts etc, you can then just do

LinearLayout myLayout = (LinearLayout) activity.findViewById(R.id.my_layout);
myLayout.requestFocus();

Requesting focus did nothing unless I set the view to be focusable.

actionshrimp
  • 5,033
  • 3
  • 19
  • 25
  • For a fleeting moment I thought this might work. "How beautiful in its simplicity!" I said to myself. Alas, it was not to be. Even when I remove requestFocus() the next row's button gets focus. This would be OK as a second preference, but only if I can somehow set focus on the first button in onCreate(). – InteXX May 24 '11 at 23:53
  • @InteXX: I came across this problem again today and found a solution. I know you've gone down a different route now, but give this a try if you ever find yourself in the same boat again! – actionshrimp May 28 '11 at 16:34
  • "I know you've gone down a different route now" It's still not too late to back up :-) I'll give that a try and let you know, thanks. – InteXX May 30 '11 at 04:06
  • Darn. Almost, but not quite. Yes, it takes focus off the last-clicked button, but it also then prevents ANY button from gaining focus. This would mean my non-touch users wouldn't be able to click a button. Is it doing the same for you? – InteXX Jun 02 '11 at 02:40
  • I wish I could upclick more! This is perfect for my username / password screen. I want the user to actively click in the username or password to modify. When the app comes up, I want it to appear with no keyboard. I just picked another layout, made it focusable as shown and voila -- it worked! -- Thanks! – JohnnyLambada Mar 14 '12 at 23:11
  • This does work for me. A dummy focusable view is required for clearFocus() to work. I used android:imeOptions="actionDone" on the EditText fields to prevent the "focus next" behavior and instead of LinearLayout I used a View with width and height set to 0dp for better performance. I'm slightly concerned about the behavior of devices with hardware keyboards because pressing "up" focuses the invisible view and none of the keys are triggering IME_ACTION_DONE. – James Wald May 25 '12 at 14:28
  • 6
    This will not work on ScrollView. If your top view is ScrollView, use this on its child. – Uroš Podkrižnik Nov 16 '16 at 08:24
  • Works very well to remove any "visible" focus from all input elements. It's true that it doesn't work if you set the focus to a ScrollView (one of the contained views will still get focus), but it works if you you have e.g. a LinearLayout as the ScrollView's child and set the focus to this LinearLayout. – weibeld Nov 21 '16 at 14:09
  • Fantastic. Just a note: You no longer have to cast to `LinearLayout`, it's done for you. – SH7890 Aug 18 '17 at 15:31
  • This answer got flagged with a user wondering why you have a link to *this very question* in your answer. I have no idea, either. I thought maybe another question that you had answered got merged with this one, but I see no evidence of that in the history. Did you mean to link to a *specific answer* here, instead of the question itself? Can you check back up on this and see if the link can be fixed or if it's better to just remove it? – Cody Gray Aug 18 '17 at 15:32
  • @CodyGray I'm almost certain it was another, similar question so I'd assume it was due to some kind of merge. My memory's a bit hazy as I wrote this quite a while back! I'll edit the text now to remove the misleading link though. – actionshrimp Aug 24 '17 at 09:50
  • There's an undesired side effect when a view is focused it becomes highlighted and that's visually NOT ok. Could be mitigated with android:defaultFocusHighlightEnabled="false" but it's available on minSDK=26+. – WindRider Nov 30 '20 at 11:20
85

Old question, but I came across it when I had a similar issue and thought I'd share what I ended up doing.

The view that gained focus was different each time so I used the very generic:

View current = getCurrentFocus();
if (current != null) current.clearFocus();
willlma
  • 6,485
  • 2
  • 24
  • 42
  • 12
    Elegant way of doing this. One might want to check for null a value from getCurrentFocus() before calling clearFocus() – Vering Oct 29 '13 at 14:16
  • 5
    +1 Yes, you will have to throw in that null check! - Why can't Java just implement a safe-traversal-operator? It would be as simple as `getCurrentFocus()?.clearFocus();`, so nice and elegant :-/ – Levite Oct 24 '14 at 07:45
  • @Vering I have edited my answer according to your suggestion. I haven't done any Android dev since I posted this answer, so could you just confirm that the edit is correct? Thanks. – willlma Nov 19 '14 at 18:30
  • 1
    @willlma: Yes, this was exactly what I meant. – Vering Nov 20 '14 at 14:21
  • 5
    @Levit Very Swift-like. :-) But even if Java did implement such an operator, it would still take Android 4 years to catch up. – Greg Brown Nov 13 '15 at 18:47
  • 3
    @Levit, you've probably found it by now, but Kotlin. – prodaea Sep 11 '16 at 19:08
  • 1
    clearFocus() just won't remove focus from my EditText view – Raphael Royer-Rivard Nov 27 '16 at 19:04
  • Unfortunately this might transfer focus to the upwards nav button, which might not be the desired outcome -- still great answer though. – Sakiboy Jun 12 '19 at 23:23
9

android:descendantFocusability="beforeDescendants"

using the following in the activity with some layout options below seemed to work as desired.

 getWindow().getDecorView().findViewById(android.R.id.content).clearFocus();

in connection with the following parameters on the root view.

<?xml
    android:focusable="true"
    android:focusableInTouchMode="true"
    android:descendantFocusability="beforeDescendants" />

https://developer.android.com/reference/android/view/ViewGroup#attr_android:descendantFocusability

Answer thanks to: https://forums.xamarin.com/discussion/1856/how-to-disable-auto-focus-on-edit-text

About windowSoftInputMode

There's yet another point of contention to be aware of. By default, Android will automatically assign initial focus to the first EditText or focusable control in your Activity. It naturally follows that the InputMethod (typically the soft keyboard) will respond to the focus event by showing itself. The windowSoftInputMode attribute in AndroidManifest.xml, when set to stateAlwaysHidden, instructs the keyboard to ignore this automatically-assigned initial focus.

<activity
    android:name=".MyActivity"
    android:windowSoftInputMode="stateAlwaysHidden"/>

great reference

CrandellWS
  • 2,358
  • 4
  • 44
  • 100
9
  1. You can use View.clearFocus().

  2. Use View.requestFocus() called from onResume().

Ahmed Salman Tahir
  • 1,751
  • 1
  • 17
  • 26
siliconeagle
  • 6,869
  • 2
  • 26
  • 35
  • @siliconeagle: Now this is very VERY odd. (Thanks for the pointer to clearFocus() btw--I wasn't aware of this.) When I click the first button, make a selection, and then return, the first button can no longer receive focus. I'm not setting focusable for this button anywhere in my code. I'd like to show you my code, but there aren't enough characters available in this editing window and being new to SO I'm not sure whether I should enter another answer just to be able to post code. – InteXX May 25 '11 at 00:35
  • @siliconeagle: Unfortunately, clearFocus() doesn't work--the buttons retain focus when the sub-activity returns. – InteXX May 25 '11 at 00:42
  • @siliconeagle: OK, never mind about that first comment. I was using setFocusable(false) in the button's click event and forgetting to turn it back on after the sub-activity returned. clearFocus() still has no effect though--I wonder why? I'm calling it on all buttons from onActivityResult(), but the clicked button still retains focus. Odd. – InteXX May 25 '11 at 01:13
  • @siliconeagle: @actionshrimp: I've decided to do away with all focus-related commands and settings. My original intent was to prevent focus on disabled buttons, but if this is what it costs it's not worth it. With no requestFocus(), clearFocus() or setFocusable(), no button retains focus after the sub-activity returns. It's the lesser of two weevils--I guess I'll have to live with the user being able to set focus to a disabled button. – InteXX May 25 '11 at 01:18
  • disabled elements aren't focusable AFAIK... use setEnabled(false) – siliconeagle May 25 '11 at 10:48
  • clearFocus() will unfocus the element IF it is focussed. the focus will shift to the next element. I think you need to disable you elements that you don't want focussed. You generally restore the activity state from onResume(). – siliconeagle May 25 '11 at 10:58
  • @siliconeagle: >> disabled elements aren't focusable AFAIK << Yes, that's what I thought too, at first, until I tried it. – InteXX May 30 '11 at 04:04
  • @siliconeagle: >> why do you use read-only edittext instead of textview? << Quite simply for the more attractive appearance. – InteXX May 30 '11 at 04:05
6
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:app="http://schemas.android.com/apk/res-auto"
          android:id="@+id/ll_root_view"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:orientation="vertical">

LinearLayout llRootView = findViewBindId(R.id.ll_root_view);
llRootView.clearFocus();

I use this when already finished update profile info and remove all focus from EditText in my layout

====> Update: In parent layout content my EditText add line:

android:focusableInTouchMode="true"
Ho Luong
  • 526
  • 7
  • 10
3

What about just adding android:windowSoftInputMode="stateHidden" on your activity in the manifest.

Taken from a smart man commenting on this: https://stackoverflow.com/a/2059394/956975

Community
  • 1
  • 1
marienke
  • 2,395
  • 4
  • 29
  • 62
3

I tried to disable and enable focusability for view and it worked for me (focus was reset):

focusedView.setFocusable(false);
focusedView.setFocusableInTouchMode(false);
focusedView.setFocusable(true);
focusedView.setFocusableInTouchMode(true);
Serhiy
  • 324
  • 5
  • 12
2

First of all, it will 100% work........

  1. Create onResume() method.
  2. Inside this onResume() find the view which is focusing again and again by findViewById().
  3. Inside this onResume() set requestFocus() to this view.
  4. Inside this onResume() set clearFocus to this view.
  5. Go in xml of same layout and find that top view which you want to be focused and set focusable true and focusableInTuch true.
  6. Inside this onResume() find the above top view by findViewById
  7. Inside this onResume() set requestFocus() to this view at the last.
  8. And now enjoy......
Ahmed Salman Tahir
  • 1,751
  • 1
  • 17
  • 26
2
android:focusableInTouchMode="true"
android:focusable="true"
android:clickable="true"

Add them to your ViewGroup that includes your EditTextView. It works properly to my Constraint Layout. Hope this help

Tánh Lee
  • 21
  • 3
1

You could try turning off the main Activity's ability to save its state (thus making it forget what control had text and what had focus). You will need to have some other way of remembering what your EditText's have and repopulating them onResume(). Launch your sub-Activities with startActivityForResult() and create an onActivityResult() handler in your main Activity that will update the EditText's correctly. This way you can set the proper button you want focused onResume() at the same time you repopulate the EditText's by using a myButton.post(new Runnable(){ run() { myButton.requestFocus(); } });

The View.post() method is useful for setting focus initially because that runnable will be executed after the window is created and things settle down, allowing the focus mechanism to function properly by that time. Trying to set focus during onCreate/Start/Resume() usually has issues, I've found.

Please note this is pseudo-code and non-tested, but it's a possible direction you could try.

InteXX
  • 5,804
  • 5
  • 33
  • 54
Uncle Code Monkey
  • 1,786
  • 1
  • 14
  • 23
  • On second thought... try just using the View.post() stuff first instead of turning off the Activity's saved state. That might do the trick for you all by itself. – Uncle Code Monkey Sep 13 '11 at 01:18
  • Alas I no longer have an easy way to test this for us. I've since decided to abandon the native code model and go with HTML5/CSS3 for mobile apps, and thus have retooled my system accordingly. But thank you for what appears to be a very in-depth answer. I voted it up. – InteXX Sep 13 '11 at 05:28
0

Try the following (calling clearAllEditTextFocuses();)

private final boolean clearAllEditTextFocuses() {
    View v = getCurrentFocus();
    if(v instanceof EditText) {
        final FocusedEditTextItems list = new FocusedEditTextItems();
        list.addAndClearFocus((EditText) v);

        //Focus von allen EditTexten entfernen
        boolean repeat = true;
        do {
            v = getCurrentFocus();
            if(v instanceof EditText) {
                if(list.containsView(v))
                    repeat = false;
                else list.addAndClearFocus((EditText) v);
            } else repeat = false;
        } while(repeat);

        final boolean result = !(v instanceof EditText);
        //Focus wieder setzen
        list.reset();
        return result;
    } else return false;
}

private final static class FocusedEditTextItem {

    private final boolean focusable;

    private final boolean focusableInTouchMode;

    @NonNull
    private final EditText editText;

    private FocusedEditTextItem(final @NonNull EditText v) {
        editText = v;
        focusable = v.isFocusable();
        focusableInTouchMode = v.isFocusableInTouchMode();
    }

    private final void clearFocus() {
        if(focusable)
            editText.setFocusable(false);
        if(focusableInTouchMode)
            editText.setFocusableInTouchMode(false);
        editText.clearFocus();
    }

    private final void reset() {
        if(focusable)
            editText.setFocusable(true);
        if(focusableInTouchMode)
            editText.setFocusableInTouchMode(true);
    }

}

private final static class FocusedEditTextItems extends ArrayList<FocusedEditTextItem> {

    private final void addAndClearFocus(final @NonNull EditText v) {
        final FocusedEditTextItem item = new FocusedEditTextItem(v);
        add(item);
        item.clearFocus();
    }

    private final boolean containsView(final @NonNull View v) {
        boolean result = false;
        for(FocusedEditTextItem item: this) {
            if(item.editText == v) {
                result = true;
                break;
            }
        }
        return result;
    }

    private final void reset() {
        for(FocusedEditTextItem item: this)
            item.reset();
    }

}
5chw4hn
  • 211
  • 2
  • 5
0

You do not need to clear focus, just add this code where you want to focus

 time_statusTV.setFocusable(true);
 time_statusTV.requestFocus();
 InputMethodManager imm = (InputMethodManager)this.getSystemService(Service.INPUT_METHOD_SERVICE);
 imm.showSoftInput( time_statusTV, 0);
rajan.kali
  • 10,789
  • 3
  • 22
  • 33