Android'de yazılım klavyesinin görünürlüğü nasıl kontrol edilir?


516

Çok basit bir şey yapmam gerekiyor - yazılım klavyesinin gösterilip gösterilmediğini öğrenin. Android'de bu mümkün mü?


9
Reuben Scratton'ın cevabı iyi olmasına rağmen bir tablette kırılmış gibi görünüyor. Dif> 128 kontrol farkını diff> screenHeight / 3 ile değiştirdim.
Kingston

2
Reuben Scratton'ın cevabı iyiydi ama KaChi'nin bunu kullanmak için ayarlanmasını istedim.
Cullan

1
Google neden standart bir yerleşik yöntem tüm klavye uygulamaları için işe yaramıyor?
Meyve

4
Hala kardeşlerim, bu bir sistem işlevi değil ...
longi

1
Bu API'nın 10 yıl sonra hala eksik olması kesinlikle üzücü . Android'den uzaklaştığım için çok memnunum.
Reuben Scratton

Yanıtlar:


674

NEW ANSWER 25 Oca 2012 ekledi

Aşağıdaki cevabı yazdığından beri birisi beni ViewTreeObserver'ın varlığına işaret etti ve arkadaşlarının, SDK'da sürüm 1'den beri gizlenen API'ların .

Özel bir Düzen türü gerektirmek yerine, daha basit bir çözüm, etkinliğinizin kök görünümüne bilinen bir kimlik vermek, örneğin @+id/activityRootViewTreeObserver'a bir GlobalLayoutListener bağlamak ve oradan etkinliğinizin görünüm kökü ile pencere boyutu arasındaki fark farkını hesaplamaktır:

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 > dpToPx(this, 200)) { // if more than 200 dp, it's probably a keyboard...
            // ... do something here
        }
     }
});

Aşağıdaki gibi bir yardımcı program kullanma:

public static float dpToPx(Context context, float valueInDp) {
    DisplayMetrics metrics = context.getResources().getDisplayMetrics();
    return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, valueInDp, metrics);
}

Kolay!

Not: Uygulamanız Android Manifest'te bu bayrağı ayarlamalıdır, android:windowSoftInputMode="adjustResize"aksi takdirde yukarıdaki çözüm çalışmaz.

ORİJİNAL CEVAP

Evet bu mümkün, ancak olması gerekenden çok daha zor.

Klavyenin ne zaman göründüğüne ve kaybolduğuna dikkat etmem gerekiyorsa (ki bu oldukça sıktır) o zaman yaptığım şey üst düzey düzen sınıfımı geçersiz kılan bir sınıfta özelleştirmektir onMeasure(). Temel mantık, mizanpajın pencerenin toplam alanından önemli ölçüde daha az dolum bulması durumunda, yumuşak bir klavyenin muhtemelen gösteriliyor olmasıdır.

import android.app.Activity;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.widget.LinearLayout;

/*
 * LinearLayoutThatDetectsSoftKeyboard - a variant of LinearLayout that can detect when 
 * the soft keyboard is shown and hidden (something Android can't tell you, weirdly). 
 */

public class LinearLayoutThatDetectsSoftKeyboard extends LinearLayout {

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

    public interface Listener {
        public void onSoftKeyboardShown(boolean isShowing);
    }
    private Listener listener;
    public void setListener(Listener listener) {
        this.listener = listener;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int height = MeasureSpec.getSize(heightMeasureSpec);
        Activity activity = (Activity)getContext();
        Rect rect = new Rect();
        activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
        int statusBarHeight = rect.top;
        int screenHeight = activity.getWindowManager().getDefaultDisplay().getHeight();
        int diff = (screenHeight - statusBarHeight) - height;
        if (listener != null) {
            listener.onSoftKeyboardShown(diff>128); // assume all soft keyboards are at least 128 pixels high
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);       
    }

    }

Sonra Etkinlik sınıfınızda ...

public class MyActivity extends Activity implements LinearLayoutThatDetectsSoftKeyboard.Listener {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...
        LinearLayoutThatDetectsSoftKeyboard mainLayout = (LinearLayoutThatDetectsSoftKeyboard)findViewById(R.id.main);
        mainLayout.setListener(this);
        ...
    }


    @Override
    public void onSoftKeyboardShown(boolean isShowing) {
        // do whatever you need to do here
    }

    ...
}

62
Etkinliğinizde şu özelliği ayarlamanız gerektiğini fark edene kadar bu benim için çalışmadı: android: windowSoftInputMode = "adjustResize"
ajh158

9
Hile yapıyor gibi görünüyor. Ayrıca, kök görünümün kimliğini bilmiyorsanız, görünümü nasıl elde edeceğiniz aşağıda açıklanmıştır:((ViewGroup) findViewById(android.R.id.content)).getChildAt(0)
Goldsmith

8
Bunu gerçek kök görünümü ( android.R.id.content) kullanarak denerseniz System, uygulamanızın yüksekliğini değiştiren varlıktan ziyade daha güvenli olduğunu söyleyebilirsiniz . Android ekibinin bize bir mola vermesi ve SoftKeyboard girişi hakkında en azından temel şeyleri bize bildirmesi çok daha güvenli olurdu.
Graeme

14
heightDiffHer zaman eylem çubuğunun yüksekliğini içerecek şekilde dikkat edin . Bu yüksekliğin sabitten biraz daha büyük olup olmadığını test ederek yok sayılan yeni yanıtta, ancak Nexus 4 gibi xxhdpi cihazları için 100 piksel yeterli değildir. Bu hacky çalışmasını gerçekten kullanmak istiyorsanız bu değeri DP'lere dönüştürmeyi düşünün etrafında.
Paul Lammertsma

