本文介绍了PopupWindow在Android API 28的自定义键盘上被剪切的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我做了一个自定义键盘.长按一个键时, PopupWindow 会在该键上方显示一些其他选择.问题在于,在API 28中,此弹出窗口会被裁剪(甚至在第一行中完全隐藏).

按钮和"Low text"(低文本) EditText 位于顶部视图下方.调用

MyInputMethodService

 公共类MyInputMethodService扩展了InputMethodService实现View.OnTouchListener {私人View mTopKey;私人PopupWindow mPopupWindow;私有视图mPopupView;@Override公共视图onCreateInputView(){最后的ConstraintLayout keyboardView =(ConstraintLayout)getLayoutInflater().inflate(R.layout.keyboard,null);mTopKey = keyboardView.findViewById(R.id.a);mTopKey.setOnTouchListener(this);keyboardView.findViewById(R.id.b).setOnTouchListener(this);keyboardView.findViewById(R.id.c).setOnTouchListener(this);keyboardView.findViewById(R.id.d).setOnTouchListener(this);keyboardView.findViewById(R.id.e).setOnTouchListener(this);keyboardView.findViewById(R.id.f).setOnTouchListener(this);keyboardView.findViewById(R.id.g).setOnTouchListener(this);keyboardView.findViewById(R.id.h).setOnTouchListener(this);mPopupView = getLayoutInflater().inflate(R.layout.popup,keyboardView,false);int measureSpec = View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);mPopupView.measure(measureSpec,measureSpec);mPopupWindow = new PopupWindow(mPopupView,ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);返回keyboardView;}@Override公共无效onComputeInsets(InputMethodService.Insets outInsets){//做标准的事情.super.onComputeInsets(outInsets);//只有键盘上的按键才可触摸.其余的应该通过修改//到后面的视图.contentTopInsets设置为与windowSoftInputMode配合使用//在清单中定义.outInsets.visibleTopInsets = mTopKey.getTop();outInsets.contentTopInsets = mTopKey.getTop();}@Overridepublic boolean onTouch(查看v,MotionEvent事件){int action = event.getAction();切换(操作){案例MotionEvent.ACTION_DOWN:layoutAndShowPopupWindow((TextView)v);休息;案例MotionEvent.ACTION_UP:案例MotionEvent.ACTION_CANCEL:mPopupWindow.dismiss();休息;}返回true;}私人void layoutAndShowPopupWindow(TextView key){((TextView)mPopupView.findViewById(R.id.popupKey)).setText(key.getText());int x = key.getLeft()+(key.getWidth()-mPopupView.getMeasuredWidth())/2;int y = key.getTop()-mPopupView.getMeasuredHeight();mPopupWindow.showAtLocation(key,Gravity.NO_GRAVITY,x,y);}} 

keyboard.xml
View 的定义仅是为弹出窗口提供扩展的空间,没有其他用途.

 < android.support.constraint.ConstraintLayoutandroid:layout_width ="match_parent"android:layout_height ="match_parent"><查看android:layout_width ="match_parent"android:layout_height ="50dp"android:layout_marginBottom ="8dp"app:layout_constraintBottom_toTopOf ="@ + id/a"/><按钮android:id ="@ + id/a"android:layout_width ="wrap_content"android:layout_height ="wrap_content"android:layout_marginBottom ="8dp"android:text ="A"app:layout_constraintBottom_toTopOf ="@ + id/e"app:layout_constraintEnd_toStartOf ="@ + id/b"app:layout_constraintStart_toStartOf ="parent"/><按钮android:id ="@ + id/b"android:layout_width ="wrap_content"android:layout_height ="wrap_content"android:layout_marginBottom ="8dp"android:text ="B"app:layout_constraintBottom_toTopOf ="@ + id/f"app:layout_constraintEnd_toStartOf ="@ + id/c"app:layout_constraintStart_toEndOf ="@ + id/a"/><按钮android:id ="@ + id/c"android:layout_width ="wrap_content"android:layout_height ="wrap_content"android:layout_marginBottom ="8dp"android:text ="C"app:layout_constraintBottom_toTopOf ="@ + id/g"app:layout_constraintEnd_toStartOf ="@ + id/d"app:layout_constraintStart_toEndOf ="@ + id/b"/><按钮android:id ="@ + id/d"android:layout_width ="wrap_content"android:layout_height ="wrap_content"android:layout_marginBottom ="8dp"android:text ="D"app:layout_constraintBottom_toTopOf ="@ + id/h"app:layout_constraintEnd_toEndOf ="parent"app:layout_constraintStart_toEndOf ="@ + id/c"/><按钮android:id ="@ + id/e"android:layout_width ="wrap_content"android:layout_height ="wrap_content"android:layout_marginBottom ="8dp"android:text ="E"app:layout_constraintBottom_toBottomOf ="parent"app:layout_constraintEnd_toStartOf ="@ + id/f"app:layout_constraintHorizo​​ntal_bias ="0.5"app:layout_constraintStart_toStartOf ="parent"/><按钮android:id ="@ + id/f"android:layout_width ="wrap_content"android:layout_height ="wrap_content"android:text ="F"app:layout_constraintEnd_toStartOf ="@ + id/g"app:layout_constraintHorizo​​ntal_bias ="0.5"app:layout_constraintStart_toEndOf ="@ + id/e"app:layout_constraintTop_toTopOf ="@ + id/e"/><按钮android:id ="@ + id/g"android:layout_width ="wrap_content"android:layout_height ="wrap_content"android:text ="G"app:layout_constraintEnd_toStartOf ="@ + id/h"app:layout_constraintHorizo​​ntal_bias ="0.5"app:layout_constraintStart_toEndOf ="@ + id/f"app:layout_constraintTop_toTopOf ="@ + id/e"/><按钮android:id ="@ + id/h"android:layout_width ="wrap_content"android:layout_height ="wrap_content"android:layout_marginBottom ="8dp"android:text ="H"app:layout_constraintBottom_toBottomOf ="parent"app:layout_constraintEnd_toEndOf ="parent"app:layout_constraintHorizo​​ntal_bias ="0.5"app:layout_constraintStart_toEndOf ="@ + id/g"app:layout_constraintTop_toTopOf ="@ + id/g"/></android.support.constraint.ConstraintLayout> 

popup.xml
只是弹出窗口.

 < LinearLayoutandroid:layout_width ="match_parent"android:layout_height ="wrap_content"xmlns:tools ="http://schemas.android.com/tools"android:background ="@ android:color/black"android:gravity ="center"android:orientation ="vertical"android:padding ="3dp">< TextViewandroid:id ="@ + id/popupKey"android:layout_width ="wrap_content"android:layout_height ="50dp"android:text ="A"android:textColor ="@ android:color/white"/></LinearLayout> 

activity_main

 < android.support.constraint.ConstraintLayoutandroid:layout_width ="match_parent"android:layout_height ="match_parent"工具:context =.MainActivity">< EditTextandroid:layout_width ="wrap_content"android:layout_height ="wrap_content"android:hint =高文本"app:layout_constraintBottom_toBottomOf ="parent"app:layout_constraintLeft_toLeftOf ="parent"app:layout_constraintRight_toRightOf ="parent"app:layout_constraintTop_toTopOf ="parent"/><按钮android:id ="@ + id/button"android:layout_width ="wrap_content"android:layout_height ="wrap_content"android:layout_marginStart ="8dp"android:layout_marginEnd ="8dp"android:layout_marginBottom ="20dp"android:text ="Button"app:layout_constraintBottom_toBottomOf ="parent"app:layout_constraintEnd_toEndOf ="parent"app:layout_constraintStart_toStartOf ="parent"/>< EditTextandroid:id ="@ + id/editText"android:layout_width ="133dp"android:layout_height ="wrap_content"android:layout_marginStart ="16dp"android:ems ="10"android:inputType ="textPersonName"android:hint ="Low text"app:layout_constraintStart_toStartOf ="parent"app:layout_constraintTop_toTopOf ="@ + id/button"/></android.support.constraint.ConstraintLayout> 

I made a custom keyboard. When you long press a key, a PopupWindow shows some extra choices above the key. The problem is that in API 28, this popup gets clipped (or even completely hidden for the top row).

I had solved this problem for API < 28 with

popupWindow.setClippingEnabled(false);

However, with API 28 the problem has come back. Here is more of the code:

private void layoutAndShowPopupWindow(Key key, int xPosition) {
    popupWindow = new PopupWindow(popupView,
            LinearLayout.LayoutParams.WRAP_CONTENT,
            LinearLayout.LayoutParams.WRAP_CONTENT);
    popupWindow.setClippingEnabled(false);
    int location[] = new int[2];
    key.getLocationInWindow(location);
    int measureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
    popupView.measure(measureSpec, measureSpec);
    int popupWidth = popupView.getMeasuredWidth();
    int spaceAboveKey = key.getHeight() / 4;
    int x = xPosition - popupWidth / popupView.getChildCount() / 2;
    int screenWidth = getScreenWidth();
    if (x < 0) {
        x = 0;
    } else if (x + popupWidth > screenWidth) {
        x = screenWidth - popupWidth;
    }
    int y = location[1] - popupView.getMeasuredHeight() - spaceAboveKey;
    popupWindow.showAtLocation(key, Gravity.NO_GRAVITY, x, y);
}

Did something happen to no longer allow third party keyboards to show content outside of the keyboard view? (This is how it is in iOS.)

What do I need to do to get the PopupWindow to stop being clipped?

解决方案

Updated to show a more tailored approach.
Updated to work with windowSoftInputMode="adjustResize".

It looks like clipping outside of windows may be a new fact of Android life although I have not found documentation to that effect. Regardless, the following method may be the preferred way to go and is, I believe, standard although not very well documented.

In the following, MyInputMethodService instantiates a keyboard that has eight keys on the bottom and an empty view strip above where popups are displayed for the top row of keys. When a key is pressed, the key value is shown in a popup window above the key for the duration of the key press. Since the empty view above the keys encloses the popups, clipping does not occur. (Not a very useful keyboard, but it makes the point.)

The button and "Low text" EditText are under the top view strip. Invocation of onComputeInsets() permits touches on the keyboard keys but disallows keyboard touches in the empty area covered by the inset. In this area, touches are passed down to the underlying views - here the "Low text" EditText and a Button that displays "OK!" when clicked.

"Gboard" seems to work in a similar fashion but uses a sister FrameLayout to display the popups with translation. Here is what a "4" popup looks like in the Layout Inspector for "Gboard".

MyInputMethodService

public class MyInputMethodService extends InputMethodService
    implements View.OnTouchListener {
    private View mTopKey;
    private PopupWindow mPopupWindow;
    private View mPopupView;

    @Override
    public View onCreateInputView() {
        final ConstraintLayout keyboardView = (ConstraintLayout) getLayoutInflater().inflate(R.layout.keyboard, null);
        mTopKey = keyboardView.findViewById(R.id.a);
        mTopKey.setOnTouchListener(this);
        keyboardView.findViewById(R.id.b).setOnTouchListener(this);
        keyboardView.findViewById(R.id.c).setOnTouchListener(this);
        keyboardView.findViewById(R.id.d).setOnTouchListener(this);
        keyboardView.findViewById(R.id.e).setOnTouchListener(this);
        keyboardView.findViewById(R.id.f).setOnTouchListener(this);
        keyboardView.findViewById(R.id.g).setOnTouchListener(this);
        keyboardView.findViewById(R.id.h).setOnTouchListener(this);

        mPopupView = getLayoutInflater().inflate(R.layout.popup, keyboardView, false);
        int measureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
        mPopupView.measure(measureSpec, measureSpec);
        mPopupWindow = new PopupWindow(mPopupView, ViewGroup.LayoutParams.WRAP_CONTENT,
                                       ViewGroup.LayoutParams.WRAP_CONTENT);

        return keyboardView;
    }

    @Override
    public void onComputeInsets(InputMethodService.Insets outInsets) {
        // Do the standard stuff.
        super.onComputeInsets(outInsets);

        // Only the keyboard are with the keys is touchable. The rest should pass touches
        // through to the views behind. contentTopInsets set to play nice with windowSoftInputMode
        // defined in the manifest.
        outInsets.visibleTopInsets = mTopKey.getTop();
        outInsets.contentTopInsets = mTopKey.getTop();
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        int action = event.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                layoutAndShowPopupWindow((TextView) v);
                break;

            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                mPopupWindow.dismiss();
                break;
        }
        return true;
    }

    private void layoutAndShowPopupWindow(TextView key) {
        ((TextView) mPopupView.findViewById(R.id.popupKey)).setText(key.getText());
        int x = key.getLeft() + (key.getWidth() - mPopupView.getMeasuredWidth()) / 2;
        int y = key.getTop() - mPopupView.getMeasuredHeight();
        mPopupWindow.showAtLocation(key, Gravity.NO_GRAVITY, x, y);
    }
}

