PopupWindow在Android API 28的自定义键盘上剪辑

PopupWindow在Android API 28的自定义键盘上剪辑,android,custom-keyboard,android-popupwindow,Android,Custom Keyboard,Android Popupwindow,我做了一个定制键盘。长按某个键时,键上方会显示一个PopupWindow。问题是在API 28中,这个弹出窗口被剪裁(甚至完全隐藏在最上面一行) 我已经解决了API

我做了一个定制键盘。长按某个键时,键上方会显示一个
PopupWindow
。问题是在API 28中,这个弹出窗口被剪裁(甚至完全隐藏在最上面一行)

我已经解决了API<28的这个问题

然而,在API 28中,问题又回来了。下面是更多的代码:

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);
}
private void layoutAndShowPopupWindow(Key-Key,int-xPosition){
popupWindow=新的popupWindow(popupView,
LinearLayout.LayoutParams.WRAP_内容,
LinearLayout.LayoutParams.WRAP_内容);
popupWindow.setClippingEnabled(假);
int location[]=新int[2];
key.getLocationInWindow(位置);
int-measureSpec=View.measureSpec.makeMeasureSpec(0,View.measureSpec.UNSPECIFIED);
popupView.measure(measureSpec,measureSpec);
int popupWidth=popupView.getMeasuredWidth();
int spaceoverkey=key.getHeight()/4;
int x=xPosition-popupWidth/popupView.getChildCount()/2;
int screenWidth=getScreenWidth();
if(x<0){
x=0;
}否则如果(x+PopuWidth>screenWidth){
x=屏幕宽度-弹出宽度;
}
int y=位置[1]-popupView.getMeasuredHeight()-空格键;
弹出窗口显示位置(键,重力,无重力,x,y);
}
是否发生了某些情况,不再允许第三方键盘显示键盘视图之外的内容?(这就是iOS中的情况。)


我需要做什么才能使
弹出窗口停止被剪裁

显示弹出视图的一般思路是使用
WindowManager
创建它们,它没有
PopupWindow
的限制

我假设
InputMethodService
负责显示弹出视图。 由于显示这样的窗口需要在API 23及更高版本中获得覆盖权限,因此我们需要制作一个临时
活动
,以便为我们执行此操作。获取权限的结果将使用
EventBus
事件传递到
InputMethodService
。您可以根据体系结构(例如每次键盘启动时)检查所需的覆盖权限

这是这个想法的一个实现,它可能需要一些操作才能完全按照您的意愿工作。我希望有帮助

MyInputMethodService.java

import android.content.Intent;
import android.inputmethodservice.InputMethodService;
import android.os.Build;
import android.provider.Settings;

import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;

public class MyInputMethodService extends InputMethodService {

    private FloatViewManager mFloatViewManager;

    @Override
    public void onCreate() {
        super.onCreate();

        EventBus.getDefault().register(this);
        checkDrawOverlayPermission();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();

        EventBus.getDefault().unregister(this);
    }

    private boolean checkDrawOverlayPermission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {
            Intent intent = new Intent(this, CheckPermissionActivity.class);
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            startActivity(intent);
            return false;
        } else {
            return true;
        }
    }

    private void showPopup(Key key, int xPosition){
        mFloatViewManager = new FloatViewManager(this);
        if (checkDrawOverlayPermission()) {
            mFloatViewManager.showFloatView(key, xPosition);
        }
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onMessageEvent(CanDrawOverlaysEvent event) {
        if (event.isAllowed()) {
            mFloatViewManager.showFloatView(key, xPosition);
        } else {
            // Maybe show an error
        }
    }

}
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.PixelFormat;
import android.os.Build;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;

import static android.content.Context.WINDOW_SERVICE;


public class FloatViewManager {

    private WindowManager mWindowManager;
    private View mFloatView;
    private WindowManager.LayoutParams mFloatViewLayoutParams;

