Android'de “sanal klavye göster / gizle” olayı nasıl yakalanır?


Yanıtlar:


69

Not

Bu çözüm, yumuşak klavyeler için çalışmaz ve onConfigurationChangedyumuşak (sanal) klavyeler için çağrılmaz.


Konfigürasyon değişikliklerini kendiniz halletmelisiniz.

http://developer.android.com/guide/topics/resources/runtime-changes.html#HandlingTheChange

Örneklem:

// from the link above
@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);


    // Checks whether a hardware keyboard is available
    if (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO) {
        Toast.makeText(this, "keyboard visible", Toast.LENGTH_SHORT).show();
    } else if (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) {
        Toast.makeText(this, "keyboard hidden", Toast.LENGTH_SHORT).show();
    }
}

Ardından, bazı görünümlerin görünürlüğünü değiştirin, bir alanı güncelleyin ve düzen dosyanızı değiştirin.


4
@shiami try newConfig.keyboardHidden == Configuration.KEYBOARDHIDDEN_NO~ Chris
cimnine

3
Bu, yalnızca AndroidManifest'te istediğiniz configChanges'i dinlemek için etkinliği kaydettiyseniz çalışır.
Raul Agrait

66
lütfen cevabınızı güncelleyin ve bunun yumuşak klavye için çalışmadığını söyleyin. Kodumu denemek için yarım günümü boşa harcadım. Ve sonra bu yorumları gördüm.
Shirish Herwade

17
Bu asıl soru olan "sanal" klavyelerde işe yaramıyor.
brummfondel

18
Soru, SOFT KLAVYE hakkındaydı, neden bir donanım klavyesi ile ilgili kabul edilen cevap? -1!
Denys Vitali

56

Bu en etkili çözüm olmayabilir. Ama bu benim için her zaman işe yaradı ... SoftKeyboard'u dinlemem gereken her yerde bu işlevi çağırıyorum.

boolean isOpened = false;

public void setListenerToRootView() {
    final View activityRootView = getWindow().getDecorView().findViewById(android.R.id.content);
    activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {

            int heightDiff = activityRootView.getRootView().getHeight() - activityRootView.getHeight();
            if (heightDiff > 100) { // 99% of the time the height diff will be due to a keyboard.
                Toast.makeText(getApplicationContext(), "Gotcha!!! softKeyboardup", 0).show();

                if (isOpened == false) {
                    //Do two things, make the view top visible and the editText smaller
                }
                isOpened = true;
            } else if (isOpened == true) {
                Toast.makeText(getApplicationContext(), "softkeyborad Down!!!", 0).show();
                isOpened = false;
            }
        }
    });
}

Not: Bu yaklaşım, kullanıcı kayan bir klavye kullanıyorsa sorunlara neden olacaktır.


1
addOnGlobalLayoutListener?
coolcool1994

7
Bu bir bellek sızıntısı gibi kokuyor. Global bir nesneye bir dinleyici ekliyorsunuz, size tutunacak ve asla gitmenize izin vermeyecek.
flexicious.com

9
Bu , mizanpaj asla yeniden boyutlandırılmadığından android:windowSoftInputMode="adjustPan"veya adjustResizetam ekran penceresiyle ayarlanan Etkinlikler için de çalışmaz .
Ionoclast Brigham

1
Bu yalnızca adjustResize ile çalışır. AdjustPan için heightDiff asla değişmez.
alexhilton

2
neden bir boole karşılaştırıyorsunuz ?
Xerus

37

IMM (sanal) klavye penceresinin Etkinliğinizden gösterilmesini / gizlenmesini istiyorsanız, mizanpajınızı alt sınıflandırmanız ve onMesure yöntemini geçersiz kılmanız gerekir (böylece mizanpajınızın ölçülen genişliğini ve ölçülen yüksekliğini belirleyebilirsiniz). Bundan sonra setContentView () ile Faaliyetiniz için alt sınıf düzeni ana görünüm olarak ayarlayın. Artık IMM pencere olaylarını göster / gizle işlemlerini yapabileceksiniz. Bu karmaşık görünüyorsa, o kadar da değil. İşte kod:

main.xml

   <?xml version="1.0" encoding="utf-8"?>
   <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="horizontal" >
        <EditText
             android:id="@+id/SearchText" 
             android:text="" 
             android:inputType="text"
             android:layout_width="fill_parent"
             android:layout_height="34dip"
             android:singleLine="True"
             />
        <Button
             android:id="@+id/Search" 
             android:layout_width="60dip"
             android:layout_height="34dip"
             android:gravity = "center"
             />
    </LinearLayout>

Şimdi Etkinliklerinize göre düzeniniz için alt sınıf bildirin (main.xml)

    public class MainSearchLayout extends LinearLayout {

    public MainSearchLayout(Context context, AttributeSet attributeSet) {
        super(context, attributeSet);
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.main, this);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        Log.d("Search Layout", "Handling Keyboard Window shown");

        final int proposedheight = MeasureSpec.getSize(heightMeasureSpec);
        final int actualHeight = getHeight();

        if (actualHeight > proposedheight){
            // Keyboard is shown

        } else {
            // Keyboard is hidden
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}

Alt sınıf yapıcısındaki Etkinliğimiz için düzeni şişirdiğimiz koddan görebilirsiniz

inflater.inflate(R.layout.main, this);

Ve şimdi Faaliyetimiz için alt sınıf düzeninin içerik görünümünü ayarlayın.

public class MainActivity extends Activity {

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        MainSearchLayout searchLayout = new MainSearchLayout(this, null);

        setContentView(searchLayout);
    }

    // rest of the Activity code and subclassed layout...

}

3
Daha fazla araştırmaya ihtiyacım var, ancak bunun, yerleşim ölçümlerinin bir klavye varlığından etkilenmeyeceği büyük bir ekran cihazında küçük diyalog için çalışıp çalışmayacağı konusunda şüphelerim var.
PJL

4
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

Bu işe yaramıyorsa, (actualHeight> önerilen
yükseklik

Ayrıca aynı fikre biraz Özel Görünüm kullanabilirsiniz, bir örnek aşağıda gist.github.com/juliomarcos/8ca307cd7eca607c8547
Julio Rodrigues

1
Düzen asla yeniden boyutlandırılmadığından android:windowSoftInputMode="adjustPan"veya adjustResizetam ekran penceresiyle ayarlanan Etkinlikler için çalışmaz .
Ionoclast Brigham

35

Bu şekilde yaptım:

OnKeyboardVisibilityListenerArayüz ekle .

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

HomeActivity.java :

public class HomeActivity extends Activity implements OnKeyboardVisibilityListener {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_sign_up);
    // Other stuff...
    setKeyboardVisibilityListener(this);
}

private void setKeyboardVisibilityListener(final OnKeyboardVisibilityListener onKeyboardVisibilityListener) {
    final View parentView = ((ViewGroup) findViewById(android.R.id.content)).getChildAt(0);
    parentView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {

        private boolean alreadyOpen;
        private final int defaultKeyboardHeightDP = 100;
        private final int EstimatedKeyboardDP = defaultKeyboardHeightDP + (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? 48 : 0);
        private final Rect rect = new Rect();

        @Override
        public void onGlobalLayout() {
            int estimatedKeyboardHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, EstimatedKeyboardDP, parentView.getResources().getDisplayMetrics());
            parentView.getWindowVisibleDisplayFrame(rect);
            int heightDiff = parentView.getRootView().getHeight() - (rect.bottom - rect.top);
            boolean isShown = heightDiff >= estimatedKeyboardHeight;

            if (isShown == alreadyOpen) {
                Log.i("Keyboard state", "Ignoring global layout change...");
                return;
            }
            alreadyOpen = isShown;
            onKeyboardVisibilityListener.onVisibilityChanged(isShown);
        }
    });
}


@Override
public void onVisibilityChanged(boolean visible) {
    Toast.makeText(HomeActivity.this, visible ? "Keyboard is active" : "Keyboard is Inactive", Toast.LENGTH_SHORT).show();
  }
}

Umarım bu sana yardımcı olur.


