创建系统覆盖窗口(始终在顶部)

cor*_*OLO 279 android overlay

我正在尝试创建一个始终操作顶部按钮/可点击图像,它始终位于所有窗口的顶部.

概念证明是

我已经成功并且现在有一个正在运行的服务.该服务始终在屏幕的左上角显示一些文本,而用户可以正常方式与其他应用程序自由交互.

我正在做的是子类ViewGroup并将其添加到带有标志的根窗口管理器TYPE_SYSTEM_OVERLAY.现在我想添加一个按钮/可点击图像来代替这个文本,它可以自己接收触摸事件.我尝试覆盖整个"onTouchEvent",ViewGroup但它没有收到任何事件.

如何仅在我的常见顶视图组的某些部分接收活动?请建议.

public class HUD extends Service {
    HUDView mView;

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Toast.makeText(getBaseContext(),"onCreate", Toast.LENGTH_LONG).show();
        mView = new HUDView(this);
        WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
                0,
//              WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
//                      | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
                PixelFormat.TRANSLUCENT);
        params.gravity = Gravity.RIGHT | Gravity.TOP;
        params.setTitle("Load Average");
        WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
        wm.addView(mView, params);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Toast.makeText(getBaseContext(),"onDestroy", Toast.LENGTH_LONG).show();
        if(mView != null)
        {
            ((WindowManager) getSystemService(WINDOW_SERVICE)).removeView(mView);
            mView = null;
        }
    }
}

class HUDView extends ViewGroup {
    private Paint mLoadPaint;

    public HUDView(Context context) {
        super(context);
        Toast.makeText(getContext(),"HUDView", Toast.LENGTH_LONG).show();

        mLoadPaint = new Paint();
        mLoadPaint.setAntiAlias(true);
        mLoadPaint.setTextSize(10);
        mLoadPaint.setARGB(255, 255, 0, 0);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawText("Hello World", 5, 15, mLoadPaint);
    }

    @Override
    protected void onLayout(boolean arg0, int arg1, int arg2, int arg3, int arg4) {
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //return super.onTouchEvent(event);
        Toast.makeText(getContext(),"onTouchEvent", Toast.LENGTH_LONG).show();
        return true;
    }
}
Run Code Online (Sandbox Code Playgroud)

Sar*_*fan 155

这可能是一个愚蠢的解决方案.但它的确有效.如果你能改进它,请告诉我.

OnCreate of your Service:我用过WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH旗帜.这是服务中唯一的变化.

@Override
    public void onCreate() {
        super.onCreate();
        Toast.makeText(getBaseContext(),"onCreate", Toast.LENGTH_LONG).show();
        mView = new HUDView(this);
        WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
                WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
                PixelFormat.TRANSLUCENT);
        params.gravity = Gravity.RIGHT | Gravity.TOP;
        params.setTitle("Load Average");
        WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
        wm.addView(mView, params);
    }
Run Code Online (Sandbox Code Playgroud)

现在,您将开始获取每个点击事件.因此,您需要在事件处理程序中进行纠正.

在您的ViewGroup触摸事件中

@Override
public boolean onTouchEvent(MotionEvent event) {

    // ATTENTION: GET THE X,Y OF EVENT FROM THE PARAMETER
    // THEN CHECK IF THAT IS INSIDE YOUR DESIRED AREA


    Toast.makeText(getContext(),"onTouchEvent", Toast.LENGTH_LONG).show();
    return true;
}
Run Code Online (Sandbox Code Playgroud)

您还可以将此权限添加到清单中:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
Run Code Online (Sandbox Code Playgroud)

  • 整个代码是正确的,我发现我的错误,我忘了链接<uses-permission android:name ="android.permission.SYSTEM_ALERT_WINDOW"/> Sarwar Erfan我觉得你编辑你的答案,它可以变得更精彩 (6认同)
  • @SarwarErfan - 我在2.3上完全使用了它的corking但是onTouch事件并没有在Android 4.0,1和4.1上进行调整.请帮忙 (6认同)
  • @pengwang:在这里找到最简单的实现:https://docs.google.com/leaf?id = 0B9OlwWEcpDrCNTc1N2Y3NTAtMWJkZi00NzhiLTg4ODgtMDNjZTAxZWIwOTM0&hl = en (3认同)
  • 从 Android Oreo 8.0 开始,您不能使用以下窗口类型:TYPE_PHONE、TYPE_PRIORITY_PHONE、TYPE_SYSTEM_ALERT、TYPE_SYSTEM_OVERLAY、TYPE_SYSTEM_ERROR。您将不得不使用 TYPE_APPLICATION_OVERLAY 并且它不能拖动。 (2认同)

