Android Phonegap app does not adjust keyboard panning

I am developing a phonegap application with version 2.9.0;

The layout was fully tested in a desktop browser using the RWD Bookmarklet ( http://responsive.victorcoulon.fr/ ) and worked fine. However, when testing on mobile devices or an emulator, the layout crashed. After a little testing, I found out that the problem was in the status bar. Changed the application to full screen mode, the problem is resolved.

But now, when I focus on the input field, the screen does not adjust, so the keyboard closes the input field!

Having examined all the questions and related problems, I found this one that makes sense to me, but I wanted to know if there is a way to do that, adjust the panning operation using full screen mode, so I don’t need to adjust the height of all my components, calculate different line heights device-based states, etc.

Codes

form.html

<form id="login-form"> <div class="form-group"> <input type="text" name="login" class="form-control" id="login" placeholder=" xxxxxxx@example.com "> </div> <div class="form-group"> <input type="password" name="pass" class="form-control" id="password" placeholder="*******"> </div> <a class="pull-right login-btn" id="btn-login" href="#"><span class="image-replacement"></span></a> <a class="pull-right login-btn" id="btn-cadastro" href="#"><span class="image-replacement"></span></a> </form> 

Android Manifest.xml

 <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:windowSoftInputMode="adjustPan" package="com.com.app" android:versionName="1.0" android:versionCode="1" android:hardwareAccelerated="true"> <supports-screens android:largeScreens="true" android:normalScreens="true" android:smallScreens="true" android:xlargeScreens="true" android:resizeable="true" android:anyDensity="true" /> <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.RECEIVE_SMS" /> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.RECORD_VIDEO"/> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> <uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.WRITE_CONTACTS" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.GET_ACCOUNTS" /> <uses-permission android:name="android.permission.BROADCAST_STICKY" /> <application android:icon="@drawable/icon" android:label="@string/app_name" android:hardwareAccelerated="true" android:debuggable="true"> <activity android:name="App" android:label="@string/app_name" android:theme="@android:style/Theme.Black.NoTitleBar" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale" android:windowSoftInputMode="stateVisible|adjustPan"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="17"/> </manifest> 

App.java

 package com.com.app; import org.apache.cordova.Config; import org.apache.cordova.DroidGap; import android.os.Bundle; import android.view.WindowManager; public class BDH extends DroidGap { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Set by <content src="index.html" /> in config.xml getWindow().setFlags(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN, WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST); getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); super.loadUrl(Config.getStartUrl()); //super.loadUrl("file:///android_asset/www/index.html") } } 
+6
source share
3 answers

Although this may not be the best way to fix it, I found a solution. Event detection and communication with JS did not work for me, either using window.scrollTo or using the jQuery plugin. Unfortunately, my time is short, and I chose to do it right in Java. Since I have time, I will reorganize it and develop a plugin based on this solution. When the code is updated, I will also update it here. Here it is:

  /** * * Due to a well known bug on Phonegap¹, android softKeyboard adjustPan functionality wasn't working * as expected when an input field recieved focus. The common workaround(Change to adjustResize and), * however, was not applicable, due to an Android bug² that crashes fullscreen apps when in adjustResize mode. * This is an workaround, to detect when the softKeyboard is activated and then programatically scroll * whenever it needs; * * During the development proccess i came across an annoying behavior on android, that were making the * input field dispatch onFocusChange twice when focus was cleared, when it should dispatch only once. * The first one, without focus(Expected behavior), the second one WITH focus(Dafuq?), causing it to * not scroll back on blur. My workaround was to only enable it to set a flag(lostFocus parameter), and * only allow the method to calculate the scroll size IF the element had not lost it focus; * * ¹ - http://stackoverflow.com/questions/11968420/softkeyboard-in-phonegap-covers-input-elements * ² - http://stackoverflow.com/questions/7417123/android-how-to-adjust-layout-in-full-screen-mode-when-softkeyboard-is-visible **/ final View activityRootView = ((ViewGroup) findViewById(android.R.id.content)).getChildAt(0); activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() { @Override public void onGlobalLayout(){ View focused = appView.findFocus(); activityRootView.getWindowVisibleDisplayFrame(r); if(focused instanceof TextView){ if(focused.getOnFocusChangeListener() == null){ focused.setOnFocusChangeListener(new OnFocusChangeListener() { @Override public void onFocusChange(View v, boolean hasFocus) { if(!hasFocus){ activityRootView.scrollTo(0,0); lostFocus = true; showKeyBoard = false; }else{ showKeyBoard = true; } } }); } /** * * Really tricky one to find, that was the only way i found to detect when this listener call came from * the buggy input focus gain. If the element had lost its focus, r(A Rect representing the screen visible area) * would be the total height, what means that there would be no keyboard to be shown, as far as the screen * was completely visible. * **/ if(showKeyBoard || r.top != activityRootView.getHeight()){ int heightDiff = 0; int keyBoardSize = 0; int scrollTo = 0; heightDiff = activityRootView.getRootView().getHeight() - focused.getTop(); keyBoardSize = activityRootView.getRootView().getHeight() - r.bottom; if((keyBoardSize < focused.getBottom() && keyBoardSize > 0) && !lostFocus){ scrollTo = focused.getBottom() - keyBoardSize; } if(scrollTo == 0){ activityRootView.scrollTo(0,scrollTo); lostFocus = false; showKeyBoard = true; }else if(heightDiff < r.bottom){ activityRootView.scrollTo(0, scrollTo); lostFocus = false; showKeyBoard = false; } } } } }); 

