【问题标题】:Back pressed events with system alert window带有系统警报窗口的后按事件
【发布时间】:2015-06-16 07:15:02
【问题描述】:

我需要在按下backhome 按钮事件时关闭系统警报窗口。我曾尝试使用onKeyEvent 但徒劳无功。由于我们无法在服务中捕获backpressed 事件,如何实现呢?

【问题讨论】:

  • 你可以通过重写 onBackButtonpressed 方法来做到这一点
  • 你能添加一段显示对话框的代码吗?
  • 系统警报窗口是我用来绘制其他应用程序的窗口。我想处理按键事件,例如从辅助功能服务中按下返回、按下主页按钮。

标签: android back-button android-homebutton


【解决方案1】:

由于它是一个托管覆盖窗口的服务,这是一个有点棘手的解决方案,但它是可能的。

您应该分别处理这两种情况(覆盖主页按钮按下和返回按钮按下)。


1.覆盖主页按钮按下:

创建这个 HomeWatcher 类,它包含一个 BroadcastReceiver,它会在按下主页按钮时发出通知。仅当您的窗口出现时才注册此接收器。

Android: associate a method to home button of smartphone

在你的服务 onCreate 方法中使用这个:

HomeWatcher mHomeWatcher = new HomeWatcher(this);
mHomeWatcher.setOnHomePressedListener(new OnHomePressedListener() {
    @Override
    public void onHomePressed() {
        yourWindow.hide()  //means: windowManager.removeView(view);
    }
    @Override
    public void onHomeLongPressed() {
    }
});
mHomeWatcher.startWatch();

2。覆盖后退按钮按下:

这个想法是创建一个空布局作为窗口类的数据成员, 并将你的视图附加到它上面(即使它是一个膨胀的 XML 布局)。

例如,这将是您的窗口类:

public class MyWindow
{
    private WindowManager windowManager;
    private WindowManager.LayoutParams params;
    private View view;

    // Add this empty layout:
    private MyLayout myLayout;

    public MyWindow()
    {
        windowManager = (WindowManager) context.getSystemService(context.WINDOW_SERVICE);
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        view = inflater.inflate(R.layout.your_original_window_layout, null);

        // Add your original view to the new empty layout:
        myLayout = new MyLayout(this);
        myLayout.addView(view, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
    }

    // And show this layout instead of your original view:
    public void show()
    {
        windowManager.addView(myLayout, params);
    }

    public void hide()
    {
        windowManager.removeView(myLayout);
    }
}

现在创建 MyLayout 类来覆盖后退按钮按下:

public class MyLayout extends LinearLayout
{
    private MyWindow myWindow;

    public MyLayout(MyWindow myWindow)
    {
        super(myWindow.context);

        this.myWindow = myWindow;
    }


    @Override public boolean dispatchKeyEvent(KeyEvent event)
    {
        if (event.getKeyCode() == KeyEvent.KEYCODE_BACK)
        {
            if (event.getAction() == KeyEvent.ACTION_DOWN  &&  event.getRepeatCount() == 0)
            {
                getKeyDispatcherState().startTracking(event, this);
                return true;

            }

            else if (event.getAction() == KeyEvent.ACTION_UP)
            {
                getKeyDispatcherState().handleUpEvent(event);

                if (event.isTracking() && !event.isCanceled())
                {
                    // dismiss your window:
                    myWindow.hide();

                    return true;
                }
            }
        }

        return super.dispatchKeyEvent(event);
    }
}

我知道这有点复杂,因为它是由服务托管的系统警报窗口,但它正在工作。我也有同样的问题,它已经完全解决了。 祝你好运。

【讨论】:

