如何在Android中捕获“虚拟键盘显示/隐藏”事件?

时间:2022-02-02 11:22:47

I would like to alter the layout based on whether the virtual keyboard is shown or not. I've searched the API and various blogs but can't seem to find anything useful.

我想根据虚拟键盘是否显示来改变布局。我搜索了API和各种博客,但似乎找不到任何有用的东西。

Is it possible?

是可能的吗?

Thanks!

谢谢!

15 个解决方案

#1


84  

You've got to handle configuration changes yourself.

您必须自己处理配置更改。

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

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

Sample:

示例:

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

Then just change the visibility of some views, update a field, and change your layout file.

然后只需更改一些视图的可见性、更新字段并更改布局文件。

Note

This solution will not work for soft keyboards and onConfigurationChanged will not be called for soft keyboards.

此解决方案不适用于软键盘,而onConfigurationChanged方法中都不需要软键盘。

#2


48  

This may not be the most effective solution. But this worked for me every time... I call this function where ever i need to listen to the softKeyboard.

这可能不是最有效的解决方案。但这每次都对我起作用……只要我需要听软键盘,我就会调用这个函数。

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

Note: This approach will cause issues if the user uses a floating keyboard.

注意:如果用户使用浮动键盘,这种方法会引起问题。

#3


36  

If you want to handle show/hide of IMM (virtual) keyboard window from your Activity, you'll need to subclass your layout and override onMesure method(so that you can determine the measured width and the measured height of your layout). After that set subclassed layout as main view for your Activity by setContentView(). Now you'll be able to handle IMM show/hide window events. If this sounds complicated, it's not that really. Here's the code:

如果您想从您的活动中处理IMM(虚拟)键盘窗口的show/hide,您需要对布局进行子类化并覆盖onMesure方法(以便您可以确定布局的测量宽度和测量高度)。然后通过setContentView()将子类布局设置为活动的主视图。现在您将能够处理IMM显示/隐藏窗口事件。如果这听起来很复杂,其实并不是那么简单。这是代码:

main.xml

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>

Now inside your Activity declare subclass for your layout (main.xml)

现在在您的活动中为布局声明子类(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);
    }
}

You can see from the code that we inflate layout for our Activity in subclass constructor

从代码中可以看出,我们在子类构造函数中扩展了活动的布局

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

And now just set content view of subclassed layout for our Activity.

现在为我们的活动设置子类布局的content view。

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

}

#4


22  

Based on the Code from Nebojsa Tomcic I've developed the following RelativeLayout-Subclass:

基于Nebojsa Tomcic的代码,我开发了如下相对论性yout子类:

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

}

This works quite fine... Mark, that this solution will just work when Soft Input Mode of your Activity is set to "WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE"

这工作很好…标记,当您的活动的软输入模式被设置为“windowmanager . layoutparams.soft_input_再调整_resize”时,这个解决方案就可以工作了。

#5


15  

Like @amalBit's answer, register a listener to global layout and calculate the difference of dectorView's visible bottom and its proposed bottom, if the difference is bigger than some value(guessed IME's height), we think IME is up:

和@amalBit的回答一样,注册一个全局布局的监听器,计算dectorView的可见底部和提议的底部的差异,如果差异大于某个值(猜测IME的高度),我们认为IME是向上的:

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

the height threshold 100 is the guessed minimum height of IME.

高度阈值100是估计的IME的最小高度。

This works for both adjustPan and adjustResize.

这适用于调节盘和调整器。

#6


12  

Nebojsa's solution almost worked for me. When I clicked inside a multi-line EditText it knew the keyboard was displayed, but when I started typing inside the EditText, the actualHeight and proposedHeight were still the same so it did not know they keyboard was still displayed. I made a slight modification to store the max height and it works fine. Here is the revised subclass:

Nebojsa的解决方案几乎对我起作用。当我在多行EditText中点击时,它知道显示了键盘,但当我开始在EditText中输入时,实际高度和建议高度仍然相同,因此它不知道键盘仍然显示。我做了一个小的修改来存储最大高度,它工作得很好。下面是修改后的子类:

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

#7


12  

I have done this way:

我是这样做的:

Add OnKeyboardVisibilityListener interface.

添加OnKeyboardVisibilityListener接口。

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

HomeActivity.java:

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

Hope this would help you.

希望这能对你有所帮助。

#8


11  

Not sure if anyone post this. Found this solution simple to use!. The SoftKeyboard class is on gist.github.com. But while keyboard popup/hide event callback we need a handler to properly do things on UI:

不知道有没有人贴出来。发现这个解决方案简单易用!。SoftKeyboard类在github.com上。但是当键盘弹出/隐藏事件回调时,我们需要一个处理器来正确地处理UI:

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

    }   
});

#9


9  

I solve this by overriding onKeyPreIme(int keyCode, KeyEvent event) in my custom EditText.

