创建一个系统覆盖窗口(总是在顶部)

我试图创建一个始终在顶部按钮/点击图像 它一直在所有的窗口上面。< / p >

证明 概念是

我已经成功了,现在有一个正在运行的服务。服务 在屏幕左上角一直显示一些文本 用户可以自由地与其他应用程序进行正常的交互。< / p > < p >我什么 ViewGroup的子类,并将其添加到根窗口管理器 国旗TYPE_SYSTEM_OVERLAY。现在我想添加一个按钮/可点击的图像 在这个文本的地方,它可以接收自己的触摸事件。我 尝试重写整个ViewGroup的"onTouchEvent",但它确实如此 没有收到任何事件。< / p >

我如何只能在某些部分上接收事件 我总是在顶部的视图组?请建议。< / p >

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;
}
}
273081 次浏览

试试我的代码,至少它给了你一个字符串作为覆盖,你可以很好地用一个按钮或图像替换它。你不会相信这是我的第一个安卓应用,哈哈。无论如何,如果你比我更有经验的android应用程序,请尝试

  • 在“new WindowManager”中更改参数2和3。LayoutParams”
  • 尝试一些不同的事件方法

这可能是一个愚蠢的解决方案。但它确实有效。如果你能改进,请让我知道。

OnCreate你的服务:我已经使用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);
}

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

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

此外,您可能需要将此权限添加到您的清单:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
从Android 4开始。x, Android团队修复了一个潜在的 通过添加一个新函数adjustWindowParamsLw()来解决安全问题 将为TYPE_SYSTEM_OVERLAY窗口添加FLAG_NOT_FOCUSABLEFLAG_NOT_TOUCHABLE并删除FLAG_WATCH_OUTSIDE_TOUCH标志

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

以下@Sam Lu的回答, 确实是Android 4。x阻止某些类型监听外部触摸事件,但一些类型,如TYPE_SYSTEM_ALERT,仍然做这项工作。
< / p >

例子

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

权限

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
< p >试试这个。在ICS中工作正常。 如果您想停止服务,只需单击状态栏中生成的通知。< / p >
 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);
}
}

更新

样本在这里

要创建一个覆盖视图,在设置LayoutParams时不要将类型设置为TYPE_SYSTEM_OVERLAY。

Instead set it to TYPE_PHONE.


Use the following flags:


FLAG_NOT_TOUCH_MODAL


FLAG_WATCH_OUTSIDE_TOUCH

工作总是在顶部图像按钮

首先,我的英语不好

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

此外,它还提供了触摸监听器,以排除其他元素

按钮排列在底部和左侧

你可以改变对齐,但你需要在if元素的触摸事件中改变坐标

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;
}
}
这里有一些简单的解决方案, 您所需要做的就是像在列表适配器上做的那样膨胀XML布局, 只是做XML布局来膨胀它。

.

.
 public class HUD extends Service {
View mView;


LayoutInflater inflate;
TextView t;
Button b;


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


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




WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);


Display display = wm.getDefaultDisplay();  get phone display size
int width = display.getWidth();  // deprecated - get phone display width
int height = display.getHeight(); // deprecated - get phone display height




WindowManager.LayoutParams params = new WindowManager.LayoutParams(
width,
height,
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.CENTER;
params.setTitle("Load Average");


inflate = (LayoutInflater) getBaseContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);


mView = inflate.inflate(R.layout.canvas, null);


b =  (Button) mView.findViewById(R.id.button1);
t = (TextView) mView.findViewById(R.id.textView1);
b.setOnClickListener(new OnClickListener() {


public void onClick(View v) {
// TODO Auto-generated method stub
t.setText("yes you click me ");
}
});


wm.addView(mView, params);


}






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


}

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

这里的答案没有提到的一个问题是Android的“安全按钮”。

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

指定当视图窗口被遮挡时是否过滤触摸 通过另一扇可见的窗户。当设置为true时,视图将不接收 控件上方出现吐司、对话框或其他窗口时,进行触摸 视图的窗口。参考{@link android.view。视图}安全

.

当你试图安装第三方apks时,这样的按钮的一个例子是安装按钮。任何应用程序都可以显示这样一个按钮,如果添加到视图布局下面的行:

android:filterTouchesWhenObscured="true"
如果你在一个“安全按钮”上显示一个始终在顶部的窗口,那么所有被覆盖覆盖的安全按钮部分将不会处理任何触摸,即使覆盖是不可点击的。因此,如果您计划显示这样一个窗口,您应该为用户提供一种方法来移动它或关闭它。 如果你的覆盖部分是透明的,要考虑到你的用户可能会感到困惑,为什么底层应用中的某个按钮突然不工作了

如果有人还在阅读这篇文章,不能让这个工作,我很抱歉地告诉你这种拦截运动事件的方式被认为是错误和修复在android >=4.2。

你截获的运动事件,尽管有动作作为ACTION_OUTSIDE,在getX中返回0 和getY。这意味着你不能在屏幕上看到所有的运动位置,也不能做任何事情。我知道医生说它会得到x和y,但事实是它不会。这似乎是阻止密钥记录器。

如果有人有解决办法,请留下你的评论。

< p >参考: 为什么ACTION_OUTSIDE每次在奇巧4.4.2上都返回0 ? < / p >

