【问题标题】:Creating a system overlay where the home buttons still work?创建主页按钮仍然有效的系统覆盖?
【发布时间】:2015-09-24 15:17:57
【问题描述】:

我正在尝试创建一个始终显示在屏幕上的按钮。

  1. 该按钮应该是可点击的并且 按钮正下方的任何内容 不应在印刷机上激活。
  2. 正在运行的活动或主屏幕 按钮后面应该仍然可以工作, 意味着用户仍然应该是 能够与家互动 屏幕或应用程序。
  3. 软键 按钮应该仍然有效:主页, 返回、菜单等

以下代码执行 #1 和 #2,但软键按钮不再起作用:

WindowManager.LayoutParams params = new WindowManager.LayoutParams(
        WindowManager.LayoutParams.WRAP_CONTENT,
        WindowManager.LayoutParams.WRAP_CONTENT,
        WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
        WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
        WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
        PixelFormat.TRANSLUCENT);

将其更改为此会禁用覆盖点击,但 #2 和 #3 有效:

WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,

最后,在这个例子中,覆盖层及其后面的内容被点击:

WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,

如何更改此设置以使叠加层可点击,直接位于其下方的内容不可点击,并且叠加层之外的所有内容(包括主页按钮)都可以正常工作?

一个执行所有这些的示例应用程序是超级管理器。

更新:我发现以下允许使用主页按钮,但仍然不能使用其他按钮:

        WindowManager.LayoutParams.TYPE_SYSTEM_ALERT | 
            WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
        WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
            WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH

,

【问题讨论】:

  • 您能否发布最终答案或更新您的问题?另外,你用来构建它的权限是什么?

标签: android


【解决方案1】:
  1. 您不能将窗口类型组合在一起。您将创建一些随机的其他类型。老实说,您使用的窗口类型确实不是供应用使用的(这就是为什么它们中有“系统”一词)。

  2. “软键按钮不再起作用”是什么意思?如果这是一个大问题(我想知道执行此操作的代码),那么应该不可能停止在家工作。其他键被传递到当前键焦点;如果您不想集中注意力,请使用FLAG_NOT_FOCUSABLE

  3. 每个标志的文档都应该非常清楚它的作用,所以选择你想要的标志。 FLAG_NOT_FOCUSABLE 因为你不想参加关键事件。 FLAG_NOT_TOUCH_MODAL 因为您不想阻止窗口之外的触摸事件。你不会说你想了解窗外的印刷机,所以没有理由使用FLAG_WATCH_OUTSIDE_TOUCH

【讨论】:

  • 哇,谢谢!使用带有标志 not_focusable 的 type_system_alert | not_touch_modal 成功了。按钮或选择器不显示他们点击的动画,但我已经通过使用 OnClick 的点击和 OnTouch 来改变 action_down 和 action_up 的背景。
  • “应该不可能让家里停止工作。”实际上@pcm2a 是正确的。我已经在库存的 Motorola Droid 3 上对此进行了测试,带有上述标志的 TYPE_SYSTEM_ERROR 或 TYPE_SYSTEM_ALERT 窗口可以消耗包括 HOME 键在内的关键事件(事实上,即使未消耗事件,默认行为也不会发生)。这很奇怪,因为我的印象是这是任何应用程序都无法拦截的一个键。
  • @Tom 我认为当 Dianne 说不可能使软键按钮不起作用时,她的意思是不可能阻止用户单击软键按钮。当然,您可以让您的窗口覆盖按钮的默认行为,但您不能“禁用”主页按钮(即使其不可点击),例如。这是因为这些按钮作为系统拥有的单独窗口的一部分存在。
  • 顺便说一句:@pcm2a 是对的。硬件主页按钮不适用于 Android 4.0.4 上的 FLAG_NOT_FOCUSABLE,在模拟器和真实设备上都经过测试;-)
  • @pcm2a 你的评论让我免于陷入混乱。感谢所有做出贡献的人。
【解决方案2】:

我看到 pcm2a 没有发布可行的解决方案,所以我不妨为他做。

注意:虽然我提出了一个可行的解决方案,但就 Android 架构设计而言,使用这些方法并不是 100% 正确的。我建议向 Android 开发团队的人员询问更多信息(请在此处发布他们的答案以供我们所有人查看)。

权限

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

服务

package com.vidmind.test.service.video;
import android.app.Service;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnPreparedListener;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.VideoView;

public class TestVideoService extends Service {

    /** Command to the service to display a message */
    public static final int MSG_PLAY_VIDEO = 1;
    public static final int MSG_VIDEO_LOOP_MODE = 2;


    /** Bundle Strings */
    public static final String KEY_VIDEO_URL = "KEY_VIDEO_URL";
    public static final String KEY_VIDEO_LOOP_MODE = "KEY_VIDEO_LOOP_MODE";


    // Binder given to clients
    private static final String TAG = "TestVideoService";
    final Messenger mMessenger = new Messenger(new IncomingHandler());
    private LinearLayout mOverlay;
    private VideoView mVideoView;
    private boolean mVideoLoop = true;


    /** ****************** Handler implementation class ****************** **/

    /**
     * Handler of incoming messages from clients.
     */
    class IncomingHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            Bundle bundle;
            switch (msg.what) {
                case MSG_PLAY_VIDEO:
                    bundle = msg.getData();
                    String url = bundle.getString(KEY_VIDEO_URL);
                    play(url);
                    break;

                case MSG_VIDEO_LOOP_MODE:
                    bundle = msg.getData();
                    boolean looping = bundle.getBoolean(KEY_VIDEO_LOOP_MODE);
                    setVideoLoop(looping);
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    /** ****************************************************************** **/

    @Override
    public IBinder onBind(Intent intent) {
        return mMessenger.getBinder();
    }


    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "Service has started");

    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "Serice destroyed");

        // Remove view from WindowManager
        WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
        wm.removeView(mOverlay);

        mOverlay = null;
        mVideoView = null;
    }


    /** ****************** Private Functions ****************** **/

    /**
     * Set video loop mode
     * @param value
     */
    private void setVideoLoop(boolean value) {
        mVideoLoop = value;
    }

    /**
     * Start playing the movie
     * @param url
     * @returns success/failure
     */
    private boolean play(String url) {
        boolean isSuccess = false;

        if (mOverlay != null && mVideoView.isPlaying()) {
            Log.w(TAG, "Cannot recreate video overlay");
            return isSuccess;
        }

        // Create overlay video
        createOverlay(mOverlay != null);
        if (!mVideoView.isPlaying()) {
            mVideoView.setOnPreparedListener(new OnPreparedListener() {

                @Override
                public void onPrepared(MediaPlayer mp) {
                    mp.setLooping(mVideoLoop);
                } 
            });

            mVideoView.setVideoURI(Uri.parse(url));
            mVideoView.requestFocus();
            mVideoView.start();
            isSuccess = true;
        }
        return isSuccess;
    }

    /**
     * Create video overlay
     * 
     * @param isCreated
     */
    private void createOverlay(boolean isCreated) {
        if (isCreated) return;

        // Create System overlay video
        WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.FILL_PARENT, 150,
                WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
                PixelFormat.TRANSLUCENT);
        params.gravity = Gravity.BOTTOM;

        LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
        mOverlay = (LinearLayout) inflater.inflate(R.layout.main, null);
        mVideoView = (VideoView) mOverlay.findViewById(R.id.video_player);

        WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
        wm.addView(mOverlay, params);

    }
}

layout.main

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <VideoView
        android:id="@+id/video_player"
        android:layout_width="120dp"
        android:layout_height="120dp"/>

</LinearLayout>

编码愉快!
阿米尔

【讨论】:

    猜你喜欢
    • 2015-05-14
    • 1970-01-01
    • 2012-04-19
    • 1970-01-01
    • 2023-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多