8
Uyarı: WindowManager.LayoutParams.FLAG_FULLSCREEN ve tam ekran teması ile çalışmıyor.
VAV

303

Umarım bu birisine yardım eder.

Reuben Scratton'un verdiği yeni cevap harika ve gerçekten etkilidir, ancak gerçekten sadece windowSoftInputMode öğenizi ResResize ayarlayacak şekilde ayarlarsanız çalışır. AdjustPan olarak ayarlarsanız, kod snippet'ini kullanarak klavyenin görünür olup olmadığını tespit etmek yine de mümkün değildir. Bu soruna geçici bir çözüm bulmak için yukarıdaki kodda bu küçük değişikliği yaptım.

final View activityRootView = findViewById(R.id.activityRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
    Rect r = new Rect();
    //r will be populated with the coordinates of your view that area still visible.
    activityRootView.getWindowVisibleDisplayFrame(r);

    int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
    if (heightDiff > 0.25*activityRootView.getRootView().getHeight()) { // if more than 25% of the screen, its probably a keyboard...
        ... do something here
    }
 }
}); 

1
Benim için çalışan bu. Yakınlaştırmada da olsa stackoverflow.com/a/5224088/530513TwoDScrollerView benzeri bir özel klavye durumunu algılamaya çalışıyordum . Çocuk basit değil, özel bir düzendi (genişliyor ), ancak ayarlara rağmen önerilen çözümü kullanarak klavyeyi algılayamadı . Teşekkürler! ImageViewRelativeLayoutandroid:windowSoftInputMode="adjustResize"
David O'Meara

1
Teşekkürler teşekkürler teşekkürler! adjustResize sadece uygulamam için uygun değil ve çözümünüz mükemmel çalıştı.
dwemthy

1
İle çalışır ActionBarve ActionBarSherlock. Çok teşekkürler! Bu arada, bir yöntem var r.height():)
Dmitry Zaytsev

1
Bu yanıtı bir şekilde işaretlemek için 23 saat içinde buraya bir ödül ekleyeceğim.
Dmitry Zaytsev

9
heightDiff > root.getRootView().getHeight() / 4yüksek çözünürlüklü cihazlarla çalışmak için iyi bir değerdir. 100 piksel kısadır. 1080x1920 res ile Nexus 5'te, 1920 - (996-75)>? 100 = 999 1920 - (1776-75)>? 100 = 219 // klavye galaxy s2'de 480x800 res, 800 - (800-38) ile çalışıyor>? 100 = 38800 - (410-38)>? 100 = 428 // klavye bitti, sihirli sayı 100px yeterince iyi değil.
Flask_KR

55

Bilgisayar açısından sonsuza dek sürdü, ancak bu soru hala inanılmaz derecede alakalı!

Bu yüzden yukarıdaki cevapları aldım ve onları biraz birleştirdim ve geliştirdim ...

public interface OnKeyboardVisibilityListener {


    void onVisibilityChanged(boolean visible);
}

public final void setKeyboardListener(final OnKeyboardVisibilityListener listener) {
    final View activityRootView = ((ViewGroup) getActivity().findViewById(android.R.id.content)).getChildAt(0);

    activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {

        private boolean wasOpened;

        private final int DefaultKeyboardDP = 100;

        // From @nathanielwolf answer...  Lollipop includes button bar in the root. Add height of button bar (48dp) to maxDiff
        private final int EstimatedKeyboardDP = DefaultKeyboardDP + (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? 48 : 0);

        private final Rect r = new Rect();

        @Override
        public void onGlobalLayout() {
            // Convert the dp to pixels.
            int estimatedKeyboardHeight = (int) TypedValue
                    .applyDimension(TypedValue.COMPLEX_UNIT_DIP, EstimatedKeyboardDP, activityRootView.getResources().getDisplayMetrics());

            // Conclude whether the keyboard is shown or not.
            activityRootView.getWindowVisibleDisplayFrame(r);
            int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
            boolean isShown = heightDiff >= estimatedKeyboardHeight;

            if (isShown == wasOpened) {
                Log.d("Keyboard state", "Ignoring global layout change...");
                return;
            }

            wasOpened = isShown;
            listener.onVisibilityChanged(isShown);
        }
    });
}

Benim için çalışıyor :)

NOT: Eğer fark ederseniz DefaultKeyboardDP cihazınıza uygun ve herkesin değerin ne olması gerektiğini bilmesi için bir yorum gönderirseniz ... sonunda tüm cihazlara uyacak doğru değeri elde ederiz!

Daha fazla ayrıntı için Cyborg'daki uygulamaya göz atın


2
+1 Çok teşekkür ederim !! Diğer cevapları deniyordum ama işe yaramıyorlar. Sonra seninkini buldum ve bir cazibe gibi çalışıyor. Harika kod! : D
Kevin van Mierlo

Bu yalnızca şunu eklerseniz çalışır: android: windowSoftInputMode = "stateHidden | adjustPan" veya android: windowSoftInputMode = "stateHidden | adjustResize" Teşekkürler !!!!
Lena Bru

Emin misiniz? Eğer bellek sunucuları, android de düzgün olayları aldım: windowSoftInputMode varsayılan değeri vardı ... işe yaramadı tek şey ekranın davranışı, bu yüzden elle
küçültmek

2
küçük bir düzeltme: private final int EstimatedKeyboardDP = DefaultKeyboardDP + (Build.VERSION.SDK_INT> = Build.VERSION_CODES.LOLLIPOP? 48: 0);
14:55

