Android Comment ajuster la mise en page en mode plein écran lorsque softkeyboard est visible

j'ai fait beaucoup de recherches pour ajuster la mise en page quand softkeyboard est actif et je l'ai implémenté avec succès mais le problème vient quand j'utilise android:theme="@android:style/Theme.NoTitleBar.Fullscreen" ceci dans mon étiquette d'activité dans le fichier manifest.

pour cela j'ai utilisé android:windowSoftInputMode="adjustPan|adjustResize|stateHidden" avec différentes options mais pas de chance.

après que j'ai mis en œuvre FullScreen programmatically et essayé divers layout pour travailler avec FullScreen mais tout en vain.

I référé ces liens et ont regardé de nombreux billets ici liés à cette question:

http://android-developers.blogspot.com/2009/04/updating-applications-for-on-screen.html

http://davidwparker.com/2011/08/30/android-how-to-float-a-row-above-keyboard /

voici le code xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:id="@+id/masterContainerView"
    android:layout_width="fill_parent" android:layout_height="fill_parent"
    android:orientation="vertical" xmlns:android="http://schemas.android.com/apk/res/android"
    android:background="#ffffff">

    <ScrollView android:id="@+id/parentScrollView"
        android:layout_width="fill_parent" android:layout_height="wrap_content">

        <LinearLayout android:layout_width="fill_parent"
            android:layout_height="fill_parent" android:orientation="vertical">

            <TextView android:id="@+id/setup_txt" android:layout_width="wrap_content"
                android:layout_height="wrap_content" android:text="Setup - Step 1 of 3"
                android:textColor="@color/top_header_txt_color" android:textSize="20dp"
                android:padding="8dp" android:gravity="center_horizontal" />

            <TextView android:id="@+id/txt_header" android:layout_width="fill_parent"
                android:layout_height="40dp" android:text="AutoReply:"
                android:textColor="@color/top_header_txt_color" android:textSize="14dp"
                android:textStyle="bold" android:padding="10dp"
                android:layout_below="@+id/setup_txt" />

            <EditText android:id="@+id/edit_message"
                android:layout_width="fill_parent" android:layout_height="wrap_content"
                android:text="Some text here." android:textSize="16dp"
                android:textColor="@color/setting_editmsg_color" android:padding="10dp"
                android:minLines="5" android:maxLines="6" android:layout_below="@+id/txt_header"
                android:gravity="top" android:scrollbars="vertical"
                android:maxLength="132" />

            <ImageView android:id="@+id/image_bottom"
                android:layout_width="fill_parent" android:layout_height="wrap_content"
                android:layout_below="@+id/edit_message" />

        </LinearLayout>
    </ScrollView>

    <RelativeLayout android:id="@+id/scoringContainerView"
        android:layout_width="fill_parent" android:layout_height="50px"
        android:orientation="vertical" android:layout_alignParentBottom="true"
        android:background="#535254">

        <Button android:id="@+id/btn_save" android:layout_width="wrap_content"
            android:layout_height="wrap_content" android:layout_alignParentRight="true"
            android:layout_marginTop="7dp" android:layout_marginRight="15dp"
            android:layout_below="@+id/edit_message"
            android:text = "Save" />

        <Button android:id="@+id/btn_cancel" android:layout_width="wrap_content"
            android:layout_height="wrap_content" android:layout_marginTop="7dp"
            android:layout_marginRight="10dp" android:layout_below="@+id/edit_message"
            android:layout_toLeftOf="@+id/btn_save" android:text = "Cancel" />

    </RelativeLayout>
</RelativeLayout>

enter image description here

I vous voulez les 2 boutons du bas devraient aller vers le haut quand le softkeyboard vient dans l'image.

enter image description here

148
demandé sur Vineet Shukla 2011-09-14 17:30:06

23 réponses

basé sur la solution de contournement de yghm, j'ai codé une classe de convenance qui me permet de résoudre le problème avec une doublure (après avoir ajouté la nouvelle classe à mon code source bien sûr). La doublure unique est:

     AndroidBug5497Workaround.assistActivity(this);

et la classe d'application est:


public class AndroidBug5497Workaround {

    // For more information, see https://issuetracker.google.com/issues/36911528
    // To use this class, simply invoke assistActivity() on an Activity that already has its content view set.

    public static void assistActivity (Activity activity) {
        new AndroidBug5497Workaround(activity);
    }

    private View mChildOfContent;
    private int usableHeightPrevious;
    private FrameLayout.LayoutParams frameLayoutParams;

    private AndroidBug5497Workaround(Activity activity) {
        FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content);
        mChildOfContent = content.getChildAt(0);
        mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            public void onGlobalLayout() {
                possiblyResizeChildOfContent();
            }
        });
        frameLayoutParams = (FrameLayout.LayoutParams) mChildOfContent.getLayoutParams();
    }

    private void possiblyResizeChildOfContent() {
        int usableHeightNow = computeUsableHeight();
        if (usableHeightNow != usableHeightPrevious) {
            int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight();
            int heightDifference = usableHeightSansKeyboard - usableHeightNow;
            if (heightDifference > (usableHeightSansKeyboard/4)) {
                // keyboard probably just became visible
                frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
            } else {
                // keyboard probably just became hidden
                frameLayoutParams.height = usableHeightSansKeyboard;
            }
            mChildOfContent.requestLayout();
            usableHeightPrevious = usableHeightNow;
        }
    }

    private int computeUsableHeight() {
        Rect r = new Rect();
        mChildOfContent.getWindowVisibleDisplayFrame(r);
        return (r.bottom - r.top);
    }
}