2
Teşekkürler Hiren. Bu mükemmel bir çözüm +1
Harin Kaklotar

1
Teşekkürler, benim için çalıştı! RecyclerView'inizi ayarlamak istiyorsanız, aşağıdaki çözüme bakın: stackoverflow.com/a/43204258/373106
David Papirov

1
Mükemmel yeniden kullanılabilir uygulama, Etkinlik veya Parça üzerinde çalıştı, teşekkürler
Pelanes

1
gerçekten güzel ty.
ZaoTaoBao

@DavidPapirov, bir RecyclerView bağlantısı yapıştırdınız, ancak burada bundan bahsetmediniz.
CoolMind

22

Nebojsa Tomcic koduna dayanarak aşağıdaki RelativeLayout-Subclass yazılımını geliştirdim:

import java.util.ArrayList;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.RelativeLayout;

public class KeyboardDetectorRelativeLayout extends RelativeLayout {

    public interface IKeyboardChanged {
        void onKeyboardShown();
        void onKeyboardHidden();
    }

    private ArrayList<IKeyboardChanged> keyboardListener = new ArrayList<IKeyboardChanged>();

    public KeyboardDetectorRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

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

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

    public void addKeyboardStateChangedListener(IKeyboardChanged listener) {
        keyboardListener.add(listener);
    }

    public void removeKeyboardStateChangedListener(IKeyboardChanged listener) {
        keyboardListener.remove(listener);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int proposedheight = MeasureSpec.getSize(heightMeasureSpec);
        final int actualHeight = getHeight();

        if (actualHeight > proposedheight) {
            notifyKeyboardShown();
        } else if (actualHeight < proposedheight) {
            notifyKeyboardHidden();
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    private void notifyKeyboardHidden() {
        for (IKeyboardChanged listener : keyboardListener) {
            listener.onKeyboardHidden();
        }
    }

    private void notifyKeyboardShown() {
        for (IKeyboardChanged listener : keyboardListener) {
            listener.onKeyboardShown();
        }
    }

}

Bu gayet iyi çalışıyor ... İşaretle, bu çözüm sadece Faaliyetinizin Yumuşak Giriş Modu "WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE" olarak ayarlandığında çalışır


3
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

1
Bu , mizanpaj asla yeniden boyutlandırılmadığından android:windowSoftInputMode="adjustPan"veya adjustResizetam ekran penceresiyle ayarlanan Etkinlikler için de çalışmaz .
Ionoclast Brigham

birkaç kez tetikliyor.
zionpi

22

@ AmalBit'in cevabı gibi, bir dinleyiciyi global düzene kaydedin ve dectorView'ın görünür alt ve önerilen alt farkını hesaplayın, eğer fark bir değerden daha büyükse (tahmin edilen IME'nin yüksekliği), IME'nin yükseldiğini düşünüyoruz:

    final EditText edit = (EditText) findViewById(R.id.edittext);
    edit.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            if (keyboardShown(edit.getRootView())) {
                Log.d("keyboard", "keyboard UP");
            } else {
                Log.d("keyboard", "keyboard Down");
            }
        }
    });

private boolean keyboardShown(View rootView) {

    final int softKeyboardHeight = 100;
    Rect r = new Rect();
    rootView.getWindowVisibleDisplayFrame(r);
    DisplayMetrics dm = rootView.getResources().getDisplayMetrics();
    int heightDiff = rootView.getBottom() - r.bottom;
    return heightDiff > softKeyboardHeight * dm.density;
}

yükseklik eşiği 100, IME'nin tahmin edilen minimum yüksekliğidir.

Bu, adjustPan ve adjustResize için çalışır.


2
Ben sadece saçımı çekmek üzereyim !! Saçımı kurtardın;)
Vijay Singh Chouhan

1
Buradaki tek iyi cevap, mükemmel yumuşak klavye üzerinde çalışıyor, teşekkür ederim
Z3nk

12