2
Mükemmel!! "WindowSoftInputMode", "adjustPan" / "adjustResize" / "adjustPan | stateHidden" / "adjustResize | stateHidden" olarak ayarlanırsa veya bu seçenek olmadan da her zaman çalışır! XiaoMi 8'de test edildi.
Zhou Hongbo

52

Geç cevap için özür dilerim, ancak dinleyicilere ve diğer faydalı şeylere bildirimde bulunarak açık / kapalı olayları ele almak için küçük bir yardımcı sınıf oluşturmuştum, birisi yararlı olabilir:

import android.graphics.Rect;
import android.view.View;
import android.view.ViewTreeObserver;

import java.util.LinkedList;
import java.util.List;

public class SoftKeyboardStateWatcher implements ViewTreeObserver.OnGlobalLayoutListener {

    public interface SoftKeyboardStateListener {
        void onSoftKeyboardOpened(int keyboardHeightInPx);
        void onSoftKeyboardClosed();
    }

    private final List<SoftKeyboardStateListener> listeners = new LinkedList<SoftKeyboardStateListener>();
    private final View activityRootView;
    private int        lastSoftKeyboardHeightInPx;
    private boolean    isSoftKeyboardOpened;

    public SoftKeyboardStateWatcher(View activityRootView) {
        this(activityRootView, false);
    }

    public SoftKeyboardStateWatcher(View activityRootView, boolean isSoftKeyboardOpened) {
        this.activityRootView     = activityRootView;
        this.isSoftKeyboardOpened = isSoftKeyboardOpened;
        activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(this);
    }

    @Override
    public void onGlobalLayout() {
        final Rect r = new Rect();
        //r will be populated with the coordinates of your view that area still visible.
        activityRootView.getWindowVisibleDisplayFrame(r);

        final int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
        if (!isSoftKeyboardOpened && heightDiff > 100) { // if more than 100 pixels, its probably a keyboard...
            isSoftKeyboardOpened = true;
            notifyOnSoftKeyboardOpened(heightDiff);
        } else if (isSoftKeyboardOpened && heightDiff < 100) {
            isSoftKeyboardOpened = false;
            notifyOnSoftKeyboardClosed();
        }
    }

    public void setIsSoftKeyboardOpened(boolean isSoftKeyboardOpened) {
        this.isSoftKeyboardOpened = isSoftKeyboardOpened;
    }

    public boolean isSoftKeyboardOpened() {
        return isSoftKeyboardOpened;
    }

    /**
     * Default value is zero {@code 0}.
     *
     * @return last saved keyboard height in px
     */
    public int getLastSoftKeyboardHeightInPx() {
        return lastSoftKeyboardHeightInPx;
    }

    public void addSoftKeyboardStateListener(SoftKeyboardStateListener listener) {
        listeners.add(listener);
    }

    public void removeSoftKeyboardStateListener(SoftKeyboardStateListener listener) {
        listeners.remove(listener);
    }

    private void notifyOnSoftKeyboardOpened(int keyboardHeightInPx) {
        this.lastSoftKeyboardHeightInPx = keyboardHeightInPx;

        for (SoftKeyboardStateListener listener : listeners) {
            if (listener != null) {
                listener.onSoftKeyboardOpened(keyboardHeightInPx);
            }
        }
    }

    private void notifyOnSoftKeyboardClosed() {
        for (SoftKeyboardStateListener listener : listeners) {
            if (listener != null) {
                listener.onSoftKeyboardClosed();
            }
        }
    }
}

Kullanım örneği:

final SoftKeyboardStateWatcher softKeyboardStateWatcher 
    = new SoftKeyboardStateWatcher(findViewById(R.id.activity_main_layout);

// Add listener
softKeyboardStateWatcher.addSoftKeyboardStateListener(...);
// then just handle callbacks

2
Sınıf küçük değil ama uygulama kesinlikle :). Teşekkürler bunu deneyeceğim :)
Atul O Holic

1
Bununla ilgili sorun yaşarsanız bana ping
atın

Yukarıdaki birçok örneği denedikten ve küçük sorunlarla karşılaştıktan sonra, bu, birçok farklı cihazda (xxhdpi dahil) benim için en uygun olanıydı. Artı kendi güzel yeniden kullanılabilir sınıf. Mono droidde kullanmak için dönüştürdüm.
kheit

Bazı klavyelerde, bazen, üst kısımda fazladan özel tuşlar bulunur (örneğin, tahmin edilen kelimeler). Görünüşe göre bunlar klavyenin kendisinin bir parçası değildir, çünkü bunu kullanmak getLastKeyboardHeightInPx()bu satırın yüksekliğini içermez. Bunu da dikkate almanın bir yolunu biliyor musunuz?
ygesher

Bu yalnızca klavye göründüğünde Mizanpaj yüksekliği değişikliğinden ödün vermeye hazırsanız çalışır. sağ?
M. Usman Khan

33