    @SuppressLint("InflateParams")
    public FloatViewManager(Context context) {
        mWindowManager = (WindowManager) context.getSystemService(WINDOW_SERVICE);
        LayoutInflater inflater = LayoutInflater.from(context);
        mFloatView = inflater.inflate(R.layout.float_view_layout, null);

        // --------- do initializations:
        TextView textView = mFloatView.findViewById(R.id.textView);
        // ...
        // ---------

        mFloatViewLayoutParams = new WindowManager.LayoutParams();
        mFloatViewLayoutParams.format = PixelFormat.TRANSLUCENT;
        mFloatViewLayoutParams.flags = WindowManager.LayoutParams.FORMAT_CHANGED;

        mFloatViewLayoutParams.type = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
                ? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
                : WindowManager.LayoutParams.TYPE_PHONE;

        mFloatViewLayoutParams.gravity = Gravity.NO_GRAVITY;
        mFloatViewLayoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
        mFloatViewLayoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
    }

    public void dismissFloatView() {
        mWindowManager.removeViewImmediate(mFloatView);
    }

    public void showFloatView(Key key, int xPosition) {

        // calculate x and y position as you did instead of 0
        mFloatViewLayoutParams.x = 0;
        mFloatViewLayoutParams.y = 0;

        mWindowManager.addView(mFloatView, mFloatViewLayoutParams);
        mWindowManager.updateViewLayout(mFloatView, mFloatViewLayoutParams);
    }

}
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;

import org.greenrobot.eventbus.EventBus;

public class CheckPermissionActivity extends AppCompatActivity {

    private static final int REQUEST_CODE_DRAW_OVERLAY_PERMISSION = 5;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {
            Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));
            startActivityForResult(intent, REQUEST_CODE_DRAW_OVERLAY_PERMISSION);
        } else {
            finish();
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == REQUEST_CODE_DRAW_OVERLAY_PERMISSION) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && Settings.canDrawOverlays(this)) {
                EventBus.getDefault().post(new CanDrawOverlaysEvent(true));
            } else {
                EventBus.getDefault().post(new CanDrawOverlaysEvent(false));
            }
            finish();
        }
    }

}
public class CanDrawOverlaysEvent {

    private boolean mIsAllowed;

    public CanDrawOverlaysEvent(boolean isAllowed) {
        mIsAllowed = isAllowed;
    }

    public boolean isAllowed() {
        return mIsAllowed;
    }

}
FloatViewManager.java

import android.content.Intent;
import android.inputmethodservice.InputMethodService;
import android.os.Build;
import android.provider.Settings;

import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;

public class MyInputMethodService extends InputMethodService {

    private FloatViewManager mFloatViewManager;

    @Override
    public void onCreate() {
        super.onCreate();

        EventBus.getDefault().register(this);
        checkDrawOverlayPermission();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();

        EventBus.getDefault().unregister(this);
    }

    private boolean checkDrawOverlayPermission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {
            Intent intent = new Intent(this, CheckPermissionActivity.class);
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            startActivity(intent);
            return false;
        } else {
            return true;
        }
    }

    private void showPopup(Key key, int xPosition){
        mFloatViewManager = new FloatViewManager(this);
        if (checkDrawOverlayPermission()) {
            mFloatViewManager.showFloatView(key, xPosition);
        }
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onMessageEvent(CanDrawOverlaysEvent event) {
        if (event.isAllowed()) {
            mFloatViewManager.showFloatView(key, xPosition);
        } else {
            // Maybe show an error
        }
    }

}
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.PixelFormat;
import android.os.Build;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;

import static android.content.Context.WINDOW_SERVICE;


public class FloatViewManager {

    private WindowManager mWindowManager;
    private View mFloatView;
    private WindowManager.LayoutParams mFloatViewLayoutParams;

