我正在尝试创建一个始终操作顶部按钮/可点击图像 它始终位于所有窗口的顶部。
证据 概念是
我已经成功并且现在有一个正在运行的服务。服务 一直显示屏幕左上角的一些文字 用户可以正常方式与其他应用程序自由交互。
我是什么
do是子类ViewGroup
并将其添加到根窗口管理器中
国旗TYPE_SYSTEM_OVERLAY
。现在我想添加一个按钮/可点击图像
代替可以接收触摸事件的文本。一世
尝试覆盖整个ViewGroup
的“onTouchEvent”,但确实如此
没有收到任何活动。
如何仅在某些部分接收活动 我一直在顶视图组?请建议。
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;
}
}
答案 0 :(得分:149)
这可能是一个愚蠢的解决方案。但它的确有效。如果你能改进它,请告诉我。
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);
}
现在,您将开始获取每个点击事件。因此,您需要在事件处理程序中进行纠正。
在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" />
答案 1 :(得分:62)
关注@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);
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
答案 2 :(得分:16)
从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和未来设备来说也不是一个可行的解决方案。
答案 3 :(得分:12)
我是Tooleap SDK的开发人员之一。我们还为开发人员提供了一种始终在顶部窗口和按钮上显示的方式,并且我们处理了类似的情况。
这里的答案没有解决的一个问题是Android“安全按钮”。
安全按钮具有filterTouchesWhenObscured
属性,这意味着如果放置在窗口下,即使该窗口没有接收到任何触摸,它们也无法与之交互。引用Android文档:
指定在遮挡视图窗口时是否过滤触摸 通过另一个可见窗口。设置为true时,视图将不会收到 每当吐司,对话或其他窗口出现在上方时触摸 查看窗口。请参阅{@link android.view.View}安全性 文档了解更多详情。
当您尝试安装第三方apks时,此类按钮的一个示例是安装按钮。如果向视图布局添加以下行,则任何应用都可以显示此按钮:
android:filterTouchesWhenObscured="true"
如果在“安全按钮”上显示始终在顶部的窗口,则覆盖覆盖的所有安全按钮部件将无法处理任何触摸,即使该覆盖不可点击也是如此。因此,如果您计划显示这样的窗口,您应该为用户提供移动或关闭它的方法。 如果您的叠加层的一部分是透明的,请考虑到您的用户可能会感到困惑,为什么基础应用中的某个按钮突然无法为他工作。
答案 4 :(得分:11)
始终在顶部图像按钮上工作
首先抱歉我的英文
我编辑你的代码并制作听取他的触摸事件的工作图像按钮 不要对他的背景元素进行触控。
它还为触摸侦听器提供了其他元素
按钮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;
}
}
答案 5 :(得分:6)
实际上,您可以尝试使用WindowManager.LayoutParams.TYPE_SYSTEM_ERROR而不是TYPE_SYSTEM_OVERLAY。它听起来像是一个黑客,但它可以让你在所有内容上显示视图并仍然可以获得触摸事件。
答案 6 :(得分: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);
}
}
<强>更新强>
示例here
要创建叠加视图,在设置LayoutParams时请勿将类型设置为TYPE_SYSTEM_OVERLAY。
Instead set it to TYPE_PHONE.
Use the following flags:
FLAG_NOT_TOUCH_MODAL
FLAG_WATCH_OUTSIDE_TOUCH
答案 7 :(得分:3)
TYPE_SYSTEM_OVERLAY自API级别26开始不推荐使用此常数。 请改用TYPE_APPLICATION_OVERLAY。
答案 8 :(得分:2)
这是一些简单的解决方案, 您只需要像在列表适配器上那样扩充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;
}
}
答案 9 :(得分:2)
它在此链接上使用权限“android.permission.SYSTEM_ALERT_WINDOW”完整教程:http://androidsrc.net/facebook-chat-like-floating-chat-heads/
答案 10 :(得分:1)
如果有人还在阅读这个帖子并且无法使其正常工作,我很遗憾地告诉你这种拦截动作事件被认为是bug并修复android&gt; = 4.2。
您拦截的动作事件虽然有ACTION_OUTSIDE的动作,但在getX中返回0 和getY。这意味着您无法在屏幕上看到所有运动位置,也无法执行任何操作。我知道医生说它会得到x和y,但事实是它不会。这似乎是为了阻止密钥记录器。
如果有人有解决方法,请留下您的评论。
REF: Why does ACTION_OUTSIDE return 0 everytime on KitKat 4.4.2?
答案 11 :(得分:1)
这是一个古老的问题,但最近Android支持Bubbles。气泡即将推出,但目前开发人员可以开始使用它们。气泡是为了替代SYSTEM_ALERT_WINDOW
而设计的。类似的应用程序(Facebook Messenger和MusiXMatch使用相同的概念)。
气泡是通过Notification API创建的,您可以正常发送通知。如果您希望它冒泡,则需要附加一些额外的数据。有关Bubbles的更多信息,请访问官方Android Developer Guide on Bubbles。
答案 12 :(得分:1)
找到了一个可以做到这一点的库: https://github.com/recruit-lifestyle/FloatingView
根文件夹中有一个示例项目。我运行了它,并按要求运行。背景是可点击的-即使它是另一个应用。
答案 13 :(得分:1)
通过使用服务,您可以实现这一目标:
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;
}
}
}
答案 14 :(得分:1)
试试我的代码,至少它会给你一个字符串作为叠加,你可以很好地用一个按钮或图像替换它。你不会相信这是我的第一个Android应用程序大声笑。无论如何,如果你对Android应用程序比我更有经验,请尝试
答案 15 :(得分:0)
我找到了解决这个问题的方法。您可以在以下问题中查看
答案 16 :(得分:0)
TYPE_SYSTEM_OVERLAY
已弃用,并会出现权限被拒绝错误。
改用 WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
。
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
可能正是您要找的。p>
窗口标志:即使这个窗口是可聚焦的(它的 FLAG_NOT_FOCUSABLE
没有设置),也允许窗口外的任何指针事件被发送到它后面的窗口。否则它将消耗所有指针事件本身,无论它们是否在窗口内。
实现 View.OnClickListener
或 View.onTouchListener
以侦听触摸事件。