Yüksek yoğunluklu cihazlarda yazılım klavyesinin görünürlüğünü yanlış algılamaktan kaçınmak için bazı iyileştirmeler:

  1. Yükseklik farkı eşiği 128 piksel değil 128 dp olarak tanımlanmalıdır . Metrikler ve Izgara hakkında Google tasarım dokümanına bakın , 48 dp dokunmatik nesne için rahat boyut ve düğmeler için minimum 32 dp . Genel yazılım klavyesi 4 satır tuş düğmesi içermelidir, bu nedenle minimum klavye yüksekliği şu şekilde olmalıdır: 32 dp * 4 = 128 dp , yani eşik boyutu, aygıt yoğunluğunu çarparak piksellere aktarmalıdır. Xxxhdpi aygıtları (yoğunluk 4) için, yumuşak klavye yükseklik eşiği 128 * 4 = 512 piksel olmalıdır.

  2. Kök görünüm ve görünür alanı arasındaki yükseklik farkı:
    kök görünüm yüksekliği - durum çubuğu yüksekliği - görünür çerçeve yüksekliği = kök görünüm alt - görünür çerçeve alt, çünkü durum çubuğu yüksekliği kök görünümün görünür çerçevesinin üstüne eşittir.

    private final String TAG = "TextEditor";
    private TextView mTextEditor;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_editor);
        mTextEditor = (TextView) findViewById(R.id.text_editor);
        mTextEditor.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                isKeyboardShown(mTextEditor.getRootView());
            }
        });
    }
    
    private boolean isKeyboardShown(View rootView) {
        /* 128dp = 32dp * 4, minimum button height 32dp and generic 4 rows soft keyboard */
        final int SOFT_KEYBOARD_HEIGHT_DP_THRESHOLD = 128;
    
        Rect r = new Rect();
        rootView.getWindowVisibleDisplayFrame(r);
        DisplayMetrics dm = rootView.getResources().getDisplayMetrics();
        /* heightDiff = rootView height - status bar height (r.top) - visible frame height (r.bottom - r.top) */
        int heightDiff = rootView.getBottom() - r.bottom;
        /* Threshold size: dp to pixels, multiply with display density */
        boolean isKeyboardShown = heightDiff > SOFT_KEYBOARD_HEIGHT_DP_THRESHOLD * dm.density;
    
        Log.d(TAG, "isKeyboardShown ? " + isKeyboardShown + ", heightDiff:" + heightDiff + ", density:" + dm.density
                + "root view height:" + rootView.getHeight() + ", rect:" + r);
    
        return isKeyboardShown;
    }

4
Bu kabul edilen cevap olmayı hak ediyor. Yoğunluğu göz ardı etmek, farklı form faktörlerine sahip ancak benzer piksel boyutlarına sahip cihazlarda çok farklı sonuçlar verdi. Teşekkürler!
Ginger McMurray

2
Teşekkür ederim ... bu daha iyi bir durum!
TacB0sS

1
Mükemmel. İhtiyacımız olan şey isKeyboardShown () yöntemidir. Teşekkürler
Danylo Volokh

2020'de de çalışıyor. Mükemmel cevap. Tüm kodu denedim ama bu mükemmel çalışır.
Mitesh Jain

8

Bunu anlamak için biraz zaman kullandım ... Bazı CastExceptions'ı çalıştırdım, ancak layout.xml'deki LinearLayout'u sınıfın adıyla değiştirebileceğinizi anladım.

Bunun gibi:

<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent"
    xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/llMaster">

<com.ourshoppingnote.RelativeLayoutThatDetectsSoftKeyboard android:background="@drawable/metal_background"
    android:layout_width="fill_parent" android:layout_height="fill_parent"
    android:id="@+id/rlMaster" >
    <LinearLayout android:layout_width="fill_parent"
        android:layout_height="1dip" android:background="@drawable/line"></LinearLayout>

          ....

</com.ourshoppingnote.RelativeLayoutThatDetectsSoftKeyboard>    


</LinearLayout>

Bu şekilde herhangi bir yayın sorunuyla karşılaşmazsınız.

... ve bunu her sayfada yapmak istemiyorsanız "Android'de MasterPage" kullanmanızı öneririz. Buradaki bağlantıya bakın: http://jnastase.alner.net/archive/2011/01/08/ldquomaster-pagesrdquo-in-android.aspx


Aynı paket / sınıf adına sahip değilseniz bunu XML'inize yapıştırma konusunda dikkatli olun. Tutulma sadece donmaya karar verir ve kapatmanız gerekir. Böyle profesyonel bir ürün. / s
Spencer Ruport

5
@ SpencerRuport, bu yüzden ücretsiz.
Cody

@DoctorOreo - IntelliJ'i edinin. Ücretsizdir ve emilmez.
Mark

@ Mark - Bunu gönderdikten birkaç ay sonra gerçekten IntelliJ'i denedim. Eclipse'den çok daha iyi, IMO. Tüm ürünleri (çoğunlukla) mükemmel olduğunu düşünüyorum. Hatta birkaç tane bile aldım.
Cody

Böyle eski bir yorum dizisini dirilttiğim için üzgünüm. Onu kullandığınız ve keyif aldığınız için mutluyum. IntelliJ'in yanı sıra iOS için AppCode ve Python için PyCharm'ı kullanmayı seviyorum. Şerefe!
Mark


5

Fikir şu ki, klavyenizi gizlemeniz ve yumuşak giriş durumunu aynı anda kontrol etmeniz gerekiyorsa, aşağıdaki çözümü kullanın:

public boolean hideSoftInput() {
    InputMethodManager imm = (InputMethodManager) getSystemService(Activity.INPUT_METHOD_SERVICE);
    return imm.hideSoftInputFromWindow(mViewPager.getWindowToken(), 0);
}

Gizlenmeden önce klavye gösteriliyorsa bu yöntem true değerini döndürür.


Bu yükseklik ve tüm kullanmadan çalışır ... Teşekkürler ... u zamanımı kurtardı ...
Vji

4

Ben @ Yogesh yöntemi ile birlikte @ Reuben_Scratton yönteminin bir kombinasyon en iyi gibi görünüyor bulundu. Yöntemlerini birleştirmek şöyle bir şey getirecektir:

final View activityRootView = findViewById(R.id.activityRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
  @Override
  public void onGlobalLayout() {
    if (getResources().getConfiguration().keyboardHidden == Configuration.KEYBOARDHIDDEN_NO) { // Check if keyboard is not hidden
       // ... do something here
    }
  }
});