J'espère que ça aidera quelqu'un.

222
répondu Joseph Johnson 2017-09-10 14:50:37

comme la réponse a déjà été choisie et que le problème est connu comme étant un bogue, j'ai pensé ajouter un"travail possible autour".

vous pouvez basculer en mode plein écran lorsque le clavier doux est affiché. Cela permet au "adjustPan" de fonctionner correctement.

en d'autres termes, j'utilise toujours @android:style/Thème.Noir.NoTitleBar.Plein écran dans le cadre du thème d'application et stateVisible / adjustersize dans le cadre du mode d'entrée soft de la fenêtre d'activité mais pour les faire fonctionner ensemble, je dois basculer en mode plein écran avant que le clavier se lève.

utiliser le Code suivant:

désactiver le mode plein écran

getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);

mode plein écran

getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);

Note-l'inspiration vient de: Cacher le titre en mode plein écran

34
répondu LEO 2017-05-23 11:54:59

j'ai essayé la solution de Joseph Johnson , mais comme d'autres, j'ai rencontré le problème de l'écart entre le contenu et le clavier. Le problème vient du fait que le mode d'entrée soft est toujours pan lorsqu'on utilise le mode plein écran. Ce panoramique interfère avec la solution de Joseph lorsque vous activez un champ d'entrée qui serait caché par l'entrée douce.

lorsque l'entrée soft apparaît, le contenu est d'abord panoramique sur la base de son original taille, puis redimensionné par la disposition demandée par la solution de Joseph. Le redimensionnement et la mise en page subséquente ne défont pas le panoramique, ce qui entraîne l'écart. L'ordre complet des événements est:

  1. mise en page Globale listener
  2. Panoramique
  3. mise en page du contenu (=redimensionnement réel du contenu)

il n'est pas possible de désactiver le panning, mais il est possible de forcer le pan offset à être 0 en changeant la hauteur du contenu. Cela peut être fait dans l'écouteur, parce qu'il est exécuté avant le panoramique. Le réglage de la hauteur du contenu à la hauteur Disponible permet une expérience utilisateur en douceur, c'est-à-dire sans scintillement.

j'ai aussi fait ces changements. Si l'un d'entre eux présente des problèmes, faites-le moi savoir:

    "1519120920 Mis" détermination de la hauteur disponible pour une utilisation getWindowVisibleDisplayFrame . Le Rect est mis en cache pour empêcher un peu de ordures.
  • permet également de supprimer l'auditeur. Ceci est utile lorsque vous réutilisez une activité pour différents fragments ayant des exigences différentes en plein écran.
  • ne pas faire de distinction entre clavier affiché ou caché, mais toujours régler la hauteur du contenu à la hauteur visible du cadre d'affichage.

il a été testé sur un Nexus 5, et des émulateurs fonctionnant API niveaux 16-24 avec des tailles d'écran allant de minuscules à grands.

le code a été porté sur Kotlin, mais le portage de mes modifications sur Java est simple. Laissez-moi savoir si vous avez besoin d'aide:

class AndroidBug5497Workaround constructor(activity: Activity) {
    private val contentContainer = activity.findViewById(android.R.id.content) as ViewGroup
    private val rootView = contentContainer.getChildAt(0)
    private val rootViewLayout = rootView.layoutParams as FrameLayout.LayoutParams
    private val viewTreeObserver = rootView.viewTreeObserver
    private val listener = { possiblyResizeChildOfContent() }

    private val contentAreaOfWindowBounds = Rect()
    private var usableHeightPrevious = 0

    // I call this in "onResume()" of my fragment
    fun addListener() {
        viewTreeObserver.addOnGlobalLayoutListener(listener)
    }

    // I call this in "onPause()" of my fragment
    fun removeListener() {
        viewTreeObserver.removeOnGlobalLayoutListener(listener)
    }

    private fun possiblyResizeChildOfContent() {
        contentContainer.getWindowVisibleDisplayFrame(contentAreaOfWindowBounds)
        val usableHeightNow = contentAreaOfWindowBounds.height()
        if (usableHeightNow != usableHeightPrevious) {
            rootViewLayout.height = usableHeightNow
            // Change the bounds of the root view to prevent gap between keyboard and content, and top of content positioned above top screen edge.
            rootView.layout(contentAreaOfWindowBounds.left, contentAreaOfWindowBounds.top, contentAreaOfWindowBounds.right, contentAreaOfWindowBounds.bottom)
            rootView.requestLayout()

            usableHeightPrevious = usableHeightNow
        }
    }
}
13
répondu Johan Stuyts 2017-05-23 11:54:59

j'ai dû faire face à ce problème aussi et j'ai eu un travail autour duquel j'ai vérifié sur HTC one, galaxy s1, s2, s3, note et sensation HTC.

placez un écouteur de layout global sur la vue racine de votre layout

mRootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener(){
            public void onGlobalLayout() {
                checkHeightDifference();
            }
    });

