Création d'une fenêtre de superposition du système (toujours en haut)

285

J'essaie de créer un bouton / une image cliquable toujours en haut qui reste en haut de toutes les fenêtres tout le temps.

La preuve de concept est

J'ai réussi et j'ai maintenant un service en cours d'exécution. Le service affiche tout le temps du texte dans le coin supérieur gauche de l'écran tandis que l'utilisateur peut interagir librement avec le reste des applications de manière normale.

Ce que je fais, c'est une sous-classe ViewGroupet l'ajouter au gestionnaire de fenêtres racine avec un indicateur TYPE_SYSTEM_OVERLAY. Maintenant, je veux ajouter un bouton / image cliquable à la place de ce texte qui peut recevoir des événements tactiles sur lui-même. J'ai essayé de remplacer "onTouchEvent" pour l'ensemble ViewGroupmais il ne reçoit aucun événement.

Comment puis-je recevoir des événements uniquement sur certaines parties de mon groupe de vues toujours visible? Veuillez suggérer.

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;
    }
}
coreSOLO
la source
2
@coreSOLO, j'espère que vous obtenez votre réponse, mes questions étrangement similaires n'ont reçu aucune réponse. :) +1
st0le
3
@ st0le Je promets de passer la nuit et le jour jusqu'à ce que je trouve la solution et la partage avec toi :)
coreSOLO
1
@ st0le Ok peut-être que vous et moi pouvons le découvrir ensemble, donnez-moi un lien de votre question et faites-moi savoir combien de progrès vous avez pu faire ...
coreSOLO
ce n'est pas beaucoup: - \ stackoverflow.com/questions/3732935/…
st0le
3
Il est surprenant que votre onDraw()téléphone soit appelé. Ne devrait pas dispatchDraw()être remplacé à la place? Voir groups.google.com/forum/?fromgroups#!topic/android-developers/…
faizal

Réponses:

158

Cela pourrait être une solution stupide. Mais ça marche. Si vous pouvez l'améliorer, faites-le moi savoir.

OnCréez votre service: j'ai utilisé le WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCHdrapeau. C'est le seul changement de service.

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

Maintenant, vous commencerez à obtenir chaque événement de clic. Vous devez donc rectifier dans votre gestionnaire d'événements.

Dans votre événement tactile 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;
}

Vous devrez peut-être également ajouter cette autorisation à votre manifeste:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
Sarwar Erfan
la source
1
@pengwang: quelles erreurs obtenez-vous? Ce code n'est pas indépendant, vous devez les placer dans votre projet avec les noms de fichiers appropriés sous le projet approprié.
Sarwar Erfan
4
@pengwang: Trouvez l'implémentation la plus simple ici: docs.google.com/…
Sarwar Erfan
6
tout le code est correct, je trouve mon erreur, j'oublie le lien <uses-permission android: name = "android.permission.SYSTEM_ALERT_WINDOW" /> Sarwar Erfan je pense que vous modifiez votre réponse, cela peut devenir plus merveilleux
pengwang
1
Cette réponse est très utile, sauf pour une chose. Tel qu'utilisé dans le code, WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH, selon la documentation, "ne recevra pas le geste de montée / descente / descente complet". En d'autres termes, il n'est pas possible de détecter des balayages ou autre chose qu'un ACTION_DOWN. Eh bien, je sais qu'il est possible de détecter les balayages car Wave Launcher utilise ce concept similaire et ils sont capables de détecter les balayages. Quelqu'un peut-il m'expliquer comment cela fonctionne?
Brian
6
@SarwarErfan - J'ai parfaitement utilisé son bouchage sur 2.3 mais l'événement onTouch n'est pas invocké sur Android 4.0, 1nd 4.1. S'il vous plaît aider
Sanjay Singh
66

À la suite de la réponse de @Sam Lu, Indeed Android 4.x empêche certains types d'écouter des événements tactiles extérieurs, mais certains types, tels que TYPE_SYSTEM_ALERT, font toujours le travail.