her zaman Configuration.KEYBOARDHIDDEN_NO.
fantouch

4

Etkinliğin decorView özelliğini kullanarak ekran klavyesinin gizlenmesini gözlemleyebilirsiniz.

public final class SoftKeyboardUtil {
    public static final String TAG = "SoftKeyboardUtil";
    public static void observeSoftKeyBoard(Activity activity , final OnSoftKeyBoardHideListener listener){
        final View decorView = activity.getWindow().getDecorView();
        decorView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                Rect rect = new Rect();
                decorView.getWindowVisibleDisplayFrame(rect);
                int displayHight = rect.bottom - rect.top;
                int hight = decorView.getHeight();
                boolean hide = (double)displayHight / hight > 0.8 ;
                if(Log.isLoggable(TAG, Log.DEBUG)){
                    Log.d(TAG ,"DecorView display hight = "+displayHight);
                    Log.d(TAG ,"DecorView hight = "+ hight);
                    Log.d(TAG, "softkeyboard visible = " + !hide);
                }

                listener.onSoftKeyBoardVisible(!hide);

            }
        });
    }



    public interface OnSoftKeyBoardHideListener{
        void onSoftKeyBoardVisible(boolean visible);
    }
}

4

Fark kodlama varsaymak yerine, benim uygulamada menü seçenekleri vardı dint gibi böyle bir şey yaptım.

final View root= findViewById(R.id.myrootview); 
root.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
    public void onGlobalLayout() {
        int heightDiff = root.getRootView().getHeight() - root.getHeight();

        Rect rectgle= new Rect();
        Window window= getWindow();
        window.getDecorView().getWindowVisibleDisplayFrame(rectgle);
        int contentViewTop=                     
          window.findViewById(Window.ID_ANDROID_CONTENT).getTop();
        if(heightDiff <= contentViewTop){
            //Soft KeyBoard Hidden
        }else{
            //Soft KeyBoard Shown
        }
     }
});

Android için çalışmaz: windowSoftInputMode = "adjustPan". Yumuşak klavye göründükten sonra ekranımın küçülmemesini istedim. Ayarlamak için bile çalışması için herhangi bir düzeltme söyleyebilir misinizPan
Shirish Herwade

4

Sistem ekleriyle de çözüm var, ancak yalnızca API >= 21( Android L) ile çalışıyor . Sahip olduğunuzu BottomNavigationView, hangisinin çocuk olduğunu LinearLayoutve klavye gösterildiğinde gizlemeniz gerektiğini varsayalım:

> LinearLayout
  > ContentView
  > BottomNavigationView

Tek yapmanız gereken LinearLayoutbu şekilde uzanmaktır :

public class KeyboardAwareLinearLayout extends LinearLayout {
    public KeyboardAwareLinearLayout(Context context) {
        super(context);
    }

    public KeyboardAwareLinearLayout(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public KeyboardAwareLinearLayout(Context context,
                                     @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

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

    @Override
    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
        int childCount = getChildCount();
        for (int index = 0; index < childCount; index++) {
            View view = getChildAt(index);
            if (view instanceof BottomNavigationView) {
                int bottom = insets.getSystemWindowInsetBottom();
                if (bottom >= ViewUtils.dpToPx(200)) {
                    // keyboard is shown
                    view.setVisibility(GONE);
                } else {
                    // keyboard is hidden
                    view.setVisibility(VISIBLE);
                }
            }
        }
        return insets;
    }
}

Fikir, klavye gösterildiğinde, sistem eklerinin oldukça büyük bir .bottomdeğerle değiştirilmesidir .


4

Bunun için yardımcı olabilecek gizli bir yöntem var InputMethodManager.getInputMethodWindowVisibleHeight. Ama neden gizli olduğunu bilmiyorum.

import android.content.Context
import android.os.Handler
import android.view.inputmethod.InputMethodManager

class SoftKeyboardStateWatcher(private val ctx: Context) {
  companion object {
    private const val DELAY = 10L
  }

  private val handler = Handler()
  private var isSoftKeyboardOpened: Boolean = false

  private val height: Int
    get() {
      val imm = ctx.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
      val method = imm.javaClass.getMethod("getInputMethodWindowVisibleHeight")
      method.isAccessible = true
      return method.invoke(imm) as Int
    }

  private val task: Runnable by lazy {
    Runnable {
      start()
      if (!isSoftKeyboardOpened && height > 0) {
        isSoftKeyboardOpened = true
        notifyOnSoftKeyboardOpened(height)
      } else if (isSoftKeyboardOpened && height == 0) {
        isSoftKeyboardOpened = false
        notifyOnSoftKeyboardClosed()
      }
    }
  }

  var listener: SoftKeyboardStateListener? = null

  interface SoftKeyboardStateListener {
    fun onSoftKeyboardOpened(keyboardHeightInPx: Int)
    fun onSoftKeyboardClosed()
  }

  fun start() {
    handler.postDelayed(task, DELAY)
  }

  fun stop() {
    handler.postDelayed({
      if (!isSoftKeyboardOpened) handler.removeCallbacks(task)
    }, DELAY * 10)
  }

  private fun notifyOnSoftKeyboardOpened(keyboardHeightInPx: Int) {
    listener?.onSoftKeyboardOpened(keyboardHeightInPx)
  }