et là j'ai vérifié la différence de hauteur et si la différence de hauteur de l'écran est plus grand qu'un tiers sur la hauteur de l'écran alors nous pouvons supposer que le clavier est ouvert. extrait de cette réponse .

private void checkHeightDifference(){
    // get screen frame rectangle 
    Rect r = new Rect();
    mRootView.getWindowVisibleDisplayFrame(r);
    // get screen height
    int screenHeight = mRootView.getRootView().getHeight();
    // calculate the height difference
    int heightDifference = screenHeight - (r.bottom - r.top);

    // if height difference is different then the last height difference and
    // is bigger then a third of the screen we can assume the keyboard is open
    if (heightDifference > screenHeight/3 && heightDifference != mLastHeightDifferece) {
        // keyboard visiblevisible
        // get root view layout params
        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mRootView.getLayoutParams();
        // set the root view height to screen height minus the height difference
        lp.height = screenHeight - heightDifference;
        // call request layout so the changes will take affect
        .requestLayout();
        // save the height difference so we will run this code only when a change occurs.
        mLastHeightDifferece = heightDifference;
    } else if (heightDifference != mLastHeightDifferece) {
        // keyboard hidden
        PFLog.d("[ChatroomActivity] checkHeightDifference keyboard hidden");
        // get root view layout params and reset all the changes we have made when the keyboard opened.
        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mRootView.getLayoutParams();
        lp.height = screenHeight;
        // call request layout so the changes will take affect
        mRootView.requestLayout();
        // save the height difference so we will run this code only when a change occurs.
        mLastHeightDifferece = heightDifference;
    }
}

ce n'est probablement pas une preuve par balle et peut-être sur certains appareils, il ne fonctionnera pas, mais il a fonctionné pour moi et l'espoir qu'il vous aidera aussi.

8
répondu yghm 2017-05-23 12:34:47

veuillez noter que android:windowSoftInputMode="adjustResize" ne fonctionne pas lorsque WindowManager.LayoutParams.FLAG_FULLSCREEN est défini pour une activité. vous avez deux options.

  1. soit désactiver le mode plein écran pour votre activité. L'activité n'est pas redimensionnée en mode plein écran. Vous pouvez le faire en xml (en changeant le thème de l'activité) ou en code Java. Ajoutez les lignes suivantes dans votre méthode onCreate ().

    getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);   
    getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);`
    

ou

  1. utilisez une autre façon d'obtenir le mode plein écran. Ajoutez le code suivant dans votre méthode onCreate ().

    getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
    getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
    getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
    View decorView = getWindow().getDecorView();
    // Hide the status bar.
    int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN;
    decorView.setSystemUiVisibility(uiOptions);`
    

veuillez noter que la méthode-2 ne fonctionne qu'avec Android 4.1 et au-dessus.

8
répondu Abhinav Chauhan 2017-01-31 05:40:53

J'ai mis en œuvre la solution Joseph Johnson et ça a bien fonctionné, j'ai remarqué après avoir utilisé cette solution que parfois le tiroir de l'application ne se ferme pas correctement. J'ai ajouté une fonctionnalité pour supprimer l'écouteur removeOnGlobalLayoutListener lorsque l'utilisateur ferme le fragment où se trouvent les textes édités.

    //when the application uses full screen theme and the keyboard is shown the content not scrollable! 
//with this util it will be scrollable once again
//http://stackoverflow.com/questions/7417123/android-how-to-adjust-layout-in-full-screen-mode-when-softkeyboard-is-visible
public class AndroidBug5497Workaround {


    private static AndroidBug5497Workaround mInstance = null;
    private View mChildOfContent;
    private int usableHeightPrevious;
    private FrameLayout.LayoutParams frameLayoutParams;
    private ViewTreeObserver.OnGlobalLayoutListener _globalListener;

    // For more information, see https://code.google.com/p/android/issues/detail?id=5497
    // To use this class, simply invoke assistActivity() on an Activity that already has its content view set.

    public static AndroidBug5497Workaround getInstance (Activity activity) {
        if(mInstance==null)
        {
            synchronized (AndroidBug5497Workaround.class)
            {
                mInstance = new AndroidBug5497Workaround(activity);
            }
        }
        return mInstance;
    }

    private AndroidBug5497Workaround(Activity activity) {
        FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content);
        mChildOfContent = content.getChildAt(0);
        frameLayoutParams = (FrameLayout.LayoutParams) mChildOfContent.getLayoutParams();

        _globalListener = new ViewTreeObserver.OnGlobalLayoutListener()
        {

            @Override
            public void onGlobalLayout()
            {
                 possiblyResizeChildOfContent();
            }
        };
    }

    public void setListener()
    {
         mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(_globalListener);
    }

    public void removeListener()
    {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            mChildOfContent.getViewTreeObserver().removeOnGlobalLayoutListener(_globalListener);
        } else {
            mChildOfContent.getViewTreeObserver().removeGlobalOnLayoutListener(_globalListener);
        }
    }

    private void possiblyResizeChildOfContent() {
        int usableHeightNow = computeUsableHeight();
        if (usableHeightNow != usableHeightPrevious) {
            int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight();
            int heightDifference = usableHeightSansKeyboard - usableHeightNow;
            if (heightDifference > (usableHeightSansKeyboard/4)) {
                // keyboard probably just became visible
                frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
            } else {
                // keyboard probably just became hidden
                frameLayoutParams.height = usableHeightSansKeyboard;
            }
            mChildOfContent.requestLayout();
            usableHeightPrevious = usableHeightNow;
        }
    }

    private int computeUsableHeight() {
        Rect r = new Rect();
        mChildOfContent.getWindowVisibleDisplayFrame(r);
        return (r.bottom - r.top);
    } 
}