Nebojsa'nın çözümü neredeyse benim için çalıştı. Çok satırlı bir EditText'i tıklattığımda klavyenin görüntülendiğini biliyordu, ancak EditText'in içine yazmaya başladığımda, actualHeight ve recommendedHeight hala aynıydı, bu yüzden klavyenin hala görüntülendiğini bilmiyorlardı. Maksimum yüksekliği saklamak için küçük bir değişiklik yaptım ve iyi çalışıyor. Gözden geçirilmiş alt sınıf:

public class CheckinLayout extends RelativeLayout {

    private int largestHeight;

    public CheckinLayout(Context context, AttributeSet attributeSet) {
        super(context, attributeSet);
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.checkin, this);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int proposedheight = MeasureSpec.getSize(heightMeasureSpec);
        largestHeight = Math.max(largestHeight, getHeight());

        if (largestHeight > proposedheight)
            // Keyboard is shown
        else
            // Keyboard is hidden

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}

10

Kimsenin bunu gönderip göndermediğinden emin değilim. Bulunan bu çözüm basit kullanımı ile! . SoftKeyboard sınıf gist.github.com üzerindedir . Ancak klavye açılır / olay geri aramasını gizle, kullanıcı arayüzünde işleri düzgün bir şekilde yapmak için bir işleyiciye ihtiyacımız var:

/*
Somewhere else in your code
*/
RelativeLayout mainLayout = findViewById(R.layout.main_layout); // You must use your root layout
InputMethodManager im = (InputMethodManager) getSystemService(Service.INPUT_METHOD_SERVICE);

/*
Instantiate and pass a callback
*/
SoftKeyboard softKeyboard;
softKeyboard = new SoftKeyboard(mainLayout, im);
softKeyboard.setSoftKeyboardCallback(new SoftKeyboard.SoftKeyboardChanged()
{

    @Override
    public void onSoftKeyboardHide() 
    {
        // Code here
        new Handler(Looper.getMainLooper()).post(new Runnable() {
                @Override
                public void run() {
                    // Code here will run in UI thread
                    ...
                }
            });
    }

    @Override
    public void onSoftKeyboardShow() 
    {
        // Code here
        new Handler(Looper.getMainLooper()).post(new Runnable() {
                @Override
                public void run() {
                    // Code here will run in UI thread
                    ...
                }
            });

    }   
});

İşte SoftkeyBoard almak için Git " gist.github.com/felHR85/… "
douarbou

9

Bu benim özel EditText onKeyPreIme (int keyCode, KeyEvent olay) geçersiz kılarak çözmek.

@Override
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
        //keyboard will be hidden
    }
}

Parça veya Aktivitede Nasıl Kullanılır? @Qbait
Maulik Dodia

İşe yaramaz, yalnızca sayfayı davamda bıraktığımda çağrılabilir.
DysaniazzZ

bu EditText yöntemidir, şu yanıta bakın: stackoverflow.com/a/5993196/2093236
Dmide

4

Bunu yapmak için bir çeşit kesmek var. Yumuşak klavye görünür veya gizli algılayan bir yol olarak görünmüyor olsa da, olabilir o zaman aslında algılamak yaklaşık bir ayarlayarak gösterilen veya gizlenecek şekilde OnFocusChangeListenerüzerine EditTextsize bu konum dinleme.

EditText et = (EditText) findViewById(R.id.et);
et.setOnFocusChangeListener(new View.OnFocusChangeListener()
    {
        @Override
        public void onFocusChange(View view, boolean hasFocus)
        {
            //hasFocus tells us whether soft keyboard is about to show
        }
    });

NOT: Bu hack ile dikkat edilmesi gereken bir şey, EditTextkazançlar veya odak kaybedildiğinde bu geri aramanın hemen tetiklenmesidir . Bu , yumuşak klavye gösterilmeden veya gizlenmeden hemen önce patlar. Klavye gösterdikten veya gizlendikten sonra bir şey yapmanın en iyi yolu , bir şeyi kullanmak Handlerve ~ 400ms geciktirmektir, şöyle:

EditText et = (EditText) findViewById(R.id.et);
et.setOnFocusChangeListener(new View.OnFocusChangeListener()
    {
        @Override
        public void onFocusChange(View view, boolean hasFocus)
        {
            new Handler().postDelayed(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        //do work here
                    }
                }, 400);
        }
    });

