9

When writing in the Textfield, I need my textfield to move upwards in order to let the textfield be visible when the keyboard pops up.

Does libgdx have some kind of method which returns true if the keyboard is visible and false when it is down?

Patrick Romstad
  • 131
  • 1
  • 4
  • possible duplicate of [Detect if soft Keyboard is visible on screen](http://stackoverflow.com/questions/4745988/detect-if-soft-keyboard-is-visible-on-screen) – Shivan Dragon Mar 07 '12 at 16:13
  • This has nothing to do with libgdx, you want to listen for a native Android event. This question has been asked and answered – Shivan Dragon Mar 07 '12 at 16:13
  • 3
    Kinda, but if it isn't provided by LibGDX the implementation effort to listen to this event is much bigger ;). – Dominik Bucher Mar 07 '12 at 16:23

3 Answers3

14

The following code will detect when you press a textfield, prevent it from showing the keyboard and then open a native dialog that moves up and down with the keyboard. It will take the input from the native dialog and finally put it back in your textField:

    textField.setOnscreenKeyboard(new TextField.OnscreenKeyboard() {
        @Override
        public void show(boolean visible) {
            //Gdx.input.setOnscreenKeyboardVisible(true);
            Gdx.input.getTextInput(new Input.TextInputListener() {
                @Override
                public void input(String text) {
                    textField.setText(text);
                }

                @Override
                public void canceled() {
                    System.out.println("Cancelled.");
                }
            }, "Title", "Default text...");
        }
    });

Good Luck!

JohnyTex
  • 2,912
  • 3
  • 24
  • 44
7

I know I'm answering to an old thread, but I was googling to find an answer to this question but couldn't find it anywhere. Now I have created a solution myself. Here is how to do it on Android in an elegant way :) I'm creating an ApplicationBundle to bundle interfaces to add platform specific things. You can do this on iOS too if you want to make use of RoboVM.

My solution:

create a SizeChangeListener interface in the core project:

public interface SizeChangeListener {
    public void onSizeChange(float width, float height);
}

create a View interface in the core project:

public interface View {
    public void onSizeChange(float width, float height);
    public void addListener(SizeChangeListener sizeChangeListener);
    public float getWidth();
    public float getHeight();
}

create an AndroidView implementing the View interface:

public class AndroidView implements View {

    private ArrayList<SizeChangeListener> listeners = new ArrayList<SizeChangeListener>();
    private float width, height;
    public AndroidView(int width, int height) {
        this.width = width;
        this.height = height;
    }

    public void addListener(SizeChangeListener listener) {
        listeners.add(listener);
    }

    public void onSizeChange(float width, float height) {
        this.width = width;
        this.height = height;
        for(SizeChangeListener listener : listeners)
            listener.onSizeChange(width, height);
    }

    public float getWidth() {
        return width;
    }

    public float getHeight() {
        return height;
    }

}

create an ApplicationBundle in the core project

public class ApplicationBundle {

    private final View view;

    public ApplicationBundle(View view) {
        this.view = view;
    }

    public View getView() {
        return view;
    }
}

Make the necessary imports from the core project. In the AndroidLauncher in the Android project add the following:

public class AndroidLauncher extends AndroidApplication {

    private View rootView;
    private AndroidView androidView;
    private int width, height;

    @Override
    protected void onCreate (Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        AndroidApplicationConfiguration config = new AndroidApplicationConfiguration();
        rootView = this.getWindow().getDecorView().getRootView();
        Rect rect = new Rect();
        rootView.getWindowVisibleDisplayFrame(rect);
        width = rect.width();
        height = rect.height();
        androidView = new AndroidView(width, height);

        rootView.addOnLayoutChangeListener(new OnLayoutChangeListener() {

            @Override
            public void onLayoutChange(View v, int left, int top, int right,
                    int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {

                Rect rect = new Rect();
                rootView.getWindowVisibleDisplayFrame(rect);

                if(!(width == rect.width() && height == rect.height())) {
                    width = rect.width();
                    height = rect.height();
                    androidView.onSizeChange(width, height);
                }
            }

        }); 

        initialize(new DigMeApp(new ApplicationBundle(androidView)), config);
    }
}

In your main MyApp in the core project in the create() method add a SizeChangeListener implementation to the view you've got from the constructor.

public class MyApp extends Game { // or ApplicationAdapter
    private View view;
    private Stage stage;
    // your own variables

