Erstellen eines Systemüberlagerungsfensters (immer oben)

285

Ich versuche, eine Schaltfläche / ein anklickbares Bild zu erstellen, die immer über allen Fenstern angezeigt wird.

Der Proof of Concept ist

Ich war erfolgreich und habe jetzt einen laufenden Dienst. Der Dienst zeigt ständig Text in der oberen linken Ecke des Bildschirms an, während der Benutzer auf normale Weise frei mit anderen Apps interagieren kann.

Was ich mache, ist eine Unterklasse ViewGroupund füge sie dem Root-Fenstermanager mit Flag hinzu TYPE_SYSTEM_OVERLAY. Jetzt möchte ich anstelle dieses Textes eine Schaltfläche / ein anklickbares Bild hinzufügen, das Berührungsereignisse für sich selbst empfangen kann. Ich habe versucht, "onTouchEvent" für das Ganze zu überschreiben, ViewGroupaber es wird kein Ereignis empfangen.

Wie kann ich Ereignisse nur für bestimmte Teile meiner Gruppe "Immer im Blick" empfangen? Bitte vorschlagen.

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
quelle
2
@coreSOLO, ich hoffe du bekommst deine Antwort, meine seltsam ähnlichen Fragen haben keine Antwort bekommen. :) +1
st0le
3
@ st0le Ich verspreche, ich werde meine Nacht und Tag verbringen, bis ich die Lösung finde und sie mit Ihnen teile :)
coreSOLO
1
@ st0le Ok, vielleicht können Sie und ich es gemeinsam ausgraben, mir einen Link zu Ihrer Frage geben und mich wissen lassen, wie viel Fortschritt Sie machen konnten ...
coreSOLO
Es ist nicht viel: - \ stackoverflow.com/questions/3732935/…
st0le
3
Es ist überraschend, dass Sie onDraw()angerufen werden. Sollte dispatchDraw()stattdessen nicht überschrieben werden? Siehe groups.google.com/forum/?fromgroups#!topic/android-developers/…
faizal

Antworten:

158

Dies könnte eine dumme Lösung sein. Aber es funktioniert. Wenn Sie es verbessern können, lassen Sie es mich bitte wissen.

OnCreate Ihres Dienstes: Ich habe WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCHflag verwendet. Dies ist die einzige Änderung im 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);
    }

Jetzt erhalten Sie jedes einzelne Klickereignis. Sie müssen also in Ihrem Event-Handler korrigieren.

In Ihrem ViewGroup-Touch-Ereignis

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

Möglicherweise müssen Sie diese Berechtigung auch zu Ihrem Manifest hinzufügen:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
Sarwar Erfan
quelle
1
@pengwang: Welche Fehler bekommst du? Dieser Code ist nicht unabhängig. Sie müssen ihn in Ihrem Projekt mit den entsprechenden Dateinamen unter dem entsprechenden Projekt platzieren.
Sarwar Erfan
4
@pengwang: Die einfachste Implementierung finden Sie hier: docs.google.com/…
Sarwar Erfan
6
Der ganze Code ist korrekt, ich finde meinen Fehler, ich vergesse den Link <Verwendungsberechtigung android: name = "android.permission.SYSTEM_ALERT_WINDOW" /> Sarwar Erfan Ich denke, Sie bearbeiten Ihre Antwort, es kann wunderbarer werden
Pengwang
1
Diese Antwort ist bis auf eine Sache sehr hilfreich. Wie im Code verwendet, erhält WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH laut Dokumentation "nicht die vollständige Auf- / Verschiebe- / Abwärtsgeste". Mit anderen Worten, es ist nicht möglich, Wischbewegungen oder etwas anderes als ACTION_DOWN zu erkennen. Nun, ich weiß, dass es möglich ist, Wischbewegungen zu erkennen, da Wave Launcher dieses ähnliche Konzept verwendet und sie Wischbewegungen erkennen können. Kann mir jemand erklären, wie das funktioniert?
Brian
6
@SarwarErfan - Ich habe dieses Verkorken perfekt auf 2.3 verwendet, aber das onTouch-Ereignis wird unter Android 4.0, 1. 4.1 nicht geetet. Bitte helfen Sie
Sanjay Singh
66

Nach der Antwort von @Sam Lu verhindert Android 4.x zwar, dass bestimmte Typen externe Berührungsereignisse abhören, aber einige Typen, z. B. TYPE_SYSTEM_ALERT, erledigen den Job immer noch.