ami*_*ich 65

在@Sam Lu的回答之后,确实Android 4.x会阻止某些类型的外部触摸事件,但某些类型,如TYPE_SYSTEM_ALERT仍然可以完成这项工作.

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

    WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
    LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
    View myView = inflater.inflate(R.layout.my_view, null);
    myView.setOnTouchListener(new OnTouchListener() {
       @Override
       public boolean onTouch(View v, MotionEvent event) {
           Log.d(TAG, "touch me");
           return true;
       }
     });

    // Add layout to window manager
    wm.addView(myView, params);
Run Code Online (Sandbox Code Playgroud)

权限

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
Run Code Online (Sandbox Code Playgroud)

  • 它使我的屏幕被绞死..任何想法 (5认同)

Sam*_* Lu 17

与Android 4.x的开始,Android团队通过添加新功能修正了一个潜在的安全问题adjustWindowParamsLw()将在其中添加FLAG_NOT_FOCUSABLE,FLAG_NOT_TOUCHABLE和删除FLAG_WATCH_OUTSIDE_TOUCH标志的TYPE_SYSTEM_OVERLAY窗口.

也就是说,TYPE_SYSTEM_OVERLAY窗口不会在ICS平台上接收任何触摸事件,当然,使用TYPE_SYSTEM_OVERLAY对于ICS和未来的设备来说不是可行的解决方案.

  • 那有什么选择呢? (4认同)

Onu*_*ürk 12

始终在顶部图像按钮上工作

首先抱歉我的英语

我编辑你的代码并制作工作图像按钮,听他的触摸事件不给他的背景元素触摸控制.

它也为触摸听众提供了其他元素

按钮alingments是bottom和left

你可以使用aling,但你需要在if元素中触摸事件中的cordinats

import android.annotation.SuppressLint;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.os.IBinder;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.Toast;

public class HepUstte extends Service {
    HUDView mView;

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

        Toast.makeText(getBaseContext(),"onCreate", Toast.LENGTH_LONG).show();

        final Bitmap kangoo = BitmapFactory.decodeResource(getResources(),
                R.drawable.logo_l);


        WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                kangoo.getWidth(), 
                kangoo.getHeight(),
                WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                |WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                |WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
                 PixelFormat.TRANSLUCENT);






        params.gravity = Gravity.LEFT | Gravity.BOTTOM;
        params.setTitle("Load Average");
        WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);



        mView = new HUDView(this,kangoo);

        mView.setOnTouchListener(new OnTouchListener() {


            @Override
            public boolean onTouch(View arg0, MotionEvent arg1) {
                // TODO Auto-generated method stub
                //Log.e("kordinatlar", arg1.getX()+":"+arg1.getY()+":"+display.getHeight()+":"+kangoo.getHeight());
                if(arg1.getX()<kangoo.getWidth() & arg1.getY()>0)
                {
                 Log.d("t?kland?", "touch me");
                }
                return false;
            }
             });


        wm.addView(mView, params);



        }



    @Override
    public IBinder onBind(Intent arg0) {
        // TODO Auto-generated method stub
        return null;
    }

}



@SuppressLint("DrawAllocation")
class HUDView extends ViewGroup {


    Bitmap kangoo;

    public HUDView(Context context,Bitmap kangoo) {
        super(context);

        this.kangoo=kangoo;



    }


    protected void onDraw(Canvas canvas) {
        //super.onDraw(canvas);


        // delete below line if you want transparent back color, but to understand the sizes use back color
        canvas.drawColor(Color.BLACK);

        canvas.drawBitmap(kangoo,0 , 0, null); 


        //canvas.drawText("Hello World", 5, 15, mLoadPaint);

    }


    protected void onLayout(boolean arg0, int arg1, int arg2, int arg3, int arg4) {
    }

    public boolean onTouchEvent(MotionEvent event) {
        //return super.onTouchEvent(event);
       // Toast.makeText(getContext(),"onTouchEvent", Toast.LENGTH_LONG).show();

        return true;
    }
}
Run Code Online (Sandbox Code Playgroud)


Dan*_*nny 12

我是Tooleap SDK的开发人员之一.我们还为开发人员提供了一种始终在顶部窗口和按钮上显示的方法,并且我们已经处理了类似的情况.

这里的答案没有解决的一个问题是Android"安全按钮".

安全按钮具有filterTouchesWhenObscured属性,这意味着如果放置在窗口下,即使该窗口没有接收到任何触摸,它们也无法与之交互.引用Android文档:

指定当视图的窗口被另一个可见窗口遮挡时是否过滤触摸.设置为true时,只要在视图窗口上方出现Toast,对话框或其他窗口,视图就不会接收到触摸.有关更多详细信息,请参阅{@link android.view.View}安全文档.

当您尝试安装第三方apks时,此类按钮的一个示例是安装按钮.如果向视图布局添加以下行,任何应用都可以显示此类按钮:

android:filterTouchesWhenObscured="true"
Run Code Online (Sandbox Code Playgroud)

如果在"安全按钮"上显示始终在顶部的窗口,则覆盖覆盖的所有安全按钮部件将无法处理任何触摸,即使该覆盖不可点击也是如此.因此,如果您计划显示这样的窗口,您应该为用户提供移动或关闭它的方法.如果您的叠加层的一部分是透明的,请考虑到您的用户可能会感到困惑为什么基础应用中的某个按钮突然无法为他工作.


Ov3*_*oad 6

实际上,您可以尝试使用WindowManager.LayoutParams.TYPE_SYSTEM_ERROR而不是TYPE_SYSTEM_OVERLAY.它可能听起来像一个黑客,但它让你在所有东西上显示视图,仍然可以获得触摸事件.


arw*_*Dev 6

TYPE_SYSTEM_OVERLAY自API级别26开始不建议使用此常量。请改用TYPE_APPLICATION_OVERLAY。或**适用于android 8以下的用户

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} else {
    LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_PHONE;
}
Run Code Online (Sandbox Code Playgroud)


Vis*_*nan 5

试试这个.适用于ICS.如果要停止服务,只需单击状态栏中生成的通知即可.

 public class HUD extends Service
 {
    protected boolean foreground = false;
    protected boolean cancelNotification = false;
    private Notification notification;
    private View myView;
    protected int id = 0;
    private WindowManager wm;
    private WindowManager.LayoutParams params;
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    @Override
    public void onCreate() {
        super.onCreate();
       // System.exit(0);
        Toast.makeText(getBaseContext(),"onCreate", Toast.LENGTH_SHORT).show();
        params = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.TYPE_PHONE, WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT);
        params.gravity=Gravity.TOP|Gravity.LEFT;
    wm = (WindowManager) getSystemService(WINDOW_SERVICE);
    inflateview();
    foregroundNotification(1);
    //moveToForeground(1,n,true);
    }     
   @Override
    public void onDestroy() {
        super.onDestroy();
        ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)).cancel(0);
        Toast.makeText(getBaseContext(),"onDestroy", Toast.LENGTH_SHORT).show();
        if(myView != null)
        {
            ((WindowManager) getSystemService(WINDOW_SERVICE)).removeView(myView);
            myView = null;
        }
    }
    protected Notification foregroundNotification(int notificationId) 
   {    
    notification = new Notification(R.drawable.ic_launcher, "my Notification", System.currentTimeMillis());    
        notification.flags = notification.flags | Notification.FLAG_ONGOING_EVENT | Notification.FLAG_ONLY_ALERT_ONCE;   
        notification.setLatestEventInfo(this, "my Notification", "my Notification", notificationIntent());          
        ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)).notify(id, notification);            
        return notification;
    }
    private PendingIntent notificationIntent() {
        Intent intent = new Intent(this, stopservice.class);    
        PendingIntent pending = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);    
        return pending;
    }
    public void inflateview()
    {
         LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
            myView = inflater.inflate(R.layout.activity_button, null);
            myView.setOnTouchListener(new OnTouchListener() {
               @Override
               public boolean onTouch(View v, MotionEvent event) {
                   Toast.makeText(getBaseContext(),"onToasttt", Toast.LENGTH_SHORT).show();
                   return false;
               }
             });
            // Add layout to window manager
            wm.addView(myView, params); 
    }
}
Run Code Online (Sandbox Code Playgroud)

UPDATE

这里有样品

要创建叠加视图,在设置LayoutParams时请勿将类型设置为TYPE_SYSTEM_OVERLAY.

Instead set it to TYPE_PHONE.

Use the following flags:

FLAG_NOT_TOUCH_MODAL

FLAG_WATCH_OUTSIDE_TOUCH
Run Code Online (Sandbox Code Playgroud)


Ali*_*aka 5

找到了一个可以做到这一点的库:https : //github.com/recruit-lifestyle/FloatingView

根文件夹中有一个示例项目。我运行了它,它按要求工作。背景是可点击的 - 即使它是另一个应用程序。

在此处输入图片说明