    public MyApp(ApplicationBundle applicationBundle) {
        view = applicationBundle.getView();
    }

    @Override
    public void create () {
        stage = new Stage();
        // add some textfields
    final TextField tf1 = new TextField("", skin);
    final TextField tf2 = new TextField("", skin);

    tf1.setWidth((float)view.getWidth() * 0.6f);
    tf2.setWidth((float)view.getWidth() * 0.6f);
    tf1.setHeight((float)view.getHeight() * 0.05f);
    tf2.setHeight((float)view.getHeight() * 0.05f);
        view.addListener(new SizeChangeListener() {         
            @Override
            public void onSizeChange(float width, float height) {
                Gdx.app.log("INFO", "Visible area: " + width + "   " + height);
                Gdx.app.log("INFO", "Stage area: " + stage.getWidth() + "   " + stage.getHeight());
                float keyboardHeight = getKeyboardHeight();

// MOVE THEM OUT OF THE WAY :)

                tf1.addAction(Actions.moveTo(width / 2 - tf1.getWidth() / 2.0f, keyboardHeight + (6 * (height / 8)), 1, Interpolation.sineOut));
                tf2.addAction(Actions.moveTo(width / 2 - tf2.getWidth() / 2.0f, keyboardHeight + (7 * (height / 8)), 1, Interpolation.sineOut));


//              Gdx.gl20.
//              tf.setPosition(width / 2 - (tf.getWidth() / 2.0f), 0);
            }
        });
}

Perhaps create a little keyboard heigt method like I did:

private float getKeyboardHeight() {
        return stage.getHeight() - view.getHeight();
    }
Willempie
  • 91
  • 1
  • 3
  • I love this answer, it works very well on all devices I have tested it on. The only comment I would make is `view.getHeight()` needs to be scaled to match the stage or camera dimensions if you have forced libgdx to be a particular resolution. @Willempie do you have a working solution for iOS? – hamham May 25 '16 at 10:37
  • This is a great answer, works really well, and thankfully I don't have to implement the iOS way so that isn't an issue. This should be the selected answer, although I have one caveat. When I touch a textfield, as the keyboard appears, so does a black banner at the top of my app with my app name in it. It seems to be down to the line `rootView = this.getWindow().getDecorView().getRootView();` any idea how to avoid this? – Russ Wheeler Feb 16 '17 at 00:50
  • [SOLVED] `this.requestWindowFeature(Window.FEATURE_NO_TITLE);` I added this line before rootView gets set – Russ Wheeler Feb 16 '17 at 01:05
  • Even though I'm going to use this method in my game, about 1 in 10/20 times, the new size isn't detected, and I have no idea why. Can't replicate it, it seems almost random :( – Russ Wheeler Feb 17 '17 at 13:39
  • Good solution, pity it requires Min API Level 11 – Mitrakov Artem Mar 01 '17 at 07:01
2

Try

Gdx.input.isPeripheralAvailable(Input.Peripheral.OnscreenKeyboard);

I just looked this up in the docs, don't know if it actually does the trick. But the

Gdx.input.setOnscreenKeyboardVisible(boolean visible);

method could be used as well (like this YOU define when the keyboard is visible and when not).

Dominik Bucher
  • 1,449
  • 13
  • 16
  • Have tried Input.Peripheral.OnscreenKeyboard, but it is always true when running the application on android. But I will try to manually do it, thanks for the tip :) – Patrick Romstad Mar 07 '12 at 16:40
  • 2
    Otherwise, as Andrei Bodnarescu suggested, you can always listen for the events as described in http://stackoverflow.com/questions/4745988/detect-if-soft-keyboard-is-visible-on-screen, use the same structured as I described in http://stackoverflow.com/questions/9584959/using-sqlite-from-libgdx-on-android/9590715#9590715. Cheers! – Dominik Bucher Mar 08 '12 at 00:15
  • It seems to return always true no? – JohnyTex Apr 10 '14 at 21:59