https://code.google.com/p/android/issues/detail?id=72746

它使用权限“android.permission”。SYSTEM_ALERT_WINDOW"关于此链接的完整教程:http://androidsrc.net/facebook-chat-like-floating-chat-heads/

通过使用service,您可以实现以下目标:

public class PopupService extends Service{


private static final String TAG = PopupService.class.getSimpleName();
WindowManager mWindowManager;
View mView;
String type ;


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


@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//        registerOverlayReceiver();
type = intent.getStringExtra("type");
Utils.printLog("type = "+type);
showDialog(intent.getStringExtra("msg"));
return super.onStartCommand(intent, flags, startId);
}


private void showDialog(String aTitle)
{
if(type.equals("when screen is off") | type.equals("always"))
{
Utils.printLog("type = "+type);
PowerManager pm = (PowerManager) getApplicationContext().getSystemService(Context.POWER_SERVICE);
WakeLock mWakeLock = pm.newWakeLock((PowerManager.SCREEN_DIM_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP), "YourServie");
mWakeLock.acquire();
mWakeLock.release();
}


mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
mView = View.inflate(getApplicationContext(), R.layout.dialog_popup_notification_received, null);
mView.setTag(TAG);


int top = getApplicationContext().getResources().getDisplayMetrics().heightPixels / 2;


LinearLayout dialog = (LinearLayout) mView.findViewById(R.id.pop_exit);
//        android.widget.LinearLayout.LayoutParams lp = (android.widget.LinearLayout.LayoutParams) dialog.getLayoutParams();
//        lp.topMargin = top;
//        lp.bottomMargin = top;
//        mView.setLayoutParams(lp);


final EditText etMassage = (EditText) mView.findViewById(R.id.editTextInPopupMessageReceived);


ImageButton imageButtonSend = (ImageButton) mView.findViewById(R.id.imageButtonSendInPopupMessageReceived);
//        lp = (LayoutParams) imageButton.getLayoutParams();
//        lp.topMargin = top - 58;
//        imageButton.setLayoutParams(lp);
imageButtonSend.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Utils.printLog("clicked");
//                mView.setVisibility(View.INVISIBLE);
if(!etMassage.getText().toString().equals(""))
{
Utils.printLog("sent");
etMassage.setText("");
}
}
});


TextView close = (TextView) mView.findViewById(R.id.TextViewCloseInPopupMessageReceived);
close.setOnClickListener(new OnClickListener() {


@Override
public void onClick(View arg0) {
hideDialog();
}
});


TextView view = (TextView) mView.findViewById(R.id.textviewViewInPopupMessageReceived);
view.setOnClickListener(new OnClickListener() {


@Override
public void onClick(View arg0) {
hideDialog();
}
});


TextView message = (TextView) mView.findViewById(R.id.TextViewMessageInPopupMessageReceived);
message.setText(aTitle);


final WindowManager.LayoutParams mLayoutParams = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT, 0, 0,
WindowManager.LayoutParams.TYPE_SYSTEM_ERROR,
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
//                | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON ,
PixelFormat.RGBA_8888);


mView.setVisibility(View.VISIBLE);
mWindowManager.addView(mView, mLayoutParams);
mWindowManager.updateViewLayout(mView, mLayoutParams);


}


private void hideDialog(){
if(mView != null && mWindowManager != null){
mWindowManager.removeView(mView);
mView = null;
}
}
}

实际上,你可以试试WindowManager.LayoutParams。TYPE_SYSTEM_ERROR而不是TYPE_SYSTEM_OVERLAY。这可能听起来像一个黑客,但它让你显示视图顶部的一切,仍然得到触摸事件。

@Sarwar Erfan的答案不再有效,因为Android不允许使用WindowManager.LayoutParams添加视图。窗口的TYPE_SYSTEM_OVERLAY不再是可触摸的,甚至不是windowmanager . layoutparam . flag_watch_outside_touch。

我找到了解决这个问题的办法。你可以看看下面的问题

当使用WindowManager.LayoutParams添加视图到窗口时。TYPE_SYSTEM_OVERLAY,它没有得到触摸事件

找到一个这样做的库: https://github.com/recruit-lifestyle/FloatingView < / p >

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

enter image description here

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

这是一个老问题,但最近Android支持泡泡。泡泡很快就会推出,但目前开发者可以开始使用它们。它们被设计为使用SYSTEM_ALERT_WINDOW的替代方案。Facebook Messenger和MusiXMatch等应用程序使用了相同的概念。

Bubbles

气泡是通过通知API创建的,您可以正常发送通知。如果你想让它冒泡,你需要附加一些额外的数据。有关Bubbles的更多信息,您可以访问官方Android开发者指南泡泡

TYPE_SYSTEM_OVERLAY已弃用,将给出权限拒绝错误。 使用WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY代替

WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL可能是你要找的。

窗口标志:即使这个窗口是可聚焦的(它的FLAG_NOT_FOCUSABLE没有设置),也允许窗口外的任何指针事件被发送到它后面的窗口。否则,它将消耗所有指针事件本身,不管它们是否在窗口内。

Docs

实现View.OnClickListenerView.onTouchListener来监听触摸事件。