我通过在我的自定义EditText中重写onKeyPreIme(int keyCode, KeyEvent事件)来解决这个问题。

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

#10


4  

I have sort of a hack to do this. Although there doesn't seem to be a way to detect when the soft keyboard has shown or hidden, you can in fact detect when it is about to be shown or hidden by setting an OnFocusChangeListener on the EditText that you're listening to.

我有个办法。虽然似乎没有一种方法可以检测软键盘何时显示或隐藏,但实际上可以通过在正在监听的EditText上设置OnFocusChangeListener来检测何时显示或隐藏。

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

NOTE: One thing to be aware of with this hack is that this callback is fired immediately when the EditText gains or loses focus. This will actually fire right before the soft keyboard shows or hides. The best way I've found to do something after the keyboard shows or hides is to use a Handler and delay something ~ 400ms, like so:

注意:要注意的是,当EditText获得或失去焦点时,这个回调将立即触发。这实际上会在软键盘显示或隐藏之前启动。在键盘显示或隐藏之后,我发现最好的方法是使用一个处理器,延迟大约400ms,比如:

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

#11


3  

Sander ,I believe you are trying to show the view blocked by the soft keyboard. Try this http://android-developers.blogspot.com/2009/04/updating-applications-for-on-screen.html.

Sander,我想你是想显示被软键盘屏蔽的视图。试试http://android-developers.blogspot.com/2009/04/updating-applications-for-on-screen.html。

#12


2  

I have solve the problem on single line textview back coding.

我已经解决了单行文本视图反向编码的问题。

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

#13


2  

You can also check for first DecorView's child bottom padding. It will be set to non-zero value when keyboard is shown.

您还可以检查first DecorView的子填充。当显示键盘时,它将被设置为非零值。

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

#14


0  

Hide|Show events for keyboard can be listened through simple hack in OnGlobalLayoutListener :

隐藏|显示事件的键盘可以通过简单的破解在OnGlobalLayoutListener:

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

Here activityRootView is your Activity's root view.

这里activityRootView是活动的根视图。

#15


-2  

Nebojsa Tomcic's answer wasn't helpful for me. I have RelativeLayout with TextView and AutoCompleteTextView inside it. I need to scroll the TextView to the bottom when the keyboard is showed and when it's hidden. To accomplish this I overrode onLayout method and it works fine for me.

Nebojsa Tomcic的回答对我没有帮助。我有一个RelativeLayout里面有TextView和AutoCompleteTextView。我需要在显示键盘和隐藏键盘时将TextView滚动到底部。为了实现这一点,我重写了onLayout方法,它对我很有用。

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

#1


84  

You've got to handle configuration changes yourself.

您必须自己处理配置更改。

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

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

Sample:

示例:

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

Then just change the visibility of some views, update a field, and change your layout file.

然后只需更改一些视图的可见性、更新字段并更改布局文件。

Note

This solution will not work for soft keyboards and onConfigurationChanged will not be called for soft keyboards.

此解决方案不适用于软键盘,而onConfigurationChanged方法中都不需要软键盘。

#2


48  

This may not be the most effective solution. But this worked for me every time... I call this function where ever i need to listen to the softKeyboard.

这可能不是最有效的解决方案。但这每次都对我起作用……只要我需要听软键盘,我就会调用这个函数。

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

Note: This approach will cause issues if the user uses a floating keyboard.

注意:如果用户使用浮动键盘,这种方法会引起问题。

#3


36  

If you want to handle show/hide of IMM (virtual) keyboard window from your Activity, you'll need to subclass your layout and override onMesure method(so that you can determine the measured width and the measured height of your layout). After that set subclassed layout as main view for your Activity by setContentView(). Now you'll be able to handle IMM show/hide window events. If this sounds complicated, it's not that really. Here's the code:

如果您想从您的活动中处理IMM(虚拟)键盘窗口的show/hide,您需要对布局进行子类化并覆盖onMesure方法(以便您可以确定布局的测量宽度和测量高度)。然后通过setContentView()将子类布局设置为活动的主视图。现在您将能够处理IMM显示/隐藏窗口事件。如果这听起来很复杂,其实并不是那么简单。这是代码:

main.xml

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>

Now inside your Activity declare subclass for your layout (main.xml)

现在在您的活动中为布局声明子类(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);
    }
}

You can see from the code that we inflate layout for our Activity in subclass constructor

从代码中可以看出,我们在子类构造函数中扩展了活动的布局

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

And now just set content view of subclassed layout for our Activity.

现在为我们的活动设置子类布局的content view。

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

}

#4


22  

Based on the Code from Nebojsa Tomcic I've developed the following RelativeLayout-Subclass:

基于Nebojsa Tomcic的代码,我开发了如下相对论性yout子类:

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

}