utilise la classe où se trouve mon edittexts

@Override
public void onStart()
{
    super.onStart();
    AndroidBug5497Workaround.getInstance(getActivity()).setListener();
}

@Override
public void onStop()
{
    super.onStop();
    AndroidBug5497Workaround.getInstance(getActivity()).removeListener();
}
6
répondu visionix visionix 2016-09-16 09:39:13

je viens de trouver une solution simple et fiable si vous utilisez l'approche D'UI de système ( https://developer.android.com/training/system-ui/immersive.html ).

cela fonctionne dans le cas où vous utilisez View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN , p.ex. si vous utilisez CoordinatorLayout .

il ne fonctionnera pas pour WindowManager.LayoutParams.FLAG_FULLSCREEN (celui que vous pouvez également mettre dans le thème avec android:windowFullscreen ), mais vous pouvez obtenir un effet similaire avec SYSTEM_UI_FLAG_LAYOUT_STABLE (qui "a le même effet visuel " selon le docs ) et cette solution devrait fonctionner à nouveau.

getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN
                    | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION /* If you want to hide navigation */
                    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE)

Je l'ai testé sur mon appareil à Marshmallow.

la clé est que les claviers soft sont également l'une des fenêtres du système (comme la barre d'état et la barre de navigation), de sorte que le WindowInsets expédié par le système contient des informations précises et fiables à ce sujet.

Pour le cas d'utilisation comme dans DrawerLayout là où nous essayons de dessiner derrière la barre d'état, nous pouvons créer une mise en page qui ignore seulement l'inset supérieur, et applique l'inset inférieur qui rend compte du clavier doux.

voici ma coutume FrameLayout :

/**
 * Implements an effect similar to {@code android:fitsSystemWindows="true"} on Lollipop or higher,
 * except ignoring the top system window inset. {@code android:fitsSystemWindows="true"} does not
 * and should not be set on this layout.
 */
public class FitsSystemWindowsExceptTopFrameLayout extends FrameLayout {

    public FitsSystemWindowsExceptTopFrameLayout(Context context) {
        super(context);
    }

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

    public FitsSystemWindowsExceptTopFrameLayout(Context context, AttributeSet attrs,
                                                 int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    public FitsSystemWindowsExceptTopFrameLayout(Context context, AttributeSet attrs,
                                                 int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            setPadding(insets.getSystemWindowInsetLeft(), 0, insets.getSystemWindowInsetRight(),
                    insets.getSystemWindowInsetBottom());
            return insets.replaceSystemWindowInsets(0, insets.getSystemWindowInsetTop(), 0, 0);
        } else {
            return super.onApplyWindowInsets(insets);
        }
    }
}

et de l'utiliser:

<com.example.yourapplication.FitsSystemWindowsExceptTopFrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- Your original layout here -->
</com.example.yourapplication.FitsSystemWindowsExceptTopFrameLayout>

cela devrait théoriquement fonctionner pour n'importe quel appareil sans modification insensée, beaucoup mieux que n'importe quel piratage qui essaie de prendre un hasard 1/3 ou 1/4 de la taille de l'écran comme référence.