Exemple

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

Autorisations

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
amirlazarovich
la source
5
Son écran a été pendu. Aucune idée
Viswanath Lekshmanan
Très bonne publication. Espérons que ce ne soit pas désactivé dans les versions futures non plus.
Cookster
Les animations ne fonctionneront pas là-dessus ... :( Et il est incapable d'attraper des clics sur une vue quelconque sur 4.2.X. Des suggestions?
Kamran Ahmed
Cela fonctionne, mais lorsque j'utilise ce raccourci (c'est-à-dire l'alerte de niveau système), les autres boîtes de dialogue ne s'affichent pas. Pourquoi ?
Chimu
@Arju que voulez-vous dire? essayez-vous de le supprimer?
amirlazarovich
18

A partir de Android 4.x, l' équipe Android fixe un problème de sécurité potentiel en ajoutant une nouvelle fonction adjustWindowParamsLw()dans laquelle il ajoutera FLAG_NOT_FOCUSABLE, FLAG_NOT_TOUCHABLEet supprimer des FLAG_WATCH_OUTSIDE_TOUCHdrapeaux pourTYPE_SYSTEM_OVERLAY les fenêtres.

Autrement dit, une TYPE_SYSTEM_OVERLAYfenêtre ne recevra aucun événement tactile sur la plate-forme ICS et, bien sûr, son utilisation TYPE_SYSTEM_OVERLAYn'est pas une solution viable pour ICS et les futurs appareils.

Sam Lu
la source
4
Alors quelles sont les alternatives?
radhoo
13

Je suis l'un des développeurs du SDK Tooleap . Nous proposons également aux développeurs un moyen d'afficher toujours les fenêtres et les boutons supérieurs, et nous avons fait face à une situation similaire.

Un problème que les réponses ici n'ont pas résolu est celui des «boutons sécurisés» Android.

Les boutons sécurisés ont la filterTouchesWhenObscuredpropriété qui signifie qu'ils ne peuvent pas interagir avec, s'ils sont placés sous une fenêtre, même si cette fenêtre ne reçoit aucune touche. Citant la documentation Android:

Spécifie s'il faut filtrer les touches lorsque la fenêtre de la vue est masquée par une autre fenêtre visible. Lorsqu'elle est définie sur true, la vue ne reçoit aucune touche lorsqu'un toast, une boîte de dialogue ou une autre fenêtre apparaît au-dessus de la fenêtre de la vue. Reportez-vous à la documentation de sécurité {@link android.view.View} pour plus de détails.

Un exemple d'un tel bouton est le bouton d' installation lorsque vous essayez d'installer des apks tiers. Toute application peut afficher un tel bouton si elle ajoute à la disposition de la vue la ligne suivante:

android:filterTouchesWhenObscured="true"

Si vous affichez une fenêtre toujours au-dessus d'un "bouton sécurisé", toutes les parties de bouton sécurisées couvertes par une superposition ne gèreront aucune touche, même si cette superposition n'est pas cliquable. Donc, si vous prévoyez d'afficher une telle fenêtre, vous devez fournir à l'utilisateur un moyen de la déplacer ou de la fermer. Et si une partie de votre superposition est transparente, sachez que votre utilisateur peut être confus pourquoi un certain bouton de l'application sous-jacente ne fonctionne-t-il pas soudainement pour lui.

Danny
la source
Pouvez-vous s'il vous plaît répondre à ma question: stackoverflow.com/questions/28964771/…
Nauman Afzaal
il y a une activité dans Android 10 que la vue de superposition ne peut pas montrer au-dessus d'eux, pouvez-vous s'il vous plaît indiquer la raison ou voir ma question ici, ce sera très utile stackoverflow.com/questions/59598264/…
Mateen Chaudhry
12

TRAVAILLER TOUJOURS SUR LE BOUTON D'IMAGE SUPÉRIEUR

tout d'abord désolé pour mon anglais

je modifie vos codes et fais en sorte que le bouton d'image de travail qui écoute son événement tactile ne donne pas de contrôle tactile à ses éléments d'arrière-plan.

il donne également aux auditeurs tactiles d'autres éléments

les modifications des boutons sont en bas et à gauche

vous pouvez modifier les modifications, mais vous devez modifier les cordinats dans l'événement tactile dans l'élément 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;
    }
}
Onur Öztürk
la source
Cela fonctionne, mais comment puis-je m'assurer que mon image a été cliquée et non quelque chose autour de l'image?
Liam W
@LiamW, les coordonnées du toucher sont comparées à kangaroo.getWidth () dans cet exemple. Vous pouvez déduire ce à quoi le comparer dans votre propre code en fonction de l'endroit où vous dessinez votre image.
Yevgeny Simkin
Très agréable! Fonctionne parfaitement.
Yevgeny Simkin
Quelle est la version minimale d'Android sur laquelle cela fonctionne? Je travaille sur l'écriture de cela, mais je suis nouveau sur Android, donc j'ai du temps pour les débutants.
Noitidart
6