    @SuppressLint("InflateParams")
    public FloatViewManager(Context context) {
        mWindowManager = (WindowManager) context.getSystemService(WINDOW_SERVICE);
        LayoutInflater inflater = LayoutInflater.from(context);
        mFloatView = inflater.inflate(R.layout.float_view_layout, null);

        // --------- do initializations:
        TextView textView = mFloatView.findViewById(R.id.textView);
        // ...
        // ---------

        mFloatViewLayoutParams = new WindowManager.LayoutParams();
        mFloatViewLayoutParams.format = PixelFormat.TRANSLUCENT;
        mFloatViewLayoutParams.flags = WindowManager.LayoutParams.FORMAT_CHANGED;

        mFloatViewLayoutParams.type = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
                ? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
                : WindowManager.LayoutParams.TYPE_PHONE;

        mFloatViewLayoutParams.gravity = Gravity.NO_GRAVITY;
        mFloatViewLayoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
        mFloatViewLayoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
    }

    public void dismissFloatView() {
        mWindowManager.removeViewImmediate(mFloatView);
    }

    public void showFloatView(Key key, int xPosition) {

        // calculate x and y position as you did instead of 0
        mFloatViewLayoutParams.x = 0;
        mFloatViewLayoutParams.y = 0;

        mWindowManager.addView(mFloatView, mFloatViewLayoutParams);
        mWindowManager.updateViewLayout(mFloatView, mFloatViewLayoutParams);
    }

}
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;

import org.greenrobot.eventbus.EventBus;

public class CheckPermissionActivity extends AppCompatActivity {

    private static final int REQUEST_CODE_DRAW_OVERLAY_PERMISSION = 5;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {
            Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));
            startActivityForResult(intent, REQUEST_CODE_DRAW_OVERLAY_PERMISSION);
        } else {
            finish();
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == REQUEST_CODE_DRAW_OVERLAY_PERMISSION) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && Settings.canDrawOverlays(this)) {
                EventBus.getDefault().post(new CanDrawOverlaysEvent(true));
            } else {
                EventBus.getDefault().post(new CanDrawOverlaysEvent(false));
            }
            finish();
        }
    }

}
public class CanDrawOverlaysEvent {

    private boolean mIsAllowed;

    public CanDrawOverlaysEvent(boolean isAllowed) {
        mIsAllowed = isAllowed;
    }

    public boolean isAllowed() {
        return mIsAllowed;
    }

}
CheckPermissionActivity.java

import android.content.Intent;
import android.inputmethodservice.InputMethodService;
import android.os.Build;
import android.provider.Settings;

import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;

public class MyInputMethodService extends InputMethodService {

    private FloatViewManager mFloatViewManager;

    @Override
    public void onCreate() {
        super.onCreate();

        EventBus.getDefault().register(this);
        checkDrawOverlayPermission();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();

        EventBus.getDefault().unregister(this);
    }

    private boolean checkDrawOverlayPermission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {
            Intent intent = new Intent(this, CheckPermissionActivity.class);
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            startActivity(intent);
            return false;
        } else {
            return true;
        }
    }

    private void showPopup(Key key, int xPosition){
        mFloatViewManager = new FloatViewManager(this);
        if (checkDrawOverlayPermission()) {
            mFloatViewManager.showFloatView(key, xPosition);
        }
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onMessageEvent(CanDrawOverlaysEvent event) {
        if (event.isAllowed()) {
            mFloatViewManager.showFloatView(key, xPosition);
        } else {
            // Maybe show an error
        }
    }

}
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.PixelFormat;
import android.os.Build;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;

import static android.content.Context.WINDOW_SERVICE;


public class FloatViewManager {

    private WindowManager mWindowManager;
    private View mFloatView;
    private WindowManager.LayoutParams mFloatViewLayoutParams;

    @SuppressLint("InflateParams")
    public FloatViewManager(Context context) {
        mWindowManager = (WindowManager) context.getSystemService(WINDOW_SERVICE);
        LayoutInflater inflater = LayoutInflater.from(context);
        mFloatView = inflater.inflate(R.layout.float_view_layout, null);

        // --------- do initializations:
        TextView textView = mFloatView.findViewById(R.id.textView);
        // ...
        // ---------

        mFloatViewLayoutParams = new WindowManager.LayoutParams();
        mFloatViewLayoutParams.format = PixelFormat.TRANSLUCENT;
        mFloatViewLayoutParams.flags = WindowManager.LayoutParams.FORMAT_CHANGED;

        mFloatViewLayoutParams.type = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
                ? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
                : WindowManager.LayoutParams.TYPE_PHONE;

        mFloatViewLayoutParams.gravity = Gravity.NO_GRAVITY;
        mFloatViewLayoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
        mFloatViewLayoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
    }