(il nécessite API 16+, mais j'utilise fullscreen seulement sur Lollipop+ pour le dessin derrière la barre d'état de sorte que c'est la meilleure solution dans ce cas.)

6
répondu Dreaming in Code 2017-02-19 10:16:13

pour le faire fonctionner avec FullScreen:

utilisez le plugin clavier ionique. Cela vous permet d'écouter lorsque le clavier apparaît et disparaît.

OnDeviceReady ajouter ces écouteurs d'événements:

// Allow Screen to Move Up when Keyboard is Present
window.addEventListener('native.keyboardshow', onKeyboardShow);
// Reset Screen after Keyboard hides
window.addEventListener('native.keyboardhide', onKeyboardHide);

La Logique:

function onKeyboardShow(e) {
    // Get Focused Element
    var thisElement = $(':focus');
    // Get input size
    var i = thisElement.height();
    // Get Window Height
    var h = $(window).height()
    // Get Keyboard Height
    var kH = e.keyboardHeight
    // Get Focused Element Top Offset
    var eH = thisElement.offset().top;
    // Top of Input should still be visible (30 = Fixed Header)
    var vS = h - kH;
    i = i > vS ? (vS - 30) : i;
    // Get Difference
    var diff = (vS - eH - i);
    if (diff < 0) {
        var parent = $('.myOuter-xs.myOuter-md');
        // Add Padding
        var marginTop = parseInt(parent.css('marginTop')) + diff - 25;
        parent.css('marginTop', marginTop + 'px');
    }
}

function onKeyboardHide(e) {
  // Remove All Style Attributes from Parent Div
  $('.myOuter-xs.myOuter-md').removeAttr('style');
}

en gros, si la différence est moins, c'est la quantité de pixels que le clavier couvre de votre entrée. Donc, si vous ajustez votre div parent par cela devrait le contrer.

ajouter des temps morts à la logique dit 300ms devrait également optimiser les performances (car cela permettra au clavier temps d'apparaître.

5
répondu tyler_mitchell 2015-09-11 10:40:45

n'utilisez android:windowSoftInputMode="adjustResize|stateHidden que si vous utilisez AdjustPan puis il désactiver la propriété redimensionnement

4
répondu Azhar Shaikh 2011-09-20 06:35:29

j'ai essayé la classe de Joseph Johnson, et ça a marché, mais ça n'a pas tout à fait répondu à mes besoins. Plutôt que d'émuler android:windowSoftInputMode="adjustResize", je devais émuler android:windowSoftInputMode="adjustPan".

j'utilise ceci pour un webview plein écran. Pour parcourir la vue de contenu à la bonne position, j'ai besoin d'utiliser une interface javascript qui fournit des détails sur la position de l'élément de page qui a la mise au point et reçoit ainsi l'entrée de clavier. J'ai omis ces détails, mais fourni ma réécriture de la classe de Joseph Johnson. Il fournira une base très solide pour vous de mettre en œuvre un panoramique contre son redimensionnement.

package some.package.name;

import some.package.name.JavaScriptObject;

import android.app.Activity;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;

//-------------------------------------------------------
// ActivityPanner Class
//
// Convenience class to handle Activity attributes bug.
// Use this class instead of windowSoftInputMode="adjustPan".
//
// To implement, call enable() and pass a reference
// to an Activity which already has its content view set.
// Example:
//      setContentView( R.layout.someview );
//      ActivityPanner.enable( this );
//-------------------------------------------------------
//
// Notes:
//
// The standard method for handling screen panning
// when the virtual keyboard appears is to set an activity
// attribute in the manifest.
// Example:
// <activity
//      ...
//      android:windowSoftInputMode="adjustPan"
//      ... >
// Unfortunately, this is ignored when using the fullscreen attribute:
//      android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
//
//-------------------------------------------------------
public class ActivityPanner {

    private View contentView_;
    private int priorVisibleHeight_;

    public static void enable( Activity activity ) {
        new ActivityPanner( activity );
    }

    private ActivityPanner( Activity activity ) {
        FrameLayout content = (FrameLayout)
            activity.findViewById( android.R.id.content );
        contentView_ = content.getChildAt( 0 );
        contentView_.getViewTreeObserver().addOnGlobalLayoutListener(
            new ViewTreeObserver.OnGlobalLayoutListener() {
                public void onGlobalLayout() { panAsNeeded(); }
        });
    }

    private void panAsNeeded() {

        // Get current visible height
        int currentVisibleHeight = visibleHeight();

        // Determine if visible height changed
        if( currentVisibleHeight != priorVisibleHeight_ ) {

            // Determine if keyboard visiblity changed
            int screenHeight =
                contentView_.getRootView().getHeight();
            int coveredHeight =
                screenHeight - currentVisibleHeight;
            if( coveredHeight > (screenHeight/4) ) {
                // Keyboard probably just became visible

                // Get the current focus elements top & bottom
                // using a ratio to convert the values
                // to the native scale.
                float ratio = (float) screenHeight / viewPortHeight();
                int elTop = focusElementTop( ratio );
                int elBottom = focusElementBottom( ratio );

                // Determine the amount of the focus element covered
                // by the keyboard
                int elPixelsCovered = elBottom - currentVisibleHeight;

                // If any amount is covered
                if( elPixelsCovered > 0 ) {

                    // Pan by the amount of coverage
                    int panUpPixels = elPixelsCovered;

                    // Prevent panning so much the top of the element
                    // becomes hidden
                    panUpPixels = ( panUpPixels > elTop ?
                                    elTop : panUpPixels );

                    // Prevent panning more than the keyboard height
                    // (which produces an empty gap in the screen)
                    panUpPixels = ( panUpPixels > coveredHeight ?
                                    coveredHeight : panUpPixels );

                    // Pan up
                    contentView_.setY( -panUpPixels );
                }
            }
            else {
                // Keyboard probably just became hidden

                // Reset pan
                contentView_.setY( 0 );
            }

            // Save usabale height for the next comparison
            priorVisibleHeight_ = currentVisibleHeight;
        }
    }

    private int visibleHeight() {
        Rect r = new Rect();
        contentView_.getWindowVisibleDisplayFrame( r );
        return r.bottom - r.top;
    }

    // Customize this as needed...
    private int viewPortHeight() { return JavaScriptObject.viewPortHeight(); }
    private int focusElementTop( final float ratio ) {
        return (int) (ratio * JavaScriptObject.focusElementTop());
    }
    private int focusElementBottom( final float ratio ) {
        return (int) (ratio * JavaScriptObject.focusElementBottom());
    }

}
3
répondu BuvinJ 2014-08-06 15:58:48

en effet, l'apparence douce du clavier ne semble pas affecter le Activity de quelque manière que ce soit, peu importe ce que windowSoftInputMode je sélectionne dans le mode FullScreen .

bien que je n'ai pas pu trouver beaucoup de documentation sur cette propriété, je pense que le mode FullScreen a été conçu pour l'application de jeu qui ne nécessitent pas beaucoup d'utilisation du clavier souple. Si votre activité nécessite une interaction avec l'utilisateur par le biais d'un clavier souple, veuillez reconsidérer l'utilisation d'un non-Plein écran thème. Vous pouvez désactiver la barre de titre en utilisant un thème NoTitleBar . Pourquoi vouloir cacher la barre de notification?

2
répondu Arnab Chakraborty 2011-09-26 04:50:25

continue comme android:windowSoftInputMode="adjustResize" . Parce qu'il est indiqué de ne garder qu'un seul de "adjustResize" et "adjustPan" (le mode de réglage de la fenêtre est spécifié soit avec adjustResize soit avec adjustPan. Il est fortement recommandé que vous spécifiez toujours l'un ou l'autre). Vous pouvez le retrouver ici: http://developer.android.com/resources/articles/on-screen-inputs.html

ça marche parfaitement pour moi.

2
répondu Balaji Khadake 2015-08-24 02:43:32

ajouter android:fitsSystemWindows="true" à la mise en page, et cette mise en page se redimensionnera.

2
répondu zayn 2017-10-20 05:25:32

vous voulez que la barre du bas colle au bas de la vue, mais quand le clavier est affiché, ils devraient se déplacer vers le haut pour être placé au-dessus du clavier, Non?

vous pouvez essayer ce code snippet:

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    ...>

    <RelativeLayout
        android:id="@+id/RelativeLayoutTopBar"
    ...>
    </RelativeLayout>

    <LinearLayout
        android:id="@+id/LinearLayoutBottomBar"
        android:layout_alignParentBottom = true
        ...>
    </LinearLayout>

    <LinearLayout
    android:layout_width="fill_parent"
    android:layout_height="390dp"
    android:orientation="vertical" 
    android:layout_above="@+id/LinearLayoutBottomBar"
    android:layout_below="@+id/RelativeLayoutTopBar"> 

    <ScrollView 
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:layout_marginBottom="10dp"
        android:id="@+id/ScrollViewBackground">

            ...

        </ScrollView>
     </LinearLayout>
  </RelativeLayout>

le BottomBar restera collé au bas de la vue et la sortie linéaire contenant le ScrollView prendra ce qui reste de la vue après que la barre haut/bas et le clavier seront affichés. Laissez-moi savoir si cela fonctionne pour vous bien.

0
répondu banzai86 2011-09-20 09:39:45

Merci Joseph pour votre réponse. Toutefois, dans la méthode éventuellement resizechildofcontenu (), la portion

else {
            // keyboard probably just became hidden
            frameLayoutParams.height = usableHeightSansKeyboard;
        }

ne fonctionnait pas pour moi, car la partie inférieure de la vue est devenue cachée. J'ai donc dû prendre une variable globale restoreHeight, et dans le constructeur, j'ai inséré la dernière ligne

restoreHeight = frameLayoutParams.height;

et puis j'ai remplacé la première partie mentionnée par

else {
            // keyboard probably just became hidden
            frameLayoutParams.height = restoreHeight;
        }

mais je ne sais pas pourquoi votre code ne fonctionne pas pour moi. Il serait d'une grande aide, si quelqu'un peut jeter la lumière sur cette.

0
répondu Debanjan 2016-01-27 09:09:55

j'utilisais seulement le mode plein écran pour cacher la barre d'état. Cependant, je veux que l'application redimensionne lorsque le clavier est affiché. Toutes les autres solutions (probablement en raison de l'âge de la post) étaient compliquées ou pas possibles pour mon utilisation (je veux éviter de changer le code Java pour sack of PhoneGap Build).

au lieu d'utiliser le plein écran, j'ai modifié mon configure pour Android pour qu'il ne soit pas en plein écran:

            <preference name="fullscreen" value="false" />

et a ajouté le cordova-plugin-statusbar , via la ligne de commande:

cordova plugin add cordova-plugin-statusbar

quand app a chargé, j'appelle simplement une méthode sur le plugin pour se cacher, comme:

    if (window.cordova && window.cordova.platformId == 'android' && window.StatusBar)
        window.StatusBar.hide();

Cela fonctionne comme un charme. Seul inconvénient réel est que la barre d'état est bréifly visible pendant que l'application charge. Pour mes besoins, ce n'était pas un problème.

0
répondu raider33 2016-03-25 19:14:42

j'ai essayé toutes les réponses possibles de stackOverflow, finalement j'ai résolu après une semaine de recherche. J'ai utilisé la disposition des coordonnées et j'ai changé cela avec linearLayout et mon problème est corrigé. Je ne sais pas si la disposition des coordonnées a des bugs ou quoi que ce soit de mon erreur.

0
répondu yubaraj poudel 2016-06-26 15:23:18

dans mon cas, ce problème a commencé à se produire une fois que j'ai ajouté le passage pour piétons à mon application Cordova. Mon application n'est pas utilisée en plein écran et android:windowSoftInputMode="adjustPan".

j'avais déjà le plugin de clavier ionique dans l'application, donc la détection si le clavier était haut ou bas était facile grâce à elle:

// Listen for events to when the keyboard is opened and closed
window.addEventListener("native.keyboardshow", keyboardUp, false);
window.addEventListener('native.keyboardhide', keyboardDown, false);

function keyboardUp()
{
    $('html').addClass('keyboardUp');
}

function keyboardDown()
{
    $('html').removeClass('keyboardUp');
}

j'ai essayé toutes les corrections ci-dessus mais la ligne simple qui a fini par le faire pour moi était ce morceau de css:

&.keyboardUp {
        overflow-y: scroll;
}

J'espère que ça vous évitera les quelques jours que j'ai passés là-dessus. :)