En fait, vous pouvez essayer WindowManager.LayoutParams.TYPE_SYSTEM_ERROR au lieu de TYPE_SYSTEM_OVERLAY. Cela peut ressembler à un hack, mais il vous permet d'afficher la vue par-dessus tout et d'obtenir toujours des événements tactiles.

Ov3r1oad
la source
1
J'ai tout essayé pour le faire fonctionner, mais sans succès .... mais l'ajout de TYPE_SYSTEM_ERROR a fait le travail .... En fait, TYPE_SYSTEM_OVERLAY bloquait le toucher ..... + 1.
Robi Kumar Tomar
6

TYPE_SYSTEM_OVERLAY Cette constante est déconseillée depuis le niveau d'API 26. Utilisez plutôt TYPE_APPLICATION_OVERLAY. ou ** pour les utilisateurs inférieurs et supérieurs à 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;
}
arwTheDev
la source
5

Essaye ça. Fonctionne bien dans ICS. Si vous souhaitez arrêter le service, cliquez simplement sur la notification générée dans la barre d'état.

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

METTRE À JOUR

Échantillon ici

Pour créer une vue de superposition, lors de la configuration des LayoutParams, ne définissez PAS le type sur TYPE_SYSTEM_OVERLAY.

Instead set it to TYPE_PHONE.

Use the following flags:

FLAG_NOT_TOUCH_MODAL

FLAG_WATCH_OUTSIDE_TOUCH
Viswanath Lekshmanan
la source
Cela ne fonctionne pas pour moi, mon écran n'interagit pas avec l'arrière-plan. Toute autre méthode que vous connaissez ...
Vinuthan
@Vinuthan Quelle est votre production, est-ce qu'elle répond
Viswanath Lekshmanan
Non, il ne répond pas. J'ai rembourré mon activité au-dessus de la moitié de l'écran pour pouvoir interagir avec l'arrière-plan, mais je ne peux pas interagir avec l'arrière-plan.
Vinuthan
FLAG_WATCH_OUTSIDE_TOUCH vous empêche d'interagir avec l'arrière
Richard Le Mesurier
1
TYPE_PHONE a fonctionné pour moi - maintenant les touches sont livrées - au moins je peux appuyer sur un bouton;) merci!
vir us
3

Voici une solution simple.Tout ce dont vous avez besoin est de gonfler la disposition XML comme vous le faites sur les adaptateurs de liste, faites simplement la disposition XML pour la gonfler. Voici le code dont vous avez besoin.

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

}
user1668168
la source
3

Trouvé une bibliothèque qui fait exactement cela: https://github.com/recruit-lifestyle/FloatingView

Il y a un exemple de projet dans le dossier racine. Je l'ai exécuté et cela fonctionne comme requis. L'arrière-plan est cliquable - même s'il s'agit d'une autre application.

entrez la description de l'image ici

AlikElzin-kilaka
la source
Merci pour la recommandation, j'essayais juste de trouver ce grand projet.
jour le
1

