28

I have an Activity which is recreated on a config change (desired). I have a DialogFragment which calls setRetainInstance(true) with a single EditText in its layout.

In the DialogFragment's onActivityCreated I call:

getDialog().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);

A) If I open the keyboard then when I put the app into the background and then bring it to the foregournd then I want the keyboard to still be displayed.

B) If I close the keyboard (EditText still has focus and shows cursor which is desired behaviour) then I want the keyboard to still be closed if I put the app into the background and then bring it to the foreground.

I can't seem to achieve both A) and B). The keyboard is always closed when I bring the app to the foreground. I've tried .SOFT_INPUT_STATE_ALWAYS_VISIBLE but then the keyboard is always open.

Thanks in advance for any suggestions as to how I might achieve this. I also wish to maintain such keyboard state across rotation but I'm leaving that for another day. Peter.

Edit Please note that I do not want to prevent the activity from being re-created on a configuration change.

I also experimented with WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED which did maintain the keyboard open/close state during rotation on a phone (single pane layout) but a) did not work with a dual pane layout b) did not maintain the keyboard state when bringing the app to the foreground.

PJL
  • 18,735
  • 17
  • 71
  • 68
  • Using `getDialog()` in the `onCreate` method shouldn't return null? – user Mar 07 '13 at 10:07
  • Thanks, changed to `onActivityCreated` – PJL Mar 07 '13 at 10:40
  • 1
    I suggest taking a look at `HIDE_IMPLICIT_ONLY`, `HIDE_NOT_ALWAYS`, `SHOW_FORCED`, `SHOW_IMPLICIT`of `InputMethodManager` http://developer.android.com/reference/android/view/inputmethod/InputMethodManager.html and also inform that this functionality may not behave in all devices the same as some ROMs (official/or not) may force keyboard changes at switching activities. – madlymad Mar 13 '13 at 09:55
  • Not related to your question but you should not use setRetainInstance(true) on fragments that contains view widgets in them. – Tobias Lindberg Jun 24 '15 at 11:23

6 Answers6

12

Hello first of all thanks for an interesting question. It made me experiment with code. Here I am describing my solution.

To find the solution I had to know two things

1. how to detect whether a softkeyboard is visible or not

2. How to set softkeyboard visible or hidden.

I got the solution in the following steps after searching a bit I realized that the best solution of detecting a softkeyboardstate (visible/hidden) is to use ViewTreeObserver. I am directly pointing to a so answer to know about it if you don't know. Here is the link.

and to set the softkeyboardstate I just used Window.setSoftInputMode method.

and to know a user interaction I override onUserInteraction method

Kept two flag. one flag is to preserve keyboardstate another is to know whether the application went to background or not

CODE:

1. variable declared

int lastDiff = 0;
volatile boolean flag = false;
volatile int flag2 = 0;

2. ViewTreeObserver

activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(
    new OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            Rect r = new Rect();
            activityRootView.getWindowVisibleDisplayFrame(r);

            int heightDiff = activityRootView.getRootView()
                    .getHeight() - (r.bottom - r.top);
            if (lastDiff == heightDiff)
                return;
            lastDiff = heightDiff;
            Log.i("aerfin","arefin "+lastDiff);
            if (heightDiff > 100) { // if more than 100 pixels, its
                                    // probably a keyboard...
                flag2 = 0;
            } else {
                if (flag == false)
                    flag2 = 1;
            }
        }
    });

3. Handling user interaction

 @Override
 public void onUserInteraction() {
     super.onUserInteraction();
     flag = true;
 }

4. Finally onPause and onResume

@Override
protected void onPause() {
    super.onPause();
    flag = true;
}

@Override
protected void onResume() {
    flag = false;

    switch (flag2) {
    case 0:
        getWindow().setSoftInputMode(
                WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
        break;
    case 1:
        getWindow().setSoftInputMode(
                WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);

        break;
    default:
        break;
    }

    super.onResume();
}

Explanation:

Here I used two flags (flag2 and flag). flag2 preserves the keyboardstate and flag preserves whether the application goes to background or is there any user interaction. flag is used because when application goes to background then at first it hides the keyboard. Other things can be easily understood from the code above.

Test:

tested in s2(ics), desire s (ics), galaxy y (2.3.6)

Final comment:

I wrote the code quickly so might be missed some other optimization. Also there might be chance of exceptional cases. If the screen changes for some reasons other than keyboard then it might not be able to detect keyboard state.

Community
  • 1
  • 1
stinepike
  • 54,068
  • 14
  • 92
  • 112
  • Thanks for your conclusive answer. I agree that it does seem that a solution does seem to hinge upon knowing when the keyboard is open. I do wonder, however, whether such solutions proposed would work say with a dialog on a large screen device where the layout height of the dialog would not necessary change when a keyboard is presented. – PJL Mar 13 '13 at 10:31
  • I didnt't check that. So can't tell right now. may be you can check using my solution and post the result :). – stinepike Mar 14 '13 at 08:20
  • Comprehensive answer with details on how to invoke the keyboard together with necessary caveats as I do agree that there are likely to be cases when keyboard detection doesn't work. – PJL Mar 19 '13 at 09:16
  • Would this also apply in the case of device rotation? I see similar behavior there as well. Moving to landscape dismisses the keyboard. – sr09 Aug 26 '16 at 23:25
6

You should use a flag (boolean kbShowing) to keep the current keyboard status, such as set kbShowing = true when keyboard show, otherwise set kbShowing = false.

onCreate

    showKB(); // if keyboard is not showed automatically. 

onRestart

    if(kbShowing)
        showKb(); // if keyboard is not showed automatically. 
    else 
        hideKb(); // if keyboard is showed automatically.