    public void dismissFloatView() {
        mWindowManager.removeViewImmediate(mFloatView);
    }

    public void showFloatView(Key key, int xPosition) {

        // calculate x and y position as you did instead of 0
        mFloatViewLayoutParams.x = 0;
        mFloatViewLayoutParams.y = 0;

        mWindowManager.addView(mFloatView, mFloatViewLayoutParams);
        mWindowManager.updateViewLayout(mFloatView, mFloatViewLayoutParams);
    }

}
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;

import org.greenrobot.eventbus.EventBus;

public class CheckPermissionActivity extends AppCompatActivity {

    private static final int REQUEST_CODE_DRAW_OVERLAY_PERMISSION = 5;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {
            Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));
            startActivityForResult(intent, REQUEST_CODE_DRAW_OVERLAY_PERMISSION);
        } else {
            finish();
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == REQUEST_CODE_DRAW_OVERLAY_PERMISSION) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && Settings.canDrawOverlays(this)) {
                EventBus.getDefault().post(new CanDrawOverlaysEvent(true));
            } else {
                EventBus.getDefault().post(new CanDrawOverlaysEvent(false));
            }
            finish();
        }
    }

}
public class CanDrawOverlaysEvent {

    private boolean mIsAllowed;

    public CanDrawOverlaysEvent(boolean isAllowed) {
        mIsAllowed = isAllowed;
    }

    public boolean isAllowed() {
        return mIsAllowed;
    }

}
candrawOverlyEvent.java

import android.content.Intent;
import android.inputmethodservice.InputMethodService;
import android.os.Build;
import android.provider.Settings;

import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;

public class MyInputMethodService extends InputMethodService {

    private FloatViewManager mFloatViewManager;

    @Override
    public void onCreate() {
        super.onCreate();

        EventBus.getDefault().register(this);
        checkDrawOverlayPermission();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();

        EventBus.getDefault().unregister(this);
    }

    private boolean checkDrawOverlayPermission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {
            Intent intent = new Intent(this, CheckPermissionActivity.class);
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            startActivity(intent);
            return false;
        } else {
            return true;
        }
    }

    private void showPopup(Key key, int xPosition){
        mFloatViewManager = new FloatViewManager(this);
        if (checkDrawOverlayPermission()) {
            mFloatViewManager.showFloatView(key, xPosition);
        }
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onMessageEvent(CanDrawOverlaysEvent event) {
        if (event.isAllowed()) {
            mFloatViewManager.showFloatView(key, xPosition);
        } else {
            // Maybe show an error
        }
    }

}
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.PixelFormat;
import android.os.Build;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;

import static android.content.Context.WINDOW_SERVICE;


public class FloatViewManager {

    private WindowManager mWindowManager;
    private View mFloatView;
    private WindowManager.LayoutParams mFloatViewLayoutParams;

    @SuppressLint("InflateParams")
    public FloatViewManager(Context context) {
        mWindowManager = (WindowManager) context.getSystemService(WINDOW_SERVICE);
        LayoutInflater inflater = LayoutInflater.from(context);
        mFloatView = inflater.inflate(R.layout.float_view_layout, null);

        // --------- do initializations:
        TextView textView = mFloatView.findViewById(R.id.textView);
        // ...
        // ---------

        mFloatViewLayoutParams = new WindowManager.LayoutParams();
        mFloatViewLayoutParams.format = PixelFormat.TRANSLUCENT;
        mFloatViewLayoutParams.flags = WindowManager.LayoutParams.FORMAT_CHANGED;

        mFloatViewLayoutParams.type = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
                ? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
                : WindowManager.LayoutParams.TYPE_PHONE;

        mFloatViewLayoutParams.gravity = Gravity.NO_GRAVITY;
        mFloatViewLayoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
        mFloatViewLayoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
    }