Eh bien essayez mon code, au moins il vous donne une chaîne en superposition, vous pouvez très bien le remplacer par un bouton ou une image. Vous ne croirez pas que c'est ma toute première application Android LOL. Quoi qu'il en soit, si vous êtes plus expérimenté avec les applications Android que moi, veuillez essayer

  • modification des paramètres 2 et 3 dans "new WindowManager.LayoutParams"
  • essayez une approche événementielle différente
coreSOLO
la source
je n'ai finalement pas trouvé le temps de poursuivre, donc je suis encore rare sur ses détails ...
st0le
1

Si quelqu'un lit encore ce fil et n'est pas en mesure de le faire fonctionner, je suis vraiment désolé de vous dire que cette façon d'intercepter l'événement de mouvement est considérée comme un bug et une correction dans Android> = 4.2.

L'événement de mouvement que vous avez intercepté, bien que son action soit ACTION_OUTSIDE, renvoie 0 dans getX et getY. Cela signifie que vous ne pouvez pas voir toute la position du mouvement à l'écran, ni rien faire. Je sais que le doc a dit qu'il obtiendrait x et y, mais la vérité est qu'il ne le fera PAS. Il semble que ce soit pour bloquer l'enregistreur de frappe.

Si quelqu'un a une solution de contournement, veuillez laisser votre commentaire.

ref: Pourquoi ACTION_OUTSIDE retourne 0 à chaque fois sur KitKat 4.4.2?

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

Nick Jian
la source
1

en utilisant le service, vous pouvez y parvenir:

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;
        }
    }
}
Akshay Paliwal
la source
1

La réponse de @Sarwar Erfan ne fonctionne plus car Android ne permet pas d'ajouter la vue avec WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY à la fenêtre pour qu'elle ne soit plus accessible, même avec WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH.

J'ai trouvé une solution à ce problème. Vous pouvez le vérifier dans la question suivante

Lorsque vous ajoutez une vue à une fenêtre avec WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, il ne reçoit pas d'événement tactile

patilmandar2007
la source
Votre solution référée ne fonctionne pas non plus à Marshmallow
Ahmad Shahwaiz
@AhmadShahwaiz Veuillez vérifier à nouveau le lien J'ai ajouté un commentaire pour le problème que vous avez mentionné là-bas
patilmandar2007
Oui, maintenant ça marche. Mais ne pas obtenir la réponse de rappel après avoir appelé startActivityForResult (intention, OVERLAY_REQ_CODE); dans la méthode onActivityResult (int requestCode, int resultCode, Intent data) de void protégé.
Ahmad Shahwaiz
@AhmadShahwaiz Les modifications mentionnées dans le lien doivent être appelées en utilisant une activité où nous obtenons un rappel dans la méthode onActivityResult. Si vous vérifiez l'autorisation ci-dessus dans Service, vous devrez peut-être ajouter une activité transparente factice pour démarrer à partir du service, ce qui peut demander l'autorisation à l'utilisateur, puis dans l'activité factice onActivityResult, vous obtiendrez le résultat de l'utilisateur. Après, vous devrez informer votre service pour ajouter ou non la vue à la fenêtre en fonction de la réponse de l'utilisateur
patilmandar2007
1

Il s'agit d'une ancienne question, mais récemment Android prend en charge les bulles. Les bulles vont bientôt être lancées mais actuellement les développeurs peuvent commencer à les utiliser, elles sont conçues pour être une alternative à l'utilisation SYSTEM_ALERT_WINDOW. Des applications comme (Facebook Messenger et MusiXMatch utilisent le même concept).

Bulles

Les bulles sont créées via l'API de notification, vous envoyez votre notification comme d'habitude. Si vous voulez qu'il bouillonne, vous devez y attacher des données supplémentaires. Pour plus d'informations sur Bubbles, vous pouvez consulter le Guide officiel du développeur Android sur Bubbles .

Xenolion
la source