Development on r, lostFocus and showKeyboard

r is a Rect object that is populated with the getWindowVisibleDisplayFrame (Rect r) method

From Documents:

Get the total visible size of the screen in which this view window is attached to which it was placed. This takes into account the decoration of the screen above the window, for both cases when the window itself is inside or the window is placed underneath and closed inserts are used for the window to place its contents inside. Essentially, this tells you about an accessible area where content can be placed and remain visible to users.

So, if the keyboard is shown, r.bottom will be different from the height of the rootView.

showKeyboard and lostFocus are two workarounds to reliably get the correct focus / blur behavior. showKeyboard is simple, just a flag, to tell the application if it should or should not scroll. Theoretically, this would work, however, I ran into an annoying error, due to which the input field was focused immediately after losing focus, before hiding the soft keyboard (Only inside the application, on the device, the element didn’t get focus, and the keyboard already hidden). To solve this problem, I used lostFocus to tell the application when it really lost focus, and only let it figure out where to scroll if the item didn't lose focus.

+2
source

UPDATE

Following my initial answer, the JavaScript below will work on windowSoftInputMode="adjustResize" , however it will not work on "adjustPan" because the JavaScript resize() event will not be fired when the keyboard is shown.

However, as mentioned here , you can capture the event displayed on the keyboard on the Java side by connecting the GlobalLayoutListener to ViewTreeObserver :

 package com.com.app; import org.apache.cordova.Config; import org.apache.cordova.DroidGap; import android.os.Bundle; import android.view.WindowManager; public class BDH extends DroidGap { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Set by <content src="index.html" /> in config.xml getWindow().setFlags(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN, WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST); getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); super.loadUrl(Config.getStartUrl()); //super.loadUrl("file:///android_asset/www/index.html") final View activityRootView = ((ViewGroup) findViewById(android.R.id.content)).getChildAt(0); activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() { @Override public void onGlobalLayout() { // r will be populated with the coordinates of your view that area still visible. Rect r = new Rect(); activityRootView.getWindowVisibleDisplayFrame(r); int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top); // If more than 100 pixels, its probably a keyboard... if (heightDiff > 100) { // Fire off a function to the JavaScript. this.sendJavascript("try { onKeyboardShowing(); } catch (e) {};"); } } }); } } 

So, when the keyboard is displayed, you can run the JavaScript function by indicating that the keyboard is displayed, then you can customize the screen:

 var fieldFocused = null; function onKeyboardShowing() { if(fieldFocused != null) { $('body').scrollTo(fieldFocused, 500, {offset:-50}); } } $(document).on('focus', 'input, textarea', function() { fieldFocused = $(this); }); $(document).on('blur', 'input, textarea', function() { fieldFocused = null; }); 

ORIGINAL RESPONSE

We had a terrible time trying to fix a soft Android keyboard covering input fields. The solution I came up with is by no means "nice," but it worked ...

You will need jQuery, as well as another jQuery plugin called jQuery.ScrollTo by Ariel Flesher, found here .

Now add this to your JavaScript:

 var fieldFocused = null; $(window).resize(function(e) { if(fieldFocused != null) { $('body').scrollTo(fieldFocused, 500, {offset:-50}); } }); $(document).on('focus', 'input, textarea', function() { fieldFocused = $(this); }); $(document).on('blur', 'input, textarea', function() { fieldFocused = null; }); 

When the textarea / input object is brought into focus, the DOM element is assigned to the variable. When the window changes, the window scrolls to bring the DOM element to the beginning.

We used android: windowSoftInputMode = "adjustResize" in the Android manifest.

As I said, these are not the most elegant fixes, but we implemented it in our PhoneGap application and it works.

+2
source
0
source

Source: https://habr.com/ru/post/953079/


All Articles