6

我正在开发一个版本为 2.9.0 的 phonegap 应用程序;

使用 RWD 书签(http://responsive.victorcoulon.fr/)在桌面浏览器中对布局进行了全面测试,并且运行良好。然而,当在移动设备或模拟器中进行测试时,布局被破坏了。经过一点点测试,我发现问题出在状态栏高度上。将应用程序更改为全屏,问题解决。

但是现在,当我专注于输入字段时,屏幕没有被调整,所以键盘覆盖了输入字段!

在查看了所有问题和相关问题之后,我发现了这个,这对我来说很有意义,但我想知道是否有办法让调整平移与全屏一起工作,所以我不需要调整我的所有组件高度,根据设备计算不同的状态栏高度等。

代码

表单.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>

安卓清单.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> 

应用程序.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")
    }
}
4

3 回答 3

2

虽然这可能不是修复它的更好方法,但我找到了解决方案。检测事件并与 JS 通信对我不起作用,无论是使用 window.scrollTo 还是使用 jQuery 插件。不幸的是,我的时间很短,我更喜欢直接用 Java 来做。只要我有时间,我会重构它并基于这个解决方案开发一个插件。随着代码的更新,我也会在这里更新。它是这样的:

    /**
     * 
     *  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's 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;
                    }
                }
            }
        }
    });

详述 r、lostFocus 和 showKeyboard

r是一个 Rect 对象,由getWindowVisibleDisplayFrame(Rect r)方法填充

从文档:

检索此视图所附加到的窗口所在的整体可见显示大小。这考虑到窗口上方的屏幕装饰,无论是窗口本身位于其中还是窗口位于下方然后和覆盖的插图用于窗口将其内容定位在里面。实际上,这告诉您可以放置​​内容并保持对用户可见的可用区域。

因此,如果显示键盘,则r.bottom将与 rootView 高度不同。

showKeyboardlostFocus是可靠地获得正确的焦点/模糊行为的两种解决方法。showKeyboard很简单,只是告诉应用程序是否应该滚动的标志。从理论上讲,它会起作用,但是,我遇到了一个烦人的错误,这导致输入字段在失去焦点后立即聚焦,在软键盘隐藏之前(仅在应用程序内部,在设备上,元素没有' t 获得焦点并且键盘已经隐藏)。为了解决这个问题,我使用了 lostFocus 来告诉应用程序何时真正失去焦点,并且仅在元素没有失去焦点时才允许它计算滚动的位置。

于 2013-09-16T15:59:11.033 回答
2

更新

跟进我的原始答案,下面的 JavaScript 将起作用windowSoftInputMode="adjustResize",但是,它不起作用,因为显示键盘时不会触发"adjustPan"JavaScript事件。resize()

但是,正如这里提到的,您可以通过将 a 挂钩GlobalLayoutListener到Java 端来捕获键盘显示事件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) {};");
                }
            }
        });
    }
}

所以当键盘显示时,你可以向 JavaScript 触发一个函数,表明键盘正在显示,然后你可以调整屏幕:

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;
});

原始答案

我们在尝试修复覆盖输入字段的 Android 软键盘时度过了一段可怕的时光。我想出的解决方案绝不是“好”,但它确实有效......

您将需要 jQuery 以及 Ariel Flesher 的另一个名为 jQuery.ScrollTo 的 jQuery 插件,可在此处找到。

现在将其添加到 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;
});

当 textarea/input 成为焦点时,DOM 元素被分配给一个变量。调整窗口大小时,会滚动窗口以将 DOM 元素置于顶部。

我们在 Android 清单中使用了 android:windowSoftInputMode="adjustResize"。

正如我所说,这不是最优雅的修复,但我们已经在我们的 PhoneGap 应用程序中实施并且它可以工作。

于 2013-09-04T09:41:56.580 回答
0

刚刚将解决方案发布在: http: //www.stackoverflow.com/questions/19849462/

于 2013-11-10T16:22:39.537 回答