0
répondu J R 2016-07-06 22:17:56

J'ai utilisé Joseph Johnson créé Androidbug5497 workaround class mais obtenir l'espace noir entre softkeyboard et la vue. J'ai fait référence à ce lien Greg Ennis . Après avoir apporté quelques modifications à ce qui précède, voici mon code de travail final.

 public class SignUpActivity extends Activity {

 private RelativeLayout rlRootView; // this is my root layout
 private View rootView;
 private ViewGroup contentContainer;
 private ViewTreeObserver viewTreeObserver;
 private ViewTreeObserver.OnGlobalLayoutListener listener;
 private Rect contentAreaOfWindowBounds = new Rect();
 private FrameLayout.LayoutParams rootViewLayout;
 private int usableHeightPrevious = 0;

 private View mDecorView;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_sign_up);
  mDecorView = getWindow().getDecorView();
  contentContainer =
   (ViewGroup) this.findViewById(android.R.id.content);

  listener = new OnGlobalLayoutListener() {
   @Override
   public void onGlobalLayout() {
    possiblyResizeChildOfContent();
   }
  };

  rootView = contentContainer.getChildAt(0);
  rootViewLayout = (FrameLayout.LayoutParams)
  rootView.getLayoutParams();

  rlRootView = (RelativeLayout) findViewById(R.id.rlRootView);


  rlRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
   @Override
   public void onGlobalLayout() {
    int heightDiff = rlRootView.getRootView().getHeight() - rlRootView.getHeight();
    if (heightDiff > Util.dpToPx(SignUpActivity.this, 200)) {
     // if more than 200 dp, it's probably a keyboard...
     //  Logger.info("Soft Key Board ", "Key board is open");

    } else {
     Logger.info("Soft Key Board ", "Key board is CLOSED");

     hideSystemUI();
    }
   }
  });
 }

 // This snippet hides the system bars.
 protected void hideSystemUI() {
  // Set the IMMERSIVE flag.
  // Set the content to appear under the system bars so that the 
  content
  // doesn't resize when the system bars hide and show.
  mDecorView.setSystemUiVisibility(
   View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
 }
 @Override
 protected void onPause() {
  super.onPause();
  if (viewTreeObserver.isAlive()) {
   viewTreeObserver.removeOnGlobalLayoutListener(listener);
  }
 }

 @Override
 protected void onResume() {
  super.onResume();
  if (viewTreeObserver == null || !viewTreeObserver.isAlive()) {
   viewTreeObserver = rootView.getViewTreeObserver();
  }
  viewTreeObserver.addOnGlobalLayoutListener(listener);
 }

 @Override
 protected void onDestroy() {
  super.onDestroy();
  rootView = null;
  contentContainer = null;
  viewTreeObserver = null;
 }
 private void possiblyResizeChildOfContent() {
  contentContainer.getWindowVisibleDisplayFrame(contentAreaOfWindowBounds);

  int usableHeightNow = contentAreaOfWindowBounds.height();

  if (usableHeightNow != usableHeightPrevious) {
   rootViewLayout.height = usableHeightNow;
   rootView.layout(contentAreaOfWindowBounds.left,
    contentAreaOfWindowBounds.top, contentAreaOfWindowBounds.right, contentAreaOfWindowBounds.bottom);
   rootView.requestLayout();

   usableHeightPrevious = usableHeightNow;
  } else {

   this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
  }
 }
}
0
répondu Venkat 2017-05-29 12:02:51