Beispiel

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

Berechtigungen

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
amirlazarovich
quelle
5
Es macht meinen Bildschirm hängen .. Jede Idee
Viswanath Lekshmanan
Guter Eintrag. Hoffentlich wird dies auch in zukünftigen Versionen nicht deaktiviert.
Cookster
Animationen funktionieren nicht ... :( Und es ist nicht möglich, Klicks auf eine Ansicht in 4.2.X zu fangen. Irgendwelche Vorschläge?
Kamran Ahmed
Es funktioniert, aber wenn ich diese Verknüpfung verwende (z. B. Alert auf Systemebene), werden andere Dialogfelder nicht angezeigt. Warum ?
Chimu
@Arju was meinst du? Versuchst du es zu entfernen?
Amirlazarovich
18

Beginnend mit Android 4.x, Fest Android - Team ein potenzielles Sicherheitsproblem durch eine neue Funktion hinzugefügt , adjustWindowParamsLw()in dem es hinzuzufügen FLAG_NOT_FOCUSABLE, FLAG_NOT_TOUCHABLEund entfernen FLAG_WATCH_OUTSIDE_TOUCHFlaggen für TYPE_SYSTEM_OVERLAYFenster.

Das heißt, ein TYPE_SYSTEM_OVERLAYFenster empfängt kein Berührungsereignis auf der ICS-Plattform und die Verwendung TYPE_SYSTEM_OVERLAYist natürlich keine praktikable Lösung für ICS und zukünftige Geräte.

Sam Lu
quelle
4
Also, was sind die Alternativen?
Radhoo
13

Ich bin einer der Entwickler des Tooleap SDK . Wir bieten Entwicklern auch die Möglichkeit, immer oben Fenster und Schaltflächen anzuzeigen, und wir haben uns mit einer ähnlichen Situation befasst.

Ein Problem, das die Antworten hier nicht angesprochen haben, ist das der Android "Secured Buttons".

Gesicherte Schaltflächen haben die filterTouchesWhenObscuredEigenschaft, dass sie nicht interagiert werden können, wenn sie unter einem Fenster platziert werden, selbst wenn dieses Fenster keine Berührungen erhält. Zitieren der Android-Dokumentation:

Gibt an, ob Berührungen gefiltert werden sollen, wenn das Fenster der Ansicht durch ein anderes sichtbares Fenster verdeckt wird. Bei true wird die Ansicht nicht berührt, wenn ein Toast, ein Dialogfeld oder ein anderes Fenster über dem Fenster der Ansicht angezeigt wird. Weitere Informationen finden Sie in der Sicherheitsdokumentation {@link android.view.View}.

Ein Beispiel für eine solche Schaltfläche ist die Schaltfläche " Installieren" , wenn Sie versuchen, Apks von Drittanbietern zu installieren. Jede App kann eine solche Schaltfläche anzeigen, wenn dem Ansichtslayout die folgende Zeile hinzugefügt wird:

android:filterTouchesWhenObscured="true"

Wenn Sie über einer "gesicherten Schaltfläche" ein Fenster anzeigen, das immer oben angezeigt wird, können alle gesicherten Schaltflächenteile, die von einer Überlagerung abgedeckt werden, keine Berührungen verarbeiten, auch wenn diese Überlagerung nicht anklickbar ist. Wenn Sie also ein solches Fenster anzeigen möchten, sollten Sie dem Benutzer die Möglichkeit geben, es zu verschieben oder zu schließen. Und wenn ein Teil Ihres Overlays transparent ist, berücksichtigen Sie, dass Ihr Benutzer möglicherweise verwirrt ist, warum eine bestimmte Schaltfläche in der zugrunde liegenden App nicht plötzlich für ihn funktioniert.

Danny
quelle
Können Sie bitte meine Frage beantworten: stackoverflow.com/questions/28964771/…
Nauman Afzaal
Es gibt einige Aktivitäten in Android 10, die die Overlay-Ansicht nicht über ihnen anzeigen kann. Können
Mateen Chaudhry
12

IMMER AUF DER OBEREN BILD-TASTE ARBEITEN

Zunächst einmal entschuldigen Sie mein Englisch

Ich bearbeite Ihre Codes und lasse die Schaltfläche für Arbeitsbilder, die sein Berührungsereignis abhört, seinen Hintergrundelementen keine Berührungssteuerung geben.

Es gibt auch Touch-Listener für andere Elemente

Schaltflächen sind unten und links

Sie können Alingments ändern, aber Sie müssen Koordinaten im Berührungsereignis im if-Element ändern

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
quelle
Dies funktioniert, aber wie kann ich sicherstellen, dass auf mein Bild geklickt wurde und nicht um das Bild herum?
Liam W
@LiamW, die Koordinaten der Berührung werden in diesem Beispiel mit kangaroo.getWidth () verglichen. Sie können in Ihrem eigenen Code ableiten, womit Sie es vergleichen möchten, je nachdem, wo Sie Ihr Bild zeichnen.
Jewgeni Simkin
Sehr schön! Funktioniert perfekt.
Jewgeni Simkin
Was ist die minimale Android-Version, auf der dies funktioniert? Ich arbeite daran, dies aufzuschreiben, aber ich bin neu bei Android, also habe ich Anfängerzeit.
Noitidart
6

Sie können WindowManager.LayoutParams.TYPE_SYSTEM_ERROR anstelle von TYPE_SYSTEM_OVERLAY ausprobieren. Es mag wie ein Hack klingen, aber Sie können die Ansicht über alles anzeigen und trotzdem Berührungsereignisse erhalten.

Ov3r1oad
quelle
1
Ich habe alles versucht, damit es funktioniert, aber kein Erfolg ... aber das Hinzufügen von TYPE_SYSTEM_ERROR hat den Job erledigt ... Eigentlich hat TYPE_SYSTEM_OVERLAY die Berührung blockiert ... + 1.
Robi Kumar Tomar
6

TYPE_SYSTEM_OVERLAY Diese Konstante ist seit API-Ebene 26 veraltet. Verwenden Sie stattdessen TYPE_APPLICATION_OVERLAY. oder ** für Benutzer unter und über 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
quelle
5

Versuche dies. Funktioniert gut in ICS. Wenn Sie den Dienst beenden möchten, klicken Sie einfach auf die in der Statusleiste generierte Benachrichtigung.

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

AKTUALISIEREN

Probieren Sie hier

Um eine Überlagerungsansicht zu erstellen, setzen Sie beim Einrichten der LayoutParams den Typ NICHT auf TYPE_SYSTEM_OVERLAY.

Instead set it to TYPE_PHONE.

Use the following flags:

FLAG_NOT_TOUCH_MODAL

FLAG_WATCH_OUTSIDE_TOUCH
Viswanath Lekshmanan
quelle
Dies funktioniert bei mir nicht. Mein Bildschirm interagiert nicht mit dem Hintergrund. Jede andere Methode, die Sie kennen ...
Vinuthan
@ Vinuthan Was ist Ihre Ausgabe, reagiert es
Viswanath Lekshmanan
Nein, es antwortet nicht. Ich habe meine Aktivität oben auf die Hälfte des Bildschirms aufgefüllt, damit ich mit dem Hintergrund interagieren kann, aber ich kann nicht mit dem Hintergrund interagieren.
Vinuthan
FLAG_WATCH_OUTSIDE_TOUCH verhindert, dass Sie mit dem Hintergrund interagieren
Richard Le Mesurier
1
TYPE_PHONE hat bei mir funktioniert - jetzt werden Berührungen geliefert - zumindest kann ich auf einen Knopf tippen;) danke!
Vir uns
3