keyboard.xml
The View is defined solely to give the popups a place to expand into and has no other purpose.

<android.support.constraint.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <View
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_marginBottom="8dp"
        app:layout_constraintBottom_toTopOf="@+id/a" />

    <Button
        android:id="@+id/a"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="8dp"
        android:text="A"
        app:layout_constraintBottom_toTopOf="@+id/e"
        app:layout_constraintEnd_toStartOf="@+id/b"
        app:layout_constraintStart_toStartOf="parent" />

    <Button
        android:id="@+id/b"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="8dp"
        android:text="B"
        app:layout_constraintBottom_toTopOf="@+id/f"
        app:layout_constraintEnd_toStartOf="@+id/c"
        app:layout_constraintStart_toEndOf="@+id/a" />

    <Button
        android:id="@+id/c"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="8dp"
        android:text="C"
        app:layout_constraintBottom_toTopOf="@+id/g"
        app:layout_constraintEnd_toStartOf="@+id/d"
        app:layout_constraintStart_toEndOf="@+id/b" />

    <Button
        android:id="@+id/d"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="8dp"
        android:text="D"
        app:layout_constraintBottom_toTopOf="@+id/h"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/c" />

    <Button
        android:id="@+id/e"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="8dp"
        android:text="E"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/f"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent" />

    <Button
        android:id="@+id/f"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="F"
        app:layout_constraintEnd_toStartOf="@+id/g"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/e"
        app:layout_constraintTop_toTopOf="@+id/e" />

    <Button
        android:id="@+id/g"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="G"
        app:layout_constraintEnd_toStartOf="@+id/h"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/f"
        app:layout_constraintTop_toTopOf="@+id/e" />

    <Button
        android:id="@+id/h"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="8dp"
        android:text="H"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/g"
        app:layout_constraintTop_toTopOf="@+id/g" />
</android.support.constraint.ConstraintLayout>

popup.xml
Just the popup.

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    xmlns:tools="http://schemas.android.com/tools"
    android:background="@android:color/black"
    android:gravity="center"
    android:orientation="vertical"
    android:padding="3dp">

    <TextView
        android:id="@+id/popupKey"
        android:layout_width="wrap_content"
        android:layout_height="50dp"
        android:text="A"
        android:textColor="@android:color/white" />

</LinearLayout>

activity_main

<android.support.constraint.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <EditText
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:hint="High text"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginBottom="20dp"
        android:text="Button"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

    <EditText
        android:id="@+id/editText"
        android:layout_width="133dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:ems="10"
        android:inputType="textPersonName"
        android:hint="Low text"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@+id/button" />

</android.support.constraint.ConstraintLayout>

这篇关于PopupWindow在Android API 28的自定义键盘上被剪切的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-04 15:13