  private fun notifyOnSoftKeyboardClosed() {
    listener?.onSoftKeyboardClosed()
  }
}

Birisi buna ihtiyaç duyarsa - Xamarin'de de çalışır, yöntemin adı tamamen aynıdır ve aynı şekilde erişilmesi gerekir - InputMethodManager'daki Class özelliği aracılığıyla.
Konstantin Severy

Bunu kullanırken dikkatli olun, desteklenmeyen API (bir nedenden dolayı gizlenmiştir) ve yeni başlayanlar için KitKat'ta çalışmaz.
Daniele Ricci

3

Bu çözümlerin hiçbiri Lollipop için olduğu gibi çalışmaz. Lollipop'ta activityRootView.getRootView().getHeight()görünümü ölçerken düğme çubuğunun yüksekliğini içerir. Lollipop ile çalışmak için yukarıdaki en iyi / en basit çözümü uyarladım.

    final View activityRootView = findViewById(R.id.activityRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
  @Override
  public void onGlobalLayout() {
    Rect r = new Rect();
    //r will be populated with the coordinates of your view that area still visible.
    activityRootView.getWindowVisibleDisplayFrame(r);

    int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
    Resources res = getResources();
    // The status bar is 25dp, use 50dp for assurance
    float maxDiff =
        TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, res.getDisplayMetrics());

    //Lollipop includes button bar in the root. Add height of button bar (48dp) to maxDiff
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
      float buttonBarHeight =
          TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48, res.getDisplayMetrics());
      maxDiff += buttonBarHeight;
    }
    if (heightDiff > maxDiff) { // if more than 100 pixels, its probably a keyboard...
      ...do something here
    }
  }
});

Stackoverflow.com/a/18992807/2914140 adresinden benzer bir çözüm neden sizin için çalışmadı ve çözümünüz nasıl farklı?
CoolMind

3

Sabit bir sayı eklemeyi öneren yukarıdaki çözümlerin çoğunu kullanırken bir hatayla karşılaştım.

S4, gezinme çubuğunun yüksekliğinin 100 piksel olmasına neden olan yüksek bir dpi'ye sahip, bu yüzden uygulamam klavyenin her zaman açık olduğunu düşünüyor.

Yani tüm yeni yüksek çözünürlüklü telefonlar piyasaya sürüldüğünde, sabit kodlanmış bir değer kullanmanın uzun vadede iyi bir fikir olmadığını düşünüyorum.

Çeşitli ekranlar ve cihazlarda bazı testlerden sonra bulduğum daha iyi bir yaklaşım yüzde kullanmaktı. DecorView ve ur uygulama içeriği arasındaki farkı edinin ve daha sonra bu farkın yüzdesinin ne olduğunu kontrol edin. Sahip olduğum istatistiklerden, çoğu gezinti çubuğu (boyut, çözünürlük vb. Ne olursa olsun) ekranın% 3 ila% 5'ini alacaktır. Klavyenin açık olduğu yerlerde ekranın% 47 ila% 55'ini alıyordu.

Sonuç olarak benim çözümüm fark% 10'dan fazla olup olmadığını kontrol etmek oldu ben onun bir klavye açık olduğunu varsayalım.


3

Reuban'ın cevabının, özellikle yüksek çözünürlüklü cihazlarda, bazı durumlarda daha yararlı olduğunu kanıtlayan hafif bir varyantını kullandım.

final View activityRootView = findViewById(android.R.id.content);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(
        new OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                int heightView = activityRootView.getHeight();
                int widthView = activityRootView.getWidth();
                if (1.0 * widthView / heightView > 3) {
                    //Make changes for Keyboard not visible
                } else {
                    //Make changes for keyboard visible
                }
            }
        });

Bu nedir R.id.activityRoot
ranjith

2
yerine oluşturma ve kullanmanın R.id.activityRoot, sadece kullanabilirsiniz android.R.id.contenttam olarak neye ihtiyacınız olduğu.
Marcin Orlowski

3

Bilgisayar açısından sonsuza dek sürdü, ancak bu soru hala inanılmaz derecede alakalı! Bu yüzden yukarıdaki cevapları aldım ve onları biraz birleştirdim ve geliştirdim ...

public interface OnKeyboardVisibilityListener {
    void onVisibilityChanged(boolean visible);
}

public final void setKeyboardListener(final OnKeyboardVisibilityListener listener) {
    final View activityRootView = ((ViewGroup) getActivity().findViewById(android.R.id.content)).getChildAt(0);
    activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {

        private boolean wasOpened;

    private final Rect r = new Rect();

        @Override
        public void onGlobalLayout() {
            activityRootView.getWindowVisibleDisplayFrame(r);

            int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
            boolean isOpen = heightDiff > 100;
            if (isOpen == wasOpened) {
                logDebug("Ignoring global layout change...");
                return;
            }

            wasOpened = isOpen;
            listener.onVisibilityChanged(isOpen);
        }
    });
}

Benim için çalışıyor.


3

Bunu dene:

final View activityRootView = getWindow().getDecorView().getRootView();
activityRootView.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.
        activityRootView.getWindowVisibleDisplayFrame(r);

        int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
        if (heightDiff < activityRootView.getRootView().getHeight() / 4 ) { // if more than 100 pixels, its probably a keyboard...
             // ... do something here ... \\
        }
    }
});

2

Cevabım temel olarak Kachi'nin cevabı ile aynı, ama benim uygulama boyunca kullanım şeklini temizlemek için güzel bir yardımcı sınıfa sardım.

import android.app.Activity;
import android.app.Fragment;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;

/**
 * Detects Keyboard Status changes and fires events only once for each change
 */
public class KeyboardStatusDetector {
    KeyboardVisibilityListener visibilityListener;

    boolean keyboardVisible = false;

    public void registerFragment(Fragment f) {
        registerView(f.getView());
    }

