varias - navegacion entre ventanas android studio
Creación de una ventana de superposición del sistema(siempre en la parte superior) (14)
A partir de Android 4.x, el equipo de Android solucionó un problema de seguridad potencial al agregar una nueva función adjustWindowParamsLw()
en la que agregará FLAG_NOT_FOCUSABLE
, FLAG_NOT_TOUCHABLE
y eliminará las ventanas TYPE_SYSTEM_OVERLAY
para TYPE_SYSTEM_OVERLAY
ventanas TYPE_SYSTEM_OVERLAY
.
Es decir, una ventana TYPE_SYSTEM_OVERLAY
no recibirá ningún evento táctil en la plataforma ICS y, por supuesto, usar TYPE_SYSTEM_OVERLAY
no es una solución viable para ICS y dispositivos futuros.
Estoy tratando de crear un botón siempre op-top / imagen pulsable que permanece en la parte superior de todas las ventanas todo el tiempo.
La prueba de concepto es
- aquí - Smart Taskbar (en AppBrain)
- y aquí SW1 de estilo de la barra lateral V1.4.0 - Salvador de botones (en xda-developers)
He tenido éxito y tengo un servicio en ejecución ahora. El servicio muestra algo de texto en la esquina superior izquierda de la pantalla todo el tiempo, mientras que el usuario puede interactuar libremente con el resto de aplicaciones de manera normal.
Lo que estoy haciendo es la subclase ViewGroup
y agregarlo al administrador de la ventana raíz con el indicador TYPE_SYSTEM_OVERLAY
. Ahora quiero agregar un botón / imagen pulsable en lugar de este texto que puede recibir eventos táctiles en sí mismo. He intentado anular "onTouchEvent" para todo el ViewGroup
pero no recibe ningún evento.
¿Cómo puedo recibir eventos solo en ciertas partes de mi grupo siempre visible? Por favor sugerir
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;
}
}
Al usar el servicio puedes lograr esto:
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;
}
}
}
Aquí hay una solución simple. Todo lo que necesita es inflar el diseño XML tal como lo hace en los adaptadores de lista, simplemente haga el diseño XML para inflarlo. Aquí está el código que todo lo que necesitas.
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;
}
}
Bueno, prueba mi código, al menos te da una cadena como superposición, puedes reemplazarlo con un botón o una imagen. No vas a creer que esta es mi primera aplicación para Android LOL. De todos modos, si tiene más experiencia con aplicaciones de Android que yo, por favor intente
- cambiando los parámetros 2 y 3 en "nuevo WindowManager.LayoutParams"
- probar un enfoque de evento diferente
En realidad, puedes probar WindowManager.LayoutParams.TYPE_SYSTEM_ERROR en lugar de TYPE_SYSTEM_OVERLAY. Puede sonar como un hack, pero te permite mostrar la vista sobre todo y obtener eventos táctiles.
Encontró una biblioteca que hace precisamente eso: https://github.com/recruit-lifestyle/FloatingView
Hay un proyecto de muestra en la carpeta raíz. Lo ejecuté y funciona como se requiere. Se puede hacer clic en el fondo, incluso si se trata de otra aplicación.
Esto podría ser una solución estúpida. Pero funciona. Si puedes mejorarlo, por favor házmelo saber.
Sobre la creación de su servicio: He utilizado el indicador WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
. Este es el único cambio en el servicio.
@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);
}
Ahora, comenzarás a recibir cada evento de clic. Por lo tanto, necesita rectificar en su controlador de eventos.
En tu evento táctil de 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;
}
También es posible que necesite agregar este permiso a su manifiesto:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
La respuesta de @Sarwar Erfan ya no funciona, ya que Android no permite agregar vistas con WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY a la ventana para que se pueda tocar, ni siquiera con WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH.
He encontrado solución a este problema. Puedes comprobarlo en la siguiente pregunta.
Prueba esto. Funciona bien en ICS. Si desea detener el servicio, simplemente haga clic en la notificación generada en la barra de estado.
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);
}
}
ACTUALIZAR
Muestra here
Para crear una vista de superposición, al configurar los LayoutParams NO configure el tipo en TYPE_SYSTEM_OVERLAY.
Instead set it to TYPE_PHONE.
Use the following flags:
FLAG_NOT_TOUCH_MODAL
FLAG_WATCH_OUTSIDE_TOUCH
Si alguien sigue leyendo este hilo y no puede hacer que esto funcione, siento mucho decirte que esta manera de interceptar eventos de movimiento se considera un error y se corrige en Android> = 4.2.
El evento de movimiento que interceptaste, aunque tiene acción como ACTION_OUTSIDE, devuelve 0 en getX y getY. Esto significa que no puede ver toda la posición de movimiento en la pantalla, ni puede hacer nada. Sé que el doc dijo que obtendría x y y, pero la verdad es que NO lo hará. Parece que esto es para bloquear key logger.
Si alguien tiene una solución, por favor deje su comentario.
ref: ¿Por qué ACTION_OUTSIDE devuelve 0 cada vez en KitKat 4.4.2?
Siguiendo la respuesta de @Sam Lu, en efecto, Android 4.x impide que ciertos tipos escuchen eventos táctiles externos, pero algunos tipos, como TYPE_SYSTEM_ALERT
, todavía hacen el trabajo.
Ejemplo
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);
Permisos
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
Soy uno de los desarrolladores del SDK de Tooleap . También proporcionamos una forma para que los desarrolladores se muestren siempre en las ventanas y botones superiores, y hemos lidiado con una situación similar.
Un problema que las respuestas aquí no han abordado es el de los "Botones protegidos" de Android.
Los botones filterTouchesWhenObscured
tienen la propiedad filterTouchesWhenObscured
, que significa que no pueden interactuar con ellos, si se colocan debajo de una ventana, incluso si esa ventana no recibe ningún toque. Citando la documentación de Android:
Especifica si se deben filtrar los toques cuando la ventana de la vista está oculta por otra ventana visible. Cuando se establece en verdadero, la vista no recibirá toques cada vez que aparezca un brindis, un cuadro de diálogo u otra ventana encima de la ventana de la vista. Consulte la documentación de seguridad {@link android.view.View} para obtener más detalles.
Un ejemplo de tal botón es el botón de instalación cuando intenta instalar apks de terceros. Cualquier aplicación puede mostrar dicho botón si agrega al diseño de vista la siguiente línea:
android:filterTouchesWhenObscured="true"
Si muestra una ventana siempre visible en la parte superior sobre un "Botón asegurado", todas las partes de los botones asegurados que están cubiertas por una superposición no manejarán ningún toque, incluso si no se puede hacer clic en esa superposición. Por lo tanto, si planea mostrar una ventana de este tipo, debe proporcionar una forma para que el usuario la mueva o la descarte. Y si una parte de su superposición es transparente, tenga en cuenta que su usuario podría estar confundido porque un cierto botón en la aplicación subyacente no está funcionando para él de repente.
TRABAJANDO SIEMPRE EN EL BOTÓN DE IMAGEN SUPERIOR
en primer lugar lo siento por mi ingles
Edito sus códigos y hago que el botón de imagen de trabajo que escucha su evento táctil no le dé control táctil a sus elementos de fondo.
También da a los oyentes táctiles de otros elementos.
Las alineaciones de los botones están abajo y a la izquierda.
puede cambiar las alineaciones pero necesita seleccionar eventos en contacto en el elemento 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;
}
}
Utiliza el permiso completo "android.permission.SYSTEM_ALERT_WINDOW" en este enlace: http://androidsrc.net/facebook-chat-like-floating-chat-heads/