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