Как захватить событие "virtual keyboard show / hide" в Android?



Я хотел бы изменить макет на основе того, отображается ли виртуальная клавиатура или нет. Я искал API и различные блоги, но не могу найти ничего полезного.



возможно ли это?



спасибо!

764   15  

15 ответов:

вы должны сами обрабатывать изменения конфигурации.

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

затем просто измените видимость некоторых видов, обновите поле и измените файл макета.

Примечание

это решение не будет работать для мягких клавишных и onConfigurationChanged не будет вызываться для мягких клавиатур.

это может быть не самым эффективным решением. Но это работает для меня каждый раз... Я вызываю эту функцию, когда мне нужно слушать 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;
            }
        }
    });
}

Примечание.: Этот подход вызовет проблемы, если пользователь использует плавающую клавиатуру.

если вы хотите обрабатывать окно show / hide of IMM (virtual) keyboard из своей деятельности, вам нужно будет подкласс вашего макета и переопределить метод onMesure(чтобы вы могли определить измеренную ширину и измеренную высоту вашего макета). После этого выделяют подклассы макет, как основной вид вашей деятельности с помощью setContentView(). Теперь вы сможете обрабатывать события IMM show / hide window. Если это звучит сложно, то на самом деле это не так. Вот код:

главная.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);
    }
}

вы можете видеть из кода, что мы раздуваем макет для нашей деятельности в конструкторе подкласса

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

а теперь просто установите вид содержимого подкласса макета для нашей деятельности.

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

}

на основе кода от Nebojsa Tomcic я разработал следующий RelativeLayout-подкласс:

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.Ни.SOFT_INPUT_ADJUST_RESIZE"

Как и ответ @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.

Я сделал так:

добавить OnKeyboardVisibilityListener интерфейс.

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

надеюсь, что это поможет вам.

решение Небойсы почти сработало для меня. Когда я щелкнул внутри многострочного EditText, он знал, что клавиатура отображается, но когда я начал печатать внутри EditText, actualHeight и proposedHeight были все те же, поэтому он не знал, что клавиатура все еще отображается. Я сделал небольшую модификацию для хранения максимальной высоты, и она отлично работает. Вот пересмотренный подкласс:

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

Не уверен, что кто-нибудь опубликует это. Нашел Это простое в использовании решение!. Элемент класс SoftKeyboard включен gist.github.com. но в то время как клавиатура popup/скрыть событие обратного вызова нам нужен обработчик, чтобы правильно делать вещи на 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
                    ...
                }
            });

    }   
});

Я решаю это путем переопределения onKeyPreIme (int keyCode, KeyEvent event) в моем пользовательском EditText.

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

у меня есть хак для этого. Хотя, похоже, нет способа определить, когда мягкая клавиатура показала или скрыла, вы можете на самом деле определить, когда это о для отображения или скрытия, установив 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
        }
    });

Примечание: одна вещь, чтобы быть в курсе с этим взломом является то, что этот обратный вызов запускается сразу же, когда EditText получает или теряет фокус. Этот будет на самом деле огонь перед экранная клавиатура отображает или скрывает. Лучший способ я нашел, чтобы сделать что-то после клавиатура показывает или скрывает, чтобы использовать 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);
        }
    });

Sander, я считаю, что вы пытаетесь показать вид, заблокированный с помощью мягкой клавиатуры. Попробуйте это http://android-developers.blogspot.com/2009/04/updating-applications-for-on-screen.html.

У меня есть решить проблему на одной строке textview обратно кодирования.

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

вы также можете проверить наличие дочернего Нижнего заполнения 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);
}

Скрыть / показать события для клавиатуры можно слушать через простой хак в 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
                }
            }
        });

здесь activityRootView-это корневое представление Вашей активности.

ответ Небойши Томчича не был полезен для меня. У меня есть 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);
        }
    }
}

Comments

    Ничего не найдено.