Hier ist eine einfache Lösung: Sie müssen lediglich das XML-Layout wie bei Listenadaptern aufblasen. Erstellen Sie einfach ein XML-Layout, um es aufzublasen. Hier ist Code, den Sie alles brauchen.

 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
quelle
3

Es wurde eine Bibliothek gefunden, die genau das tut: https://github.com/recruit-lifestyle/FloatingView

Im Stammordner befindet sich ein Beispielprojekt. Ich habe es ausgeführt und es funktioniert wie erforderlich. Der Hintergrund ist anklickbar - auch wenn es sich um eine andere App handelt.

Geben Sie hier die Bildbeschreibung ein

AlikElzin-Kilaka
quelle
Vielen Dank für die Empfehlung, ich habe nur versucht, dieses großartige Projekt zu finden.
bis
1

Probieren Sie meinen Code aus, zumindest erhalten Sie eine Zeichenfolge als Überlagerung. Sie können ihn sehr gut durch eine Schaltfläche oder ein Bild ersetzen. Sie werden nicht glauben, dass dies meine erste Android-App LOL ist. Wenn Sie mehr Erfahrung mit Android-Apps haben als ich, versuchen Sie es bitte

  • Ändern der Parameter 2 und 3 in "new WindowManager.LayoutParams"
  • Probieren Sie einen anderen Event-Ansatz aus