  • 谢谢,我会尽快尝试的。
  • 您可以下载我的应用程序,以对其中的系统警报窗口行为印象深刻,如果您愿意,可以问我更多问题,我会给您一些提示和代码示例。基本上,系统警报覆盖窗口需要大量的关注、管理和额外的维护,因为它们不是活动。 play.google.com/store/apps/details?id=com.accio.app
  • 在 MyWindow 类中 -> 什么是“上下文”声明中没有变量
  • 这不是对主页按钮的覆盖......但它是一个扩展......主页按钮操作也会执行:-(
  • dispatchKeyEvent 从未调用过
【解决方案2】:

使用下面的方法来处理按下的返回按钮。

  public void onBackPressed()
  {
        super.onBackPressed();
  }

【讨论】:

    【解决方案3】:

    您需要覆盖onBackPressed 方法。

    @Override
    public void onBackPressed() {
    
        super.onBackPressed(); // remove this if u want to handle this event
    }
    

    【讨论】:

      【解决方案4】:

      使用下面的代码

      public boolean onKeyDown(int keyCode, KeyEvent event) {
          if (keyCode == KeyEvent.KEYCODE_BACK) {
              exitByBackKey();
      
              //moveTaskToBack(false);
      
              return true;
          }
          return super.onKeyDown(keyCode, event);
          }
      
          protected void exitByBackKey() {
      
          AlertDialog alertbox = new AlertDialog.Builder(this)
          .setMessage("Do you want to exit application?")
          .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
      
              // do something when the button is clicked
              public void onClick(DialogInterface arg0, int arg1) {
      
                  finish();
                  //close();
      
      
              }
          })
          .setNegativeButton("No", new DialogInterface.OnClickListener() {
      
              // do something when the button is clicked
              public void onClick(DialogInterface arg0, int arg1) {
                             }
          })
            .show();
      
          }
      

      【讨论】:

        【解决方案5】:
        @Override
        public void onBackPressed()
        {
               super.onBackPressed();
        }
        

        在您的活动中声明这一点。 super.OnBackPressed 自动回调android中的方法。它肯定会取消你的对话。

        此外,您的对话框必须如下所示。

        AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder1.setMessage("TEST DIALOG.\n");
            builder1.setPositiveButton("Ok",
                    new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int id) {
                        Toast.makeText(MainActivity.this, "This Is test Dialog", Toast.LENGTH_SHORT).show();
                }
            });
            AlertDialog alert11 = builder1.create();
            alert11.show();
        

        或者你可以设置负按钮..

        希望这会有所帮助!

        【讨论】:

          【解决方案6】:

          定义自定义布局并覆盖dispatchKeyEvent,例如:

          public class CustomSystemAlertWindow extends FrameLayout {
          
              public static final String TAG = "CustomSystemAlertWindow";
          
              private WeakReference<Context> mContext;
          
              public CustomSystemAlertWindow(Context context) {
                  super(context);
          
                  mContext = new WeakReference<Context>(context);
          
                  // Set a background color to identify the view on the screen
                  this.setBackgroundColor(getResources().getColor(android.R.color.holo_red_light));
              }
          
              @Override
              public boolean dispatchKeyEvent(KeyEvent event) {
                  if (event != null && event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
                      Log.d(TAG, "back button pressed");
          
                      if (mContext != null && mContext.get() != null) {
                          WindowManager wm = (WindowManager) mContext.get().getSystemService(Context.WINDOW_SERVICE);
                          wm.removeView(this);
                      }
          
                      return true;
                  }
          
                  return super.dispatchKeyEvent(event);
              }
          }
          

          然后使用以下代码添加视图:

          CustomSystemAlertWindow customSystemAlertWindow = new CustomSystemAlertWindow(context);
          
          WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                  WindowManager.LayoutParams.MATCH_PARENT,
                  WindowManager.LayoutParams.MATCH_PARENT,
                  WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
                  WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
                  PixelFormat.TRANSLUCENT);
          
          WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
          wm.addView(customSystemAlertWindow, params);
          

          当您按下返回按钮时,视图将关闭。

          【讨论】:

          • 发布您的代码,否则真的很难帮助您。由于问题不是您的问题,您可以将其发布到 pastebin.com 或类似服务。
          • 类似的不一样。答案的代码和你的有什么区别?
          • 我的布局是从一个预定义的 XML 扩展而来的,它的根是“CustomsystemAlertWindow”
          • 我刚刚尝试使用 XML,它对我有用。您确定使用相同的答案标志来创建WindowManager.LayoutParams
          【解决方案7】:
          1. 通过 Activity 显示警报窗口,以便您检测它。

          实现代码以轻松检测返回按钮或主页按钮被按下。

          public class alertPopup extends Activity {
          
              Context context;
              final AlertDialog alertDialog;
              String TAG = "your Activity Name"
              boolean homePressed = false; // to detect the Homebutton pressed
          
              @Override
              public void onCreate(Bundle savedInstanceState) {
               AlertDialog.Builder builder = newAlertDialog.Builder(YourActivity.this, R.style.AppCompatAlertDialogStyle);
               builder.setTitle("AlertDialog Title");
                      ..........
                      ....... // Build ur AlertDialog
          
               alertDialog= builder.create();
               alertDialog.show();
          
          
                   //to detect Alert Dialog cancel when user touches outside the Dialog prompt
               alertDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
                      @Override
                      public void onCancel(DialogInterface dialog) {
                          Log.v(TAG,"Alert Dialog cancelled when user touches outside the Dialog prompt")
                      }
                  });
          
              }
          
          
              @Override
              public void onBackPressed()
              {
              Log.v(TAG,"Back Button Pressed");
               super.onBackPressed();
          
               alertDialog.dismiss();   //dismiss the alertDialog
               alertPopup.this.finish();  // Destroy the current activity
          
               homePressed = false;
              }
          
              @Override
              public void onResume() {
                  super.onResume();
                  homePressed = true; // default: other wise onBackPressed will set it to false
              }
          
          
              @Override
              public void onPause() {
                  super.onPause();
                  if(homePressed) { 
          
                  alertDialog.dismiss();   //dismiss the alertDialog
                  alertPopup.this.finish();  // Destroy the current activity
          
                  Log.v(TAG, "Home Button Pressed"); }
              }
          
          
              public void onDestroy(){
          
                  super.onDestroy();
              }
          
          
          }
          

          注意:

          在 Android Manifest 中添加此权限以显示警报窗口。

           <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
          

          快乐编码:)

          【讨论】:

          • 请注意,问题是关于从服务启动的“系统警报窗口”(不涉及任何活动)。
          • 是的,我知道。如果您想检测按下后退按钮和主页按钮,您应该通过服务显示活动。还有其他方法吗?@Yaadm
          • 我会尽快检查的。我不知道是否还有其他方法,这就是我问的原因......另外,我会尝试反编译 FB messanger 应用程序,看看他们是如何做到的。
          • 好的。我给出了我正确知道的解决方案之一。不要在你不知道的情况下提问。
          【解决方案8】:

          我了解到您正在使用&lt;uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /&gt; 权限来显示浮动视图。

          使用浮动视图可以拦截back 按钮按下,但不能拦截home 按钮按下(android 不会让你主要是出于安全原因)。

          要拦截back 按钮按下,您需要在膨胀浮动视图时添加包装器。 您的包装器应如下所示:

              // Wrapper for intercepting System/Hardware key events
              ViewGroup wrapper = new FrameLayout(this) {
                  @Override
                  public boolean dispatchKeyEvent(KeyEvent event) {
                      if (event.getKeyCode()==KeyEvent.KEYCODE_BACK) {
                          hideAddNotesFloatingView();
                          return true;
                      }
                      return super.dispatchKeyEvent(event);
                  }
              };
          

          然后将其作为根添加到浮动视图中:

                  mAddNoteFloatingView = mInflater.inflate(R.layout.floating_add_note, wrapper);
          

          我的完整代码如下所示:

          private void addFloatingView() {
              final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                      WindowManager.LayoutParams.MATCH_PARENT,
                      WindowManager.LayoutParams.MATCH_PARENT,
                      WindowManager.LayoutParams.TYPE_PHONE,
                      0,
                      PixelFormat.TRANSLUCENT);
          
              params.gravity = Gravity.CENTER | Gravity.LEFT;
              params.x = 0;
              params.y = 0;
          
              // Wrapper for intercepting System/Hardware key events
              FrameLayout wrapper = new FrameLayout(this) {
                  @Override
                  public boolean dispatchKeyEvent(KeyEvent event) {
                      if (event.getKeyCode()==KeyEvent.KEYCODE_BACK) {
                          // Add your code for handling the back button press
                          return true; // Return true means that the event was handled
                      }
                      return super.dispatchKeyEvent(event);
                  }
              };
          
              mAddNoteFloatingView = mInflater.inflate(R.layout.floating_view, wrapper);
          
              mWindowManager.addView(mAddNoteFloatingView, params);
          }
          

          【讨论】:

          • 您将 mAddNoteFloatingView 添加到 windowManager 而不是包装器,为什么? wrapper 是最顶层的视图,应该添加到 WM 中吧?
          • @shantanu 在这种情况下,mAddNoteFloatingViewwrapper 指的是同一个视图。 LayoutInflater.inflate 返回:“膨胀层次结构的根视图。如果提供了根,这是根视图;否则它是膨胀 XML 文件的根。”
          【解决方案9】:

          这很简单。请按以下步骤操作:

          1. 动态创建一个视图,如相对布局、线性布局或框架布局。 2. 在创建视图时覆盖dispatchKeyEvent
          2. 使用addView() 方法将您的原始视图添加到这个动态创建的视图中。
          3. 将动态创建的视图添加到您想要的窗口管理器或警报对话框中。

          【讨论】:

            【解决方案10】:

            除了@Eliran Kuta 的解决方案,这是返回按钮的更简单的答案。

            val view = getAlertView()
            val windowParam = WindowManager.LayoutParams(
                WindowManager.LayoutParams.WRAP_CONTENT, // whatever
                WindowManager.LayoutParams.WRAP_CONTENT, // whatever
                WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, // use WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY before Oreo
                WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, // whatever
                PixelFormat.TRANSLUCENT // whatever
            )
            
            view.isFocusableInTouchMode = true
            view.setOnKeyListener { view, keyCode, event ->
                when (keyCode) {
                    KeyEvent.KEYCODE_BACK -> {
                        // do your work here
                        true
                    }
                    else -> false
                }
            }
            
            val windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager
            windowManager.addView(view, windowParam)
            

            【讨论】:

              猜你喜欢
              • 2022-01-06
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2015-12-28
              相关资源
              最近更新 更多