    public void registerActivity(Activity a) {
        registerView(a.getWindow().getDecorView().findViewById(android.R.id.content));
    }

    public KeyboardStatusDetector registerView(final View v) {
        v.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                Rect r = new Rect();
                v.getWindowVisibleDisplayFrame(r);

                int heightDiff = v.getRootView().getHeight() - (r.bottom - r.top);
                if (heightDiff > 100) { // if more than 100 pixels, its probably a keyboard...
                    /** Check this variable to debounce layout events */
                    if(!keyboardVisible) {
                        keyboardVisible = true;
                        if(visibilityListener != null) visibilityListener.onVisibilityChanged(true);
                    }
                } else {
                    if(keyboardVisible) {
                        keyboardVisible = false;
                        if(visibilityListener != null) visibilityListener.onVisibilityChanged(false);
                    }
                }
            }
        });

        return this;
    }

    public KeyboardStatusDetector setVisibilityListener(KeyboardVisibilityListener listener) {
        visibilityListener = listener;
        return this;
    }

    public static interface KeyboardVisibilityListener {
        public void onVisibilityChanged(boolean keyboardVisible);
    }
}

Bunu, uygulamanın herhangi bir yerinde klavye değişikliklerini şu şekilde tespit etmek için kullanabilirsiniz:

    new KeyboardStatusDetector()
            .registerFragment(fragment)  //register to a fragment 
            .registerActivity(activity)  //or register to an activity
            .registerView(view)          //or register to a view
            .setVisibilityListener(new KeyboardVisibilityListener() {
                @Override
                public void onVisibilityChanged(boolean keyboardVisible) {
                    if(keyboardVisible) {
                       //Do stuff for keyboard visible
                    }else {
                       //Do stuff for keyboard hidden
                    }
                }
            });

Not: "kayıt" çağrılarından yalnızca birini kullanın. Hepsi aynı şekilde çalışıyor ve sadece rahatlık için oradalar


2

bunu deneyebilir, benim için harika çalışabilirsin:

InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);

if (imm.isAcceptingText()) {
    //Software Keyboard was shown..
} else {
    //Software Keyboard was not shown..
}

1
Bunu kullanmayın, klavye geri kullanarak gizlenmişse doğru döner, ancak görünüm en azından Marshmallow'da odaklanır
Maragues

2
her zaman görünür yanıt verir
jose920405

2

Bir viewpager içindeki parçaların yönünü değiştirirken klavye durumunu korumakta zorlanıyordum. Neden olduğundan emin değilim, ama sadece sakat gibi görünüyor ve standart bir Faaliyetten farklı davranıyor.

Bu durumda klavye durumunu korumak için, önce klavyenizi eklemeniz android:windowSoftInputMode = "stateUnchanged"gerekir AndroidManifest.xml. Bununla birlikte, bunun aslında tüm sorunu çözmediğini fark edebilirsiniz - klavye daha önce yönlendirme değişikliğinden önce açılmışsa benim için açılmadı. Diğer tüm durumlarda, davranış doğru görünüyordu.

O zaman burada bahsedilen çözümlerden birini uygulamalıyız. Bulduğum en temiz George Maisuradze's - hideSoftInputFromWindow'dan boolean geri aramasını kullanın:

InputMethodManager imm = (InputMethodManager) getSystemService(Activity.INPUT_METHOD_SERVICE);
return imm.hideSoftInputFromWindow(mViewPager.getWindowToken(), 0);

Bu değeri Fragment'ın onSaveInstanceStateyönteminde sakladım ve aldım onCreate. Daha sonra, klavyeyi onCreateViewbir değeri varsa zorla gösterdim true(parça parça yıkımından önce gerçekten gizlemeden önce klavyenin görünür olması durumunda doğru döner).


1

İşte benim çözümüm ve işe yarıyor. Piksel boyutu aramak yerine, içerik görünümünün yüksekliğinin değişip değişmediğini kontrol edin:

// Scroll to the latest comment whenever the keyboard is shown
commentsContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {

        private int oldHeight;

        @Override
        public void onGlobalLayout() {
            int newHeight = commentsContent.getMeasuredHeight();
            if (newHeight < oldHeight) {
                // Check for the keyboard showing in case the height difference
                // is a result of orientation change
                if (isSoftKeyboardShowing(CommentsActivity.this)) {
                    // Keyboard is showing so scroll to the latest comment
                    scrollToLatestComment();
                }
            }
            oldHeight = newHeight;
        }

    });


public static boolean isSoftKeyboardShowing(Activity activity) {
    InputMethodManager inputMethodManager = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
    return inputMethodManager.isActive();
}

inputMethodManager.isActive (), klavyenin açık olup olmamasından bağımsız olarak her zaman benim için doğru dönüyor
EionRobb

1

Hiçbir sabit kod yazmayın. En iyi yol, KeyBord Show ile EditText'e Odaklanın sırasında görüşlerinizi yeniden boyutlandırmanızdır. Aşağıdaki kodu kullanarak etkinlikteki resize özelliğini Manifest dosyasına ekleyerek yapabilirsiniz.

android:windowSoftInputMode="adjustResize"


1

Bunu bulmak için doğrudan bir yöntem var. Ayrıca, herhangi bir Düzen değişikliği gerektirmez.
Böylece, sürükleyici tam ekran modunda da çalışır.

Hile, yumuşak klavyeyi gizlemeye veya göstermeye ve bu denemenin sonucunu yakalamaya çalışmanızdır.
Panik yok, bu gerçekten klavyeyi göstermiyor veya gizlemiyor. Sadece devleti istiyoruz.