j'ai essayé plusieurs solutions dont celles de Joseph Johnson et Johan Stuyts. Mais en conséquence j'ai obtenu un espace blanc entre le contenu et le clavier sur certains appareils (comme Lenovo s820) dans tous les cas. Donc j'ai fait quelques changements à leurs codes et finalement j'ai trouvé une solution qui fonctionne.

mon idée est basée sur l'ajout d'une marge en haut du contenu lorsque le clavier est affiché.

contentContainer.getWindowVisibleDisplayFrame(contentAreaOfWindowBounds);
    int usableHeightNow = contentAreaOfWindowBounds.height();

    if (usableHeightNow != usableHeightPrevious) {

        int difference = usableHeightNow - usableHeightPrevious;

        if (difference < 0 && difference < -150) {
            keyboardShowed = true;
            rootViewLayout.topMargin -= difference + 30;
            rootViewLayout.bottomMargin += 30;
        }
        else if (difference < 0 && difference > -150){
            rootViewLayout.topMargin -= difference + 30;
        }
        else if (difference > 0 && difference > 150) {
            keyboardShowed = false;
            rootViewLayout.topMargin = 0;
            rootViewLayout.bottomMargin = 0;
        }

        rootView.requestLayout();

        Log.e("Bug Workaround", "Difference: " + difference);

        usableHeightPrevious = usableHeightNow;
}

comme vous pouvez le voir, j'ajoute 30 px à la différence parce qu'il y a un petit espace blanc entre le haut de l'écran et la zone de contenu avec marge. Et je ne sais pas d'où ça vient donc j'ai décidé de réduire les marges et maintenant ça fonctionne exactement comme j'en avais besoin.