coreSOLO
quelle
Ich fand schließlich keine Zeit, dies zu verfolgen, daher bin ich immer noch knapp bei den Details ...
st0le
1

Wenn jemand diesen Thread noch liest und nicht in der Lage ist, dies zum Laufen zu bringen, tut es mir sehr leid, Ihnen zu sagen, dass diese Art, Bewegungsereignisse abzufangen, in Android> = 4.2 als Fehler und Fehlerbehebung angesehen wird.

Das Bewegungsereignis, das Sie abgefangen haben, hat zwar die Aktion ACTION_OUTSIDE, gibt jedoch 0 in getX und getY zurück. Dies bedeutet, dass Sie nicht alle Bewegungspositionen auf dem Bildschirm sehen und auch nichts tun können. Ich weiß, dass der Arzt sagte, dass es x und y bekommen wird, aber die Wahrheit ist, dass es NICHT wird. Es scheint, dass dies den Key Logger blockieren soll.

Wenn jemand eine Problemumgehung hat, hinterlassen Sie bitte Ihren Kommentar.

ref: Warum gibt ACTION_OUTSIDE in KitKat 4.4.2 jedes Mal 0 zurück?

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

Nick Jian
quelle
1

Mit dem Service können Sie Folgendes erreichen:

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
quelle
1

Die Antwort von @Sarwar Erfan funktioniert nicht mehr, da Android das Hinzufügen einer Ansicht mit WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY zum Fenster nicht mehr zum Berühren zulässt, auch nicht mit WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH.

Ich habe eine Lösung für dieses Problem gefunden. Sie können es in der folgenden Frage überprüfen

Beim Hinzufügen einer Ansicht zum Fenster mit WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY wird kein Berührungsereignis angezeigt

patilmandar2007
quelle
Ihre verwiesene Lösung funktioniert auch nicht in Marshmallow
Ahmad Shahwaiz
@AhmadShahwaiz Bitte überprüfen Sie den Link erneut Ich habe einen Kommentar für das Problem hinzugefügt, das Sie dort erwähnt haben
patilmandar2007
Ja, jetzt funktioniert es. Die Rückrufantwort wird jedoch nicht nach dem Aufruf von startActivityForResult (intent, OVERLAY_REQ_CODE) abgerufen. in der Methode protected void onActivityResult (int requestCode, int resultCode, Intent data).
Ahmad Shahwaiz
@AhmadShahwaiz Änderungen, die im Link erwähnt werden, müssen mit Aktivität aufgerufen werden, bei der wir in der onActivityResult-Methode einen Rückruf erhalten. Wenn Sie die obige Berechtigung im Dienst überprüfen, müssen Sie möglicherweise eine transparente Dummy-Aktivität hinzufügen, die vom Dienst gestartet werden soll. Diese kann den Benutzer um Erlaubnis bitten. In der Dummy-Aktivität onActivityResult erhalten Sie das Ergebnis vom Benutzer. Nachdem Sie Ihren Dienst benachrichtigen müssen, um dem Fenster eine Ansicht hinzuzufügen, oder nicht, abhängig von der Benutzerantwort
patilmandar2007
1

Dies ist eine alte Frage, aber seit kurzem unterstützt Android Bubbles. Bubbles werden bald gestartet, aber derzeit können Entwickler sie verwenden. Sie sind als Alternative zur Verwendung konzipiert SYSTEM_ALERT_WINDOW. Apps wie (Facebook Messenger und MusiXMatch verwenden dasselbe Konzept).

Bläschen

Blasen werden über die Benachrichtigungs-API erstellt. Sie senden Ihre Benachrichtigung wie gewohnt. Wenn Sie möchten, dass es sprudelt, müssen Sie einige zusätzliche Daten anhängen. Weitere Informationen zu Bubbles finden Sie im offiziellen Android Developer Guide on Bubbles .

Xenolion
quelle