    public void dismissFloatView() {
        mWindowManager.removeViewImmediate(mFloatView);
    }

    public void showFloatView(Key key, int xPosition) {

        // calculate x and y position as you did instead of 0
        mFloatViewLayoutParams.x = 0;
        mFloatViewLayoutParams.y = 0;

        mWindowManager.addView(mFloatView, mFloatViewLayoutParams);
        mWindowManager.updateViewLayout(mFloatView, mFloatViewLayoutParams);
    }

}
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;

import org.greenrobot.eventbus.EventBus;

public class CheckPermissionActivity extends AppCompatActivity {

    private static final int REQUEST_CODE_DRAW_OVERLAY_PERMISSION = 5;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {
            Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));
            startActivityForResult(intent, REQUEST_CODE_DRAW_OVERLAY_PERMISSION);
        } else {
            finish();
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == REQUEST_CODE_DRAW_OVERLAY_PERMISSION) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && Settings.canDrawOverlays(this)) {
                EventBus.getDefault().post(new CanDrawOverlaysEvent(true));
            } else {
                EventBus.getDefault().post(new CanDrawOverlaysEvent(false));
            }
            finish();
        }
    }

}
public class CanDrawOverlaysEvent {

    private boolean mIsAllowed;

    public CanDrawOverlaysEvent(boolean isAllowed) {
        mIsAllowed = isAllowed;
    }

    public boolean isAllowed() {
        return mIsAllowed;
    }

}
build.gradle

dependencies {
    implementation 'org.greenrobot:eventbus:3.1.1'
}
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);
    }
}
更新以显示更为定制的方法。
更新以使用
windowSoftInputMode=“adjustResize”

看起来,在windows之外进行剪辑可能是Android生活中的一个新事实,尽管我还没有找到这样的文档。无论如何,下面的方法可能是首选方法,我相信,它是标准的,尽管没有很好的文档记录

在下面的示例中,
MyInputMethodService
实例化了一个键盘,该键盘底部有八个键,上面有一个空的视图条,其中显示了顶行键的弹出窗口。按键时,按键上方的弹出窗口会在按键持续时间内显示按键值。由于键上方的空视图包含弹出窗口,因此不会进行剪裁。(这不是一个非常有用的键盘,但它说明了这一点。)

按钮和“低文本”编辑文本位于顶视图条下。调用允许触摸键盘键,但不允许在插图覆盖的空白区域触摸键盘。在该区域中,触摸被传递到基础视图-这里是“低文本”
EditText
和单击时显示“OK!”的
按钮

“Gboard”似乎以类似的方式工作,但使用姐妹
FrameLayout
显示带有翻译的弹出窗口。下面是“Gboard”布局检查器中“4”弹出窗口的外观

MyInputMethodService

dependencies {
    implementation 'org.greenrobot:eventbus:3.1.1'
}
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
视图
仅用于为弹出窗口提供一个可扩展的位置,没有其他用途

<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
只是弹出窗口

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

主要活动

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

我用拉丁语(AOSP)解决了这个问题,比如:

  • 我的输入视图布局xml文件是
  • 重写函数:“UpdateFileScreenMode”、“setInputView”、“OnComputerInsets”并从LatinIME.java复制代码-最后修改如下代码
  • 从LatinIME(AOSP)包中复制文件“ViewLayoutUtils.java”、“ViewOutlineProviderCompatitils.java”、“ViewOutlineProviderCompatitilslxx.java”,并修改包名

最简单的解决方案是不将弹出窗口连接到键盘decorview:

popupWindow.setAttachedInDecor(false);

我最终选择了另一个答案,因为对于
InputMethodService
,它似乎更为标准。不过,使用
WindowManager
也是一个好主意,这是我以前没有考虑过的。@Suragch:谢谢你,伙计,我很高兴看到你的问题得到解决。对我来说,在空视图后面仍然有一个空视图。甚至,我已经设置了插图。你知道为什么吗?我使用了这种技术,但它在横向模式下失败了,我不知道该怎么办。这个问题在API 29中不存在,至少在模拟器中不存在。但是在API 28中。