0
répondu IDmikael 2017-12-06 11:27:27

j'utilise actuellement cette approche et cela fonctionne comme un charme. Le truc est que nous obtenons hauteur de clavier de différentes méthodes sur 21 ci-dessus et ci-dessous et puis l'utiliser comme le rembourrage inférieur de notre vue de racine dans notre activité. J'ai supposé que votre mise en page n'a pas besoin d'un rembourrage supérieur (va sous la barre de statut) mais dans le cas où vous le faites, m'informer de mettre à jour ma réponse.

activité principale.java

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        RelativeLayout mainLayout = findViewById(R.id.main_layout);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            ViewCompat.setOnApplyWindowInsetsListener(mainLayout , new OnApplyWindowInsetsListener() {
                @Override
                public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {
                    v.setPadding(0, 0, 0, insets.getSystemWindowInsetBottom());
                    return insets;
                }
            });
        } else {
            View decorView = getWindow().getDecorView();
            final View contentView = mainLayout;
            decorView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                @Override
                public void onGlobalLayout() {
                    Rect r = new Rect();
                    //r will be populated with the coordinates of your view that area still visible.
                    decorView.getWindowVisibleDisplayFrame(r);

                    //get screen height and calculate the difference with the useable area from the r
                    int height = decorView.getContext().getResources().getDisplayMetrics().heightPixels;
                    int diff = height - r.bottom;

                    //if it could be a keyboard add the padding to the view
                    if (diff != 0) {
                        // if the use-able screen height differs from the total screen height we assume that it shows a keyboard now
                        //check if the padding is 0 (if yes set the padding for the keyboard)
                        if (contentView.getPaddingBottom() != diff) {
                            //set the padding of the contentView for the keyboard
                            contentView.setPadding(0, 0, 0, diff);
                        }
                    } else {
                        //check if the padding is != 0 (if yes reset the padding)
                        if (contentView.getPaddingBottom() != 0) {
                            //reset the padding of the contentView
                            contentView.setPadding(0, 0, 0, 0);
                        }
                    }
                }
            });
        }
    }
...
}

Ne pas oubliez pas de répondre à votre vue de la racine avec un id:

activity_main.xml

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/main_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

J'espère que ça aidera quelqu'un.

0
répondu Sdghasemi 2018-10-03 08:31:34

basé sur https://stackoverflow.com/a/19494006/1815624 et le désir d'y arriver...

"151950920 mis" idée


combinant les réponses de

Code correspondant:

        if (heightDifference > (usableHeightSansKeyboard / 4)) {

            // keyboard probably just became visible
            frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
            activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
            activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
        } else {

            // keyboard probably just became hidden
            if(usableHeightPrevious != 0) {
                frameLayoutParams.height = usableHeightSansKeyboard;
                activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
                activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);

            }

source complète à https://github.com/CrandellWS/AndroidBug5497Workaround/blob/master/AndroidBug5497Workaround.java

vieille idée

créer une valeur statique de la hauteur des conteneurs avant d'ouvrir le clavier Réglez la hauteur du conteneur sur usableHeightSansKeyboard - heightDifference lorsque le clavier s'ouvre et ramenez-la à la valeur enregistrée lorsqu'il se ferme

if (heightDifference > (usableHeightSansKeyboard / 4)) {
                // keyboard probably just became visible
                frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
                int mStatusHeight = getStatusBarHeight();
                frameLayoutParams.topMargin = mStatusHeight;
                ((MainActivity)activity).setMyMainHeight(usableHeightSansKeyboard - heightDifference);

                if(BuildConfig.DEBUG){
                    Log.v("aBug5497", "keyboard probably just became visible");
                }
            } else {
                // keyboard probably just became hidden
                if(usableHeightPrevious != 0) {
                    frameLayoutParams.height = usableHeightSansKeyboard;
                    ((MainActivity)activity).setMyMainHeight();    
                }
                frameLayoutParams.topMargin = 0;

                if(BuildConfig.DEBUG){
                    Log.v("aBug5497", "keyboard probably just became hidden");
                }
            }

méthodes D'activité principale

public void setMyMainHeight(final int myMainHeight) {

    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            ConstraintLayout.LayoutParams rLparams =  (ConstraintLayout.LayoutParams) myContainer.getLayoutParams();
            rLparams.height = myMainHeight;

            myContainer.setLayoutParams(rLparams);
        }

    });

}

int mainHeight = 0;
public void setMyMainHeight() {

    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            ConstraintLayout.LayoutParams rLparams =  (ConstraintLayout.LayoutParams) myContainer.getLayoutParams();
            rLparams.height = mainHeight;

            myContainer.setLayoutParams(rLparams);
        }

    });

}

exemple de conteneur XML

<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
        <android.support.constraint.ConstraintLayout
            android:id="@+id/my_container"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            app:layout_constraintHeight_percent=".8">

de même, des marges peuvent être ajoutées si nécessaire...

une autre considération est l'utilisation de rembourrage un exemple de ce qui peut être trouvé à:

https://github.com/mikepenz/MaterialDrawer/issues/95#issuecomment-80519589

0
répondu CrandellWS 2018-10-05 14:45:15