代码之家  ›  专栏  ›  技术社区  ›  Suragch Shmidt

在Android API 28的自定义键盘上弹出窗口

  •  10
  • Suragch Shmidt  · 技术社区  · 6 年前

    我做了一个自定义键盘。当您长按一个键时, PopupWindow 会在键上方显示一些额外的选项。问题在于,在API 28中,这个弹出窗口被剪切(甚至完全隐藏在最上面一行)。

    I had solved this problem for api<28 with

    popupWindow.setclippingEnabled(false);
    < /代码> 
    
    

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

    private void layoutandshowPopupWindow(key,int xposition){
    popupWindow=新建弹出窗口(popupView,
    linearlayout.layoutParams.wrap_内容,
    linearlayout.layoutParams.wrap_content);
    PopupWindow.SetClippingEnabled(假);
    int位置[]=新int[2];
    key.getLocationinWindow(位置);
    int measurespec=view.measurespec.makeMeasurespec(0,view.measurespec.unspecified);
    popupview.measure(测量、测量);
    int popupWidth=popupView.getMeasuredWidth();
    int spaceabovekey=key.getheight()/4;
    int x=xposition-popupwidth/popupview.getchildcount()/2;
    int screenwidth=getscreenwidth();
    如果(x<0){
    x=0;
    }否则,如果(x+popupwidth>屏幕宽度){
    x=屏幕宽度-弹出宽度;
    }
    int y=位置[1]-popupView.getMeasuredHeight()-spaceAboveKey;
    弹出窗口。显示位置(键,重力。无重力,x,y);
    }
    < /代码> 
    
    

    是否发生了什么事情,不再允许第三方键盘在键盘视图之外显示内容?(这就是iOS的情况。)

    我需要做什么才能让弹出窗口停止被剪辑?

    enter image description here

    I had solved this problem for API < 28具有

    popupWindow.setClippingEnabled(false);
    

    然而,对于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);
    }
    

    是否发生了什么事情,不再允许第三方键盘在键盘视图之外显示内容?(这就是iOS的情况。)

    我需要做什么才能得到弹窗停止被夹?

    2 回复  |  直到 6 年前
        1
  •  8
  •   Cheticamp    6 年前

    updated to show a more customized approach.
    updated to work with windowsoftinputmode=“adjustersize”

    虽然我还没有找到相关的文档,但在Windows之外进行剪辑似乎是安卓生活的一个新事实。不管怎样,以下方法可能是首选的方法,我相信,标准方法虽然没有很好的记录。

    在下面的代码中, MyInputMethodService 实例化一个键盘,该键盘底部有八个键,上面有一个空的视图条,其中为键的顶行显示弹出窗口。当按下一个键时,在按键的持续时间内,键值显示在键上方的弹出窗口中。由于键上方的空视图包含弹出窗口,因此不会发生剪辑。(不是很有用的键盘,但它很有意义。)

    按钮和“low text” edittext 位于顶视图条下。调用 oncomputeinsets(). permits touches on the keyboard keys but disallows keyboard touches in the empty area co集合。在此区域中,触摸向下传递到基础视图-这里是“low text” edittext 和a button 显示“OK!”单击时。

    “gboard”似乎以类似的方式工作,但使用sister framelayout 显示带翻译的弹出窗口。这是布局检查器中“gboard”的“4”弹出窗口的外观。

    MyInputMethodService

    public class myinputmethodservice extends inputmethodservice
    实现View.OnTouchListener{
    私有视图mtopkey;
    私有弹出窗口MPopupWindow;
    私人视图MPopupView;
    
    @重写
    公共视图OnCreateInputView()。{
    final constraintlayout keyboardview=(constraintlayout)getlayoutinflator().inflat(r.layout.keyboard,空);
    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.findView byid(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=getlayoutinflator()。充气(r.layout.popup,keyboardview,false);
    int measurespec=view.measurespec.makeMeasurespec(0,view.measurespec.unspecified);
    mpopupview.measure(measurespec,measurespec);
    mpopupWindow=新弹出窗口(mpopupView,viewGroup.layoutParams.wrap_content,
    viewgroup.layoutParams.wrap_content);
    
    返回键盘视图;
    }
    
    @重写
    公共void OnComputeInsets(inputMethodService.insets OutInsets){
    //做标准的事情。
    超级.oncomputeinsets(outinsets);
    
    //只有带按键的键盘是可触摸的。其余的应该传球
    //到后面的视图。ContentTopInsets设置为与WindowsOfInputMode一起播放
    //在清单中定义。
    outinsets.visibletopinsets=mtopkey.gettop();
    outinsets.contenttopinsets=mtopkey.gettop();
    }
    
    @重写
    公共布尔型OnTouch(View V,MotionEvent事件){
    int action=event.getaction();
    开关(动作){
    case motionevent.action\u向下:
    布局和显示弹出窗口((文本视图)v);
    断裂;
    
    case motionevent.action\u向上:
    case motionevent.action_取消:
    mpopupWindow.disclose();
    断裂;
    }
    回归真实;
    }
    
    专用void布局和ShowPopupWindow(textView键){
    ((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(键,重力。无重力,x,y);
    }
    }
    < /代码> 
    
    

    keyboard.xml
    view的定义仅仅是为了给弹出窗口一个扩展的位置,没有其他用途。

    <android.support.constraint.constraintlayout
    android:layout_width=“匹配父级”
    android:layout_height=“match_parent”>
    
    视野;视野
    android:layout_width=“匹配父级”
    android:layout_height=“50dp”
    android:layout_marginbottom=“8dp”
    app:layout_constraintbottom_totopof=“@+id/a”/>gt;
    
    按钮
    安卓:id=“@+id/a”
    android:layout_width=“包装内容”
    android:layout_height=“包装内容”
    android:layout_marginbottom=“8dp”
    安卓:text=“a”
    app:layout_constraintBottom_totopof=“@+id/e”
    app:layout_constrainteden_tostartof=“@+id/b”
    app:layout_constraintStart_toStartof=“parent”/>
    
    按钮
    安卓:id=“@+id/b”
    android:layout_width=“包装内容”
    android:layout_height=“包装内容”
    android:layout_marginbottom=“8dp”
    安卓:text=“b”
    app:layout_constraintBottom_totopof=“@+id/f”
    app:layout_constrainteden_tostartof=“@+id/c”
    app:layout_constraintStart_toendoff=“@+id/a”/>
    
    按钮
    安卓:id=“@+id/c”
    android:layout_width=“包装内容”
    android:layout_height=“包装内容”
    android:layout_marginbottom=“8dp”
    安卓:text=“c”
    app:layout_constraintbottom_totopof=“@+id/g”
    app:layout_constrainteden_tostartof=“@+id/d”
    app:layout_constraintStart_toendoff=“@+id/b”/>
    
    按钮
    安卓:id=“@+id/d”
    android:layout_width=“包装内容”
    android:layout_height=“包装内容”
    android:layout_marginbottom=“8dp”
    安卓:text=“d”
    app:layout_constraintbottom_totopof=“@+id/h”
    app:layout_constrainted_toendoff=“父级”
    app:layout_constraintStart_toendoff=“@+id/c”/>
    
    按钮
    安卓:id=“@+id/e”
    android:layout_width=“包装内容”
    android:layout_height=“包装内容”
    android:layout_marginbottom=“8dp”
    安卓:text=“e”
    app:layout_constraintBottom_tobottomof=“父级”
    app:layout_constrainteden_tostartof=“@+id/f”
    app:layout_constrainthorizontal_bias=“0.5”
    app:layout_constraintStart_toStartof=“parent”/>
    
    按钮
    安卓:id=“@+id/f”
    android:layout_width=“包装内容”
    android:layout_height=“包装内容”
    安卓:text=“f”
    app:layout_constrainteden_tostartof=“@+id/g”
    app:layout_constrainthorizontal_bias=“0.5”
    app:layout_constraintStart_toendoff=“@+id/e”
    app:layout_constraintop_totopof=“@+id/e”/>
    
    按钮
    安卓:id=“@+id/g”
    android:layout_width=“包装内容”
    android:layout_height=“包装内容”
    安卓:text=“g”
    app:layout_constrainteden_tostartof=“@+id/h”
    app:layout_constrainthorizontal_bias=“0.5”
    app:layout_constraintStart_toendof f=“@+id/f”
    app:layout_constraintop_totopof=“@+id/e”/>
    
    按钮
    安卓:id=“@+id/h”
    android:layout_width=“包装内容”
    android:layout_height=“包装内容”
    android:layout_marginbottom=“8dp”
    安卓:text=“h”
    app:layout_constraintBottom_tobottomof=“父级”
    app:layout_constrainted_toendoff=“父级”
    app:layout_constrainthorizontal_bias=“0.5”
    app:layout_constraintStart_toendoff=“@+id/g”
    app:layout_constraintop_totopof=“@+id/g”/>
    </android.support.constraint.constraintlayout>
    < /代码> 
    
    

    popup.xml
    只是弹出窗口。

    <linearlayout
    android:layout_width=“匹配父级”
    android:layout_height=“包装内容”
    xmlns:tools=“http://schemas.android.com/tools”
    android:background=“@android:color/black”
    android:gravity=“中心”
    android:orientation=“垂直”
    android:padding=“3dp”>
    
    文本视图
    android:id=“@+id/popupkey”
    android:layout_width=“包装内容”
    android:layout_height=“50dp”
    安卓:text=“a”
    android:textcolor=“@android:color/white”/>
    
    </linearlayout>
    < /代码> 
    
    

    活动\主

    <android.support.constraint.constraintlayout
    android:layout_width=“匹配父级”
    android:layout_height=“匹配父级”
    工具:context=“mainActivity”>
    
    编辑文本
    android:layout_width=“包装内容”
    android:layout_height=“包装内容”
    android:hint=“高文本”
    app:layout_constraintBottom_tobottomof=“父级”
    app:layout_constraintleft_tolefto=“父级”
    app:layout_constraintricht_torightof=“父级”
    app:layout_constraintop_totopof=“parent”/>
    
    按钮
    android:id=“@+id/按钮”
    android:layout_width=“包装内容”
    android:layout_height=“包装内容”
    android:layout_marginstart=“8dp”
    android:layout_marginend=“8dp”
    android:layout_marginbottom=“20dp”
    android:text=“按钮”
    app:layout_constraintBottom_tobottomof=“父级”
    app:layout_constrainted_toendoff=“父级”
    app:layout_constraintStart_toStartof=“parent”/>
    
    编辑文本
    android:id=“@+id/edittext”
    android:layout_width=“133dp”
    android:layout_height=“包装内容”
    android:layout_marginstart=“16dp”
    安卓:ems=“10”
    android:inputType=“文本人员姓名”
    android:hint=“低文本”
    app:layout_constraintStart_toStartof=“父级”
    app:layout_constraintop_totopof=“@+id/button”/>
    
    </android.support.constraint.constraintlayout>
    < /代码> 
    g在Windows之外可能是Android生活的一个新事实,尽管我没有找到相关文档。不管怎样,下面的方法可能是首选的方法,我相信,它是标准的,尽管没有很好的文档记录。

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

    enter image description here

    按钮和“低文本”EditText在顶视图条下。调用onComputeInsets()允许触摸键盘键,但不允许在插页覆盖的空白区域触摸键盘。在这个区域中,触摸被传递到底层视图-这里是“低文本”编辑文本和AButton显示“OK!”点击时。

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

    enter image description here

    我的输入方法服务

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

    XML键盘
    这个View仅为给弹出窗口一个扩展的位置而定义,没有其他用途。

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

    Popux.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>
    
        2
  •  3
  •   aminography    6 年前

    显示弹出视图的一般想法是使用 WindowManager 它没有限制 PopupWindow .

    我想 InputMethodService 负责显示弹出视图。 由于显示这样的窗口需要在API 23和更高版本中获得覆盖权限,因此我们需要 Activity 为我们做这件事。获得许可的结果将发送到 输入方法服务 使用一个 EventBus 事件。您可以根据体系结构检查所需的覆盖权限(例如,每次键盘启动时)。

    下面是这个想法的一个实现,可能需要一些操作才能按您想要的方式工作。希望能有所帮助。

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

    floatview管理器.java

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

    检查PermissionActivity.java

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

    candrawoverlaysevent.java语言

    public class CanDrawOverlaysEvent {
    
        private boolean mIsAllowed;
    
        public CanDrawOverlaysEvent(boolean isAllowed) {
            mIsAllowed = isAllowed;
        }
    
        public boolean isAllowed() {
            return mIsAllowed;
        }
    
    }
    

    平地

    dependencies {
        implementation 'org.greenrobot:eventbus:3.1.1'
    }
    
    推荐文章