245

仮想キーボードが表示されているかどうかに基づいてレイアウトを変更したいと思います。API とさまざまなブログを検索しましたが、役立つものが見つからないようです。

出来ますか?

ありがとう!

4

19 に答える 19

81

2020年アップデート

これが可能になりました:

Android 11では、次のことができます

view.setWindowInsetsAnimationCallback(object : WindowInsetsAnimation.Callback {
    override fun onEnd(animation: WindowInsetsAnimation) {
        super.onEnd(animation)
        val showingKeyboard = view.rootWindowInsets.isVisible(WindowInsets.Type.ime())
        // now use the boolean for something
    }
})

キーボードの表示/非表示のアニメーションを聞いて、対応するトランジションを実行することもできます。

Android 11 プレビューと対応するドキュメントを読むことをお勧めします

Android 11 より前

ただし、この作品はCompatバージョンで公開されていないため、ハックに頼る必要があります。

ウィンドウのインセットを取得できます。下部のインセットが (実験により) 妥当であると判断された値よりも大きい場合は、キーボードが表示されていると見なすことができます。これは素晴らしいことではなく、場合によっては失敗する可能性がありますが、それをサポートするフレームワークはありません。

これは、この正確な質問https://stackoverflow.com/a/36259261/372076に対する良い答えです。または、Android 11 以前でこれを実現するためのいくつかの異なるアプローチを提供するページを次に示します。

https://developer.salesforce.com/docs/atlas.en-us.noversion.service_sdk_android.meta/service_sdk_android/android_detecting_keyboard.htm


ノート

このソリューションはソフト キーボードでは機能 onConfigurationChangedせず、ソフト (仮想) キーボードでは呼び出されません。


構成の変更を自分で処理する必要があります。

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

サンプル:

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

次に、いくつかのビューの可視性を変更し、フィールドを更新し、レイアウト ファイルを変更します。

于 2010-12-06T10:56:38.483 に答える
57

これは、最も効果的なソリューションではない可能性があります。しかし、これは毎回うまくいきました...ソフトキーボードを聞く必要があるときはいつでもこの関数を呼び出します。

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

注: このアプローチは、ユーザーがフローティング キーボードを使用している場合に問題を引き起こします。

于 2013-08-26T13:53:57.797 に答える
38

アクティビティから IMM (仮想) キーボード ウィンドウの表示/非表示を処理する場合は、レイアウトをサブクラス化し、onMesure メソッドをオーバーライドする必要があります (レイアウトの測定幅と測定高を判断できるようにするため)。その後、setContentView() によってサブクラス化されたレイアウトをアクティビティのメイン ビューとして設定します。これで、IMM のウィンドウの表示/非表示イベントを処理できるようになります。複雑に聞こえるかもしれませんが、実際にはそうではありません。コードは次のとおりです。

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>

アクティビティ内で、レイアウトのサブクラスを宣言します(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);
    }
}

コードからわかるように、サブクラス コンストラクターで Activity のレイアウトをインフレートします。

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

次に、Activity のサブクラス化されたレイアウトのコンテンツ ビューを設定します。

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

}
于 2011-09-14T22:09:27.510 に答える
29

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

高さのしきい値 100 は、推定される IME の最小の高さです。

これは、adjustPan とadjustResize の両方で機能します。

于 2015-10-11T07:57:20.113 に答える
22

Nebojsa Tomcic のコードに基づいて、次の RelativeLayout-Subclass を開発しました。

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

}

これは非常にうまく機能します...アクティビティのソフト入力モードが「WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE」に設定されている場合にのみ、このソリューションが機能することをマークしてください

于 2011-11-15T10:40:26.293 に答える
12

Nebojsaのソリューションはほとんどうまくいきました。複数行の EditText 内をクリックすると、キーボードが表示されていることがわかりましたが、EditText 内で入力を開始すると、actualHeight と proposalHeight は同じままだったので、キーボードがまだ表示されていることを知りませんでした。最大の高さを保存するためにわずかな変更を加えましたが、正常に動作します。改訂されたサブクラスは次のとおりです。

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);
    }
}
于 2012-01-11T21:44:38.167 に答える
10

誰かがこれを投稿したかどうかはわかりません。このソリューションは使いやすいことがわかりました。. SoftKeyboardクラスは gist.github.com にあります。しかし、キーボードの popup/hide イベント コールバック中に、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
                    ...
                }
            });

    }   
});
于 2014-12-30T02:17:37.420 に答える
4