Güncel kalmak için bir Handler kullanarak işlemi, örneğin her 200 milisaniyede bir tekrarlayabilirsiniz.

Burada bir uygulama bulabilirsiniz: https://stackoverflow.com/a/27567074/2525452


1

Bu yöntemin klavye görünür olup olmadığını bulmanıza yardımcı olacağını düşünüyorum.

 public Boolean isSoftKeyBoardVisible(){
    InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);

    if (imm.isAcceptingText()) {
        Log.d(TAG,"Software Keyboard was shown");
        return true;
    } else {
        Log.d(TAG,"Software Keyboard was not shown");
        return false;
    }

}

her zaman görünür yanıt verir
jose920405

0

int heightDiff = activityRootView.getRootView().getHeight() - activityRootView.getHeight();Yarı saydam durum çubuğu modunu ayarlarsanız, Reuben Scratton'ın yeni yanıtı (HeightDiff'i hesaplayın ) etkinlikte çalışmaz.

yarı saydam durum çubuğunu kullanırsanız activityRootView.getHeight(), yumuşak klavyenin görünür olduğu hava durumunu asla değiştirmez. her zaman etkinlik yüksekliğini ve durum çubuğunu döndürür.

Örneğin, Nexus 4, Android 5.0.1, android:windowTranslucentStatustrue olarak ayarlanmış , 1184 sonsuza kadar geri dönecek, ime bile açacaktır. Eğer ayarlarsanandroid:windowTranslucentStatusFalse olarak , Yüksekliği doğru şekilde döndürür, ime görünmezse, 1134 (durum çubuğunu içermez) döndürür。 ime'yi kapatın, belki 5xx döndürür (ime'nin yüksekliğine bağlıdır)

Hava durumu bu bir hata olduğunu bilmiyorum, 4.4.4 ve 5.0.1 üzerinde denedim, sonuç aynı.

Yani, şimdiye kadar, en çok anlaşılan ikinci cevap olan Kachi'nin çözümü ime'nin yüksekliğini hesaplamanın en güvenli yolu olacak. İşte bir kopyası:

final View activityRootView = findViewById(R.id.activityRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new        OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Rect r = new Rect();
//r will be populated with the coordinates of your view that area still visible.
activityRootView.getWindowVisibleDisplayFrame(r);

int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
if (heightDiff > 100) { // if more than 100 pixels, its probably a keyboard...
    ... do something here
    }
 }
}); 

0

Bir yöntem , bir LayoutListener ihtiyacı yoktur

Benim durumumda, Parçamı değiştirmeden önce klavyenin durumunu kaydetmek istiyorum. Ben yöntemini çağırır hideSoftInputFromWindow danonSaveInstanceState klavye görünür veya değildi beni ister klavye ve döner kapatır.

Bu yöntem basittir, ancak klavyenizin durumunu değiştirebilir.


0

Bunun eski bir yazı olduğunu biliyorum ama bunun bildiğim en basit yaklaşım olduğunu ve test cihazımın Nexus 5 olduğunu düşünüyorum. Başka cihazlarda denemedim. Umarım kodumu iyi bulmazlarsa diğerlerinin yaklaşımlarını paylaşırlar :)

public static boolean isKeyboardShown(Context context, View view) {
        if (context == null || view == null) {
            return false;
        }
        InputMethodManager imm = (InputMethodManager) context
                .getSystemService(Context.INPUT_METHOD_SERVICE);
        return imm.hideSoftInputFromWindow(view.getWindowToken(), 0); 
}

imm.hideSoftInputFromWindow boole değerini döndürür.

Teşekkürler,


0
if (keyopen())
{
                InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
                imm.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY,0);            
}

Yukarıdaki işlev, bir Klavyenin görünür olup olmadığını kontrol etmek için kullandığım işlevdir. Eğer öyleyse, kapatırım.

Aşağıda gerekli iki yöntem gösterilmektedir.

İlk olarak, onCreate'da uygulanabilir Pencere yüksekliğini tanımlayın.

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

//  add to onCreate method
    Rect rectgle= new Rect();
    Window window= getWindow();
    window.getDecorView().getWindowVisibleDisplayFrame(rectgle);
    sheight= rectgle.bottom;
//

} 

Ardından, bu örnekte Window yüksekliğini alan bir boolean yöntemi ekleyin. Orijinalle eşleşmiyorsa (yol boyunca değiştirmediğinizi varsayarsak ...) o zaman klavye açıktır.

public boolean keyopen()
{
    Rect rectgle= new Rect();
    Window window= getWindow();
    window.getDecorView().getWindowVisibleDisplayFrame(rectgle);
    int curheight= rectgle.bottom;

    if (curheight!=sheight)
    {
        return true;
    }
    else
    {
        return false;
    }
}

Frotz!


0

Klavyenin gizli olup olmadığını nasıl kesin olarak belirleyebileceğinizi biliyorum.

public int getStatusBarHeight() {
    int result = 0;
    int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
    if (resourceId > 0) {
        result = getResources().getDimensionPixelSize(resourceId);
    }
    return result;
}

public int getNavigationBarHeight() {
    int result = 0;
    int resourceId = getResources().getIdentifier("navigation_bar_height", "dimen", "android");
    if (resourceId > 0) {
        result = getResources().getDimensionPixelSize(resourceId);
    }
    return result;
}

public boolean isKeyboardHidden() {
    int delta = mRootView.getRootView().getHeight() - mRootView.getHeight() - getNavigationBarHeight() - getStatusBarHeight()
            - getSupportActionBar().getHeight();
    return delta <= 0;
}

Bu tabletler için geçerlidir. Gezinme çubuğu yatay olarak gösterildiğinde.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.