This works quite fine... Mark, that this solution will just work when Soft Input Mode of your Activity is set to "WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE"

这工作很好…标记,当您的活动的软输入模式被设置为“windowmanager . layoutparams.soft_input_再调整_resize”时,这个解决方案就可以工作了。

#5


15  

Like @amalBit's answer, register a listener to global layout and calculate the difference of dectorView's visible bottom and its proposed bottom, if the difference is bigger than some value(guessed IME's height), we think IME is up:

和@amalBit的回答一样,注册一个全局布局的监听器,计算dectorView的可见底部和提议的底部的差异,如果差异大于某个值(猜测IME的高度),我们认为IME是向上的:

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

the height threshold 100 is the guessed minimum height of IME.

高度阈值100是估计的IME的最小高度。

This works for both adjustPan and adjustResize.

这适用于调节盘和调整器。

#6


12  

Nebojsa's solution almost worked for me. When I clicked inside a multi-line EditText it knew the keyboard was displayed, but when I started typing inside the EditText, the actualHeight and proposedHeight were still the same so it did not know they keyboard was still displayed. I made a slight modification to store the max height and it works fine. Here is the revised subclass:

Nebojsa的解决方案几乎对我起作用。当我在多行EditText中点击时,它知道显示了键盘,但当我开始在EditText中输入时,实际高度和建议高度仍然相同,因此它不知道键盘仍然显示。我做了一个小的修改来存储最大高度,它工作得很好。下面是修改后的子类:

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

#7


12  

I have done this way:

我是这样做的:

Add OnKeyboardVisibilityListener interface.

添加OnKeyboardVisibilityListener接口。

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

HomeActivity.java:

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

Hope this would help you.

希望这能对你有所帮助。

#8


11  

Not sure if anyone post this. Found this solution simple to use!. The SoftKeyboard class is on gist.github.com. But while keyboard popup/hide event callback we need a handler to properly do things on UI:

不知道有没有人贴出来。发现这个解决方案简单易用!。SoftKeyboard类在github.com上。但是当键盘弹出/隐藏事件回调时,我们需要一个处理器来正确地处理UI:

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

    }   
});

#9


9  

I solve this by overriding onKeyPreIme(int keyCode, KeyEvent event) in my custom EditText.

我通过在我的自定义EditText中重写onKeyPreIme(int keyCode, KeyEvent事件)来解决这个问题。

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

#10


4  

I have sort of a hack to do this. Although there doesn't seem to be a way to detect when the soft keyboard has shown or hidden, you can in fact detect when it is about to be shown or hidden by setting an OnFocusChangeListener on the EditText that you're listening to.

我有个办法。虽然似乎没有一种方法可以检测软键盘何时显示或隐藏,但实际上可以通过在正在监听的EditText上设置OnFocusChangeListener来检测何时显示或隐藏。

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

NOTE: One thing to be aware of with this hack is that this callback is fired immediately when the EditText gains or loses focus. This will actually fire right before the soft keyboard shows or hides. The best way I've found to do something after the keyboard shows or hides is to use a Handler and delay something ~ 400ms, like so:

注意:要注意的是,当EditText获得或失去焦点时,这个回调将立即触发。这实际上会在软键盘显示或隐藏之前启动。在键盘显示或隐藏之后,我发现最好的方法是使用一个处理器,延迟大约400ms,比如:

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

#11


3  

Sander ,I believe you are trying to show the view blocked by the soft keyboard. Try this http://android-developers.blogspot.com/2009/04/updating-applications-for-on-screen.html.

Sander,我想你是想显示被软键盘屏蔽的视图。试试http://android-developers.blogspot.com/2009/04/updating-applications-for-on-screen.html。

#12


2  

I have solve the problem on single line textview back coding.

我已经解决了单行文本视图反向编码的问题。

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

#13


2  

You can also check for first DecorView's child bottom padding. It will be set to non-zero value when keyboard is shown.

您还可以检查first DecorView的子填充。当显示键盘时,它将被设置为非零值。

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

#14


0  

Hide|Show events for keyboard can be listened through simple hack in OnGlobalLayoutListener :

隐藏|显示事件的键盘可以通过简单的破解在OnGlobalLayoutListener:

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

Here activityRootView is your Activity's root view.

这里activityRootView是活动的根视图。

#15


-2  

Nebojsa Tomcic's answer wasn't helpful for me. I have RelativeLayout with TextView and AutoCompleteTextView inside it. I need to scroll the TextView to the bottom when the keyboard is showed and when it's hidden. To accomplish this I overrode onLayout method and it works fine for me.

Nebojsa Tomcic的回答对我没有帮助。我有一个RelativeLayout里面有TextView和AutoCompleteTextView。我需要在显示键盘和隐藏键盘时将TextView滚动到底部。为了实现这一点,我重写了onLayout方法,它对我很有用。

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