これを行うには、一種のハックがあります。ソフト キーボードが表示または非表示になったことを検出する方法はないようです、実際には、聞いOnFocusChangeListenerているにを設定することで、表示または非表示になるタイミングを検出できEditTextます。

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

注:このハックで注意すべきことの 1 つは、このコールバックが がEditTextフォーカスを獲得または失ったときにすぐに起動されることです。これは、ソフト キーボードが表示または非表示になる直前に実際に起動します。キーボードが表示または非表示になったに何かを行うために私が見つけた最良の方法は、 a を使用してHandler、次のように約 400 ミリ秒遅らせることです。

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);
        }
    });
于 2015-07-26T21:12:12.093 に答える
4

Android 11 以前のソリューション:

androidx.core 1.5.0 がリリースされたため、これは、Android 11 以前のデバイスでキーボードの表示/非表示イベントをリッスンするために行うことです。

グレード:

implementation "androidx.core:core-ktx:1.5.0"

断片:

   override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val view  = activity?.window?.decorView ?: return
        ViewCompat.setOnApplyWindowInsetsListener(view) { v, insets ->
            val showingKeyboard = insets.isVisible(WindowInsetsCompat.Type.ime())
            if(showingKeyboard){
                //do something
            }
            insets
        }
    }

ビューが破棄されたときにリスナーを削除して、メモリリークを回避してください。このソリューションは、ソフトウェア入力モードが の場合にのみ機能しますadjustResize。 setOnApplyWindowInsetsListener はadjustPan、 の場合はトリガーされませんadjustPan

ドキュメントによると、

* When running on devices with API Level 29 and before, the returned value is an
* approximation based on the information available. This is especially true for the {@link
* Type#ime IME} type, which currently only works when running on devices with SDK level 23
* and above.
*

insets.isVisible(ime) は、SDK レベルが 23 を超えるデバイスでのみ動作する必要があります

于 2021-06-10T10:45:49.290 に答える
3

Sander さん、ソフト キーボードによってブロックされたビューを表示しようとしていると思います。このhttp://android-developers.blogspot.com/2009/04/updating-applications-for-on-screen.htmlを試してください。

于 2010-12-03T14:38:02.467 に答える
2

1行のテキストビューのバックコーディングの問題を解決しました。

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);
    }
}
于 2013-06-24T07:09:59.377 に答える
2

最初の 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);
}
于 2013-12-10T12:33:54.847 に答える
0

@Filipkowicz の上記の回答は、Android API < 30 で正常に機能します。Android API 30 以降では、 を使用する必要がありますsetWindowInsetsAnimationCallback。したがって、以下の回答では、API 21 - 30 を機能させるために両方の方法を組み合わせています。

private fun isKeyboardVisible(insets: WindowInsets): Boolean {
    val insetsCompat = WindowInsetsCompat.toWindowInsetsCompat(insets)
    val systemWindow = insetsCompat.systemWindowInsets
    val rootStable = insetsCompat.stableInsets
    if (systemWindow.bottom > rootStable.bottom) {
        // This handles the adjustResize case on < API 30, since
        // systemWindow.bottom is probably going to be the IME
        return true
    }
    return false
}

@JvmStatic
@BindingAdapter("goneWhenKeyboardVisible")
fun View.goneWhenKeyboardVisible() {
    if (isRPlus()) {
        setWindowInsetsAnimationCallback(object :
            WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
            override fun onProgress(
                insets: WindowInsets,
                runningAnimations: MutableList<WindowInsetsAnimation>
            ): WindowInsets {
                return insets
            }

            override fun onStart(
                animation: WindowInsetsAnimation,
                bounds: WindowInsetsAnimation.Bounds
            ): WindowInsetsAnimation.Bounds {
                if (isVisible)
                    isVisible = !rootWindowInsets.isVisible(WindowInsets.Type.ime())
                return super.onStart(animation, bounds)
            }

            override fun onEnd(animation: WindowInsetsAnimation) {
                super.onEnd(animation)
                if (!isVisible)
                    isVisible = !rootWindowInsets.isVisible(WindowInsets.Type.ime())
            }
        })
    } else {
        setOnApplyWindowInsetsListener { _, insets ->
            isVisible = !isKeyboardVisible(insets)
            insets
        }
    }
}
于 2021-05-28T06:49:13.610 に答える
-2

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);
        }
    }
}
于 2012-04-22T07:14:33.253 に答える