If you don't know how to detect when keyboard show or hide, chck Stefan's answer on this topic How to capture the "virtual keyboard show/hide" event in Android?

Community
  • 1
  • 1
Wayne
  • 6,361
  • 10
  • 46
  • 69
  • I do wonder though whether Stefan's answer would help determine whether the keyboard is showing from a smallish dialog on a large screen device for which the layout measurements would not be affected by presence of a keyboard. – PJL Mar 12 '13 at 12:02
2

Declare your EditText at Class level ...

EditText editText;

Now override onResume() and onPause() method of activity ...

    @Override
    protected void onResume() 
    {
        // TODO Auto-generated method stub
        super.onResume();
        editText.requestFocus();

        editText.postDelayed(new Runnable() {
            @Override
            public void run() {
                InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
                imm.showSoftInput(editText, InputMethodManager.SHOW_FORCED);
            }   
        }, 100);
    }

    @Override
    protected void onPause() 
    {
        // TODO Auto-generated method stub
        super.onPause();
        editText.postDelayed(new Runnable() {
            @Override
            public void run() {
                InputMethodManager imm = (InputMethodManager)getSystemService(
                          Context.INPUT_METHOD_SERVICE);
                imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);
            }   
        }, 200);
    }

This code work for me perfectly.

Enjoy - :D

Darshak
  • 2,298
  • 4
  • 23
  • 45
  • Maybe my question isn't clear enough but isn't this showing the keyboard when the app comes to the foreground? If the keyboard was closed prior to putting the app into the background then I want it to be closed when coming back to the foreground. – PJL Mar 07 '13 at 14:11
2

Probably i would check in the onPause if the keyboard is open and set a flag (i think there are only hacky ways do this like the example below):

final View activityRootView = findViewById(R.id.activityRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
    int heightDiff = activityRootView.getRootView().getHeight() - activityRootView.getHeight();
    if (heightDiff > 100) { // if more than 100 pixels, its probably a keyboard...
        ... do something here
    }
 }
});

according to the answer here: How to check visibility of software keyboard in Android?

In onResume() set either this:

getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);

or:

getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
Andre Thiele
  • 3,202
  • 3
  • 20
  • 43
Luciano
  • 2,691
  • 4
  • 38
  • 64
2

Have you tried adding the keyboard state in the Manifest file in your activity:

 android:windowSoftInputMode="stateAlwaysVisible|adjustPan">

This will take care of the rotation part of your issue and should work on onResume as well. The stateAlwaysVisible will start the keyboard on the onCrate and the adjustPan will handle the rotation.

Here is a sample from one of my Activities from my Manifest file:

<activity
        android:name=".GMax3Main"
        android:label="@string/app_name" 
        android:windowSoftInputMode="stateAlwaysVisible|adjustPan">
        <intent-filter>
            <action android:name="com.medeasy.GMax3.MAIN" />

            <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
    </activity>

Hope this helps.

In my Activity Class I open my softkeyboard on the onCreate method of my class like so:

 @Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    new UserSync().execute();
    setContentView(R.layout.main);

    InputMethodManager imm = (InputMethodManager)
            GMax3Main.this.getSystemService(Context.INPUT_METHOD_SERVICE);

        if (imm != null){
            imm.toggleSoftInput(InputMethodManager.RESULT_SHOWN, 0);
        }

Then I call my android:windowSoftInputMode="stateAlwaysVisible|adjustPan"> like shown above in my Manifest file.

Brian White
  • 307
  • 1
  • 2
  • 11
  • 1
    But if the keyboard is closed prior to a rotation then won't it be open again after a rotation? – PJL Mar 13 '13 at 09:46
  • I will do a little more research when I get to my office and post back. – Brian White Mar 13 '13 at 11:14
  • I am working with a Level 7 app and it does work. If I close the keyboard and run my app in the background then resume my app the keyboard will come back up even on rotation.I am testing this on a G-Tablet that run 2.3.3. – Brian White Mar 13 '13 at 12:28
  • Can you clarify, if you close the keyboard and put the app into the background then when you bring the app back is the keyboard still closed? – PJL Mar 13 '13 at 14:07
  • I did close the keyboard in a horizontal view then place the app in the background and went to the browser app. I then closed my browser app and resumed my app where the keyboard came up with the intent. I then rotated the device and the keyboard was still there. I think this is what you are trying to do, correct? – Brian White Mar 13 '13 at 14:38
  • Select EditText to bring up keyboard. Hit 'Home' to put the app into the background. Bring the app back to the foreground. The result should be that the keyboard is still up. Similarly if you close the keyboard and put the app into the background then the keyboard should still be closed when bringing the app back to the foreground. – PJL Mar 13 '13 at 14:43
  • Ok, I know what the difference is. I open the soft keyboard on the onCreate of the activity and not on the focus of the EditText. That is why mine is coming up. – Brian White Mar 13 '13 at 14:47
2

I'd extend Wayne's approach by overwriting and creating your own EditText widget that you should use throughout your whole app.

public class PJLsEditText extends EditText 
{
    public PJLsEditText(Context context) {
        super(context);
        saveKbState();
    }

    public PJLsEditText(Context context, AttributeSet attrs) 
    {
        super(context, attrs);
        saveKbState();
    }

    private void saveKbState() 
    {
        //get keyboard state and set a flag either in a static class or as SharedPreference
    }

    // I'm not sure if EditText objects get destroyed on configuration change. 
    // If so, you might need to overwrite the onConfigurationChanged method here, 
    // as well...
} 

I assume this should help you to always know the last state of the keyboard, even if changed through Dialogs. You can hide or show the keyboard then according to this flag in your onResume and onPause methods.

Dennis Winter
  • 2,027
  • 4
  • 32
  • 45