1
Aksi halde çalışmaz. OnFocusChangeListenersadece EditTextdevlet değiştikten sonra odaklanıp odaklanmadığını söyler . Ama odak IMEolduğunda gizli olabilir EditText, bu dava nasıl tespit edilir?
DysaniazzZ


2

Tek satırlı textview geri kodlamada sorunu çözdüm.

package com.helpingdoc;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;

public class MainSearchLayout extends LinearLayout {
    int hieght = 0;
    public MainSearchLayout(Context context, AttributeSet attributeSet) {

        super(context, attributeSet);
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.main, this);


    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        Log.d("Search Layout", "Handling Keyboard Window shown");
       if(getHeight()>hieght){
           hieght = getHeight();
       }
        final int proposedheight = MeasureSpec.getSize(heightMeasureSpec);
        final int actualHeight = getHeight();
        System.out.println("....hieght = "+ hieght);
        System.out.println("....actualhieght = "+ actualHeight);
        System.out.println("....proposedheight = "+ proposedheight);
        if (actualHeight > proposedheight){
            // Keyboard is shown


        } else if(actualHeight<proposedheight){
            // Keyboard is hidden

        }

        if(proposedheight == hieght){
             // Keyboard is hidden
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}

2
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

Fonksiyonu gizleme / gösterme durumunda bu dinleyici yöntemi iki veya üç kez çağırır. Sorunun tam olarak ne olduğunu bilmiyorum.
Jagveer Singh Rajput

2

Ayrıca ilk DecorView alt alt dolgusunu da kontrol edebilirsiniz. Klavye gösterildiğinde sıfırdan farklı bir değere ayarlanır.

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    View view = getRootView();
    if (view != null && (view = ((ViewGroup) view).getChildAt(0)) != null) {
        setKeyboardVisible(view.getPaddingBottom() > 0);
    }
    super.onLayout(changed, left, top, right, bottom);
}

1

Gizle | Klavye için olayları göster OnGlobalLayoutListener'da basit bir saldırı ile dinlenebilir:

 final View activityRootView = findViewById(R.id.top_root);
        activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
            public void onGlobalLayout() {
                int heightDiff = activityRootView.getRootView().getHeight() - activityRootView.getHeight();

                if (heightDiff > 100) {
                    // keyboard is up
                } else {
                    // keyboard is down
                }
            }
        });

Burada activityRootView, Etkinliğinizin kök görünümüdür.


my heightDiff başlangıçta 160 ve kbd ile 742'dir, bu yüzden başlangıçta tanıtmak ve ayarlamak zorunda kaldım
djHightDiff

0

klavye olayını kolayca almak için viewTreeObserver kullanarak .

layout_parent.viewTreeObserver.addOnGlobalLayoutListener {
            val r = Rect()
            layout_parent.getWindowVisibleDisplayFrame(r)
            if (layout_parent.rootView.height - (r.bottom - r.top) > 100) { // if more than 100 pixels, its probably a keyboard...
                Log.e("TAG:", "keyboard open")
            } else {
                Log.e("TAG:", "keyboard close")
            }
        }

** layout_parent sizin görünümünüzedit_text.parent


-2

Nebojsa Tomcic'in cevabı bana yardımcı olmadı. Ben RelativeLayoutile TextViewve AutoCompleteTextViewonun içinde. TextViewKlavye gösterildiğinde ve gizlendiğinde alt kısmına kaydırmam gerekiyor . Bunu başarmak için onLayoutyöntemi aştım ve benim için iyi çalışıyor.

public class ExtendedLayout extends RelativeLayout
{
    public ExtendedLayout(Context context, AttributeSet attributeSet)
    {
        super(context, attributeSet);
        LayoutInflater inflater = (LayoutInflater)
                context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.main, this);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b)
    {
        super.onLayout(changed, l, t, r, b);

        if (changed)
        {
            int scrollEnd = (textView.getLineCount() - textView.getHeight() /
                textView.getLineHeight()) * textView.getLineHeight();
            textView.scrollTo(0, scrollEnd);
        }
    }
}
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.