Создание системного оверлея (всегда сверху) - PullRequest
269 голосов
/ 19 декабря 2010

Я пытаюсь создать кнопку с открытым верхом / clickable-image который остается на вершине всех окон все время.

Доказательство Концепция

Я добился успеха и теперь у меня запущен сервис. Сервис отображает текст в верхнем левом углу экрана все время, пока Пользователь может свободно взаимодействовать с остальными приложениями в обычном порядке.

Что я делать это подкласс ViewGroup и добавить его в корневой оконный менеджер с помощью флаг TYPE_SYSTEM_OVERLAY. Теперь я хочу добавить кнопку / clickable-изображение вместо этого текста, который может получать сенсорные события на себя. я попытался переопределить "onTouchEvent" для всего ViewGroup, но это делает не получить никакого события.

Как я могу получать события только в определенных частях моей группы всегда на вершине зрения? Пожалуйста, предложите.

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

Ответы [ 15 ]

152 голосов
/ 19 декабря 2010

Это может быть глупым решением. Но это работает. Если вы можете улучшить его, пожалуйста, дайте мне знать.

При создании вашего сервиса: я использовал флаг 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" />
63 голосов
/ 20 мая 2012

После ответа @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"/>
16 голосов
/ 27 февраля 2012

Начиная с 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 и будущих устройств.

12 голосов
/ 11 мая 2014

Я один из разработчиков Tooleap SDK . Мы также предоставляем разработчикам возможность всегда отображать верхние окна и кнопки, и мы столкнулись с аналогичной ситуацией.

Одной из проблем, на которые не были даны ответы, является проблема «Защищенных кнопок» Android.

Защищенные кнопки имеют свойство filterTouchesWhenObscured, что означает, что с ними нельзя взаимодействовать, если они находятся под окном, даже если это окно не получает никаких прикосновений. Цитирование документации Android:

Указывает, следует ли фильтровать касания, когда окно представления скрыто другим видимым окном. Когда установлено значение true, представление не будет получать прикасается к тосту, диалогу или другому окну над окно просмотра. Обратитесь к {@link android.view.View} безопасности документация для более подробной информации.

Примером такой кнопки является кнопка install , когда вы пытаетесь установить сторонние apks. Любое приложение может отобразить такую ​​кнопку, если добавить к макету вида следующую строку:

android:filterTouchesWhenObscured="true"

Если вы отображаете всегда поверх окна поверх «Защищенной кнопки», то все защищенные части кнопок, на которые накладывается наложение, не будут обрабатывать никаких касаний, даже если это наложение не активировано. Поэтому, если вы планируете отображать такое окно, вы должны предоставить пользователю возможность его переместить или закрыть. И если часть вашего оверлея прозрачна, учтите, что ваш пользователь может быть сбит с толку, почему определенная кнопка в базовом приложении неожиданно для него не работает.

11 голосов
/ 10 мая 2013

РАБОТАЕТ ВСЕГДА НА КНОПКЕ ИЗ КАРТИНКА

прежде всего извините за мой английский

я редактирую ваши коды и делаю рабочую картинку кнопкой, которая слушает его сенсорное событие не давайте сенсорному управлению его фоновые элементы.

также дает слушателям возможность касаться других элементов

кнопки внизу и слева

вы можете изменять сообщения, но вам нужно изменить связь в случае касания в элементе 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;
    }
}
6 голосов
/ 11 ноября 2015

На самом деле вы можете попробовать WindowManager.LayoutParams.TYPE_SYSTEM_ERROR вместо TYPE_SYSTEM_OVERLAY.Это может звучать как хак, но оно позволяет отображать представление поверх всего и по-прежнему получать сенсорные события.

5 голосов
/ 05 апреля 2013

Попробуй это.Прекрасно работает в 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); 
    }
}

ОБНОВЛЕНИЕ

Образец здесь

Чтобы создать представление наложения при настройке LayoutParams, НЕ делайтеустановите тип в TYPE_SYSTEM_OVERLAY.

Instead set it to TYPE_PHONE.

Use the following flags:

FLAG_NOT_TOUCH_MODAL

FLAG_WATCH_OUTSIDE_TOUCH
4 голосов
/ 06 февраля 2019

TYPE_SYSTEM_OVERLAY Эта константа устарела начиная с уровня API 26. Вместо этого используйте TYPE_APPLICATION_OVERLAY. или же ** для пользователей ниже и выше Android 8

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} else {
    LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_PHONE;
}
2 голосов
/ 05 февраля 2015

Полное руководство по разрешению "android.permission.SYSTEM_ALERT_WINDOW" по этой ссылке: http://androidsrc.net/facebook-chat-like-floating-chat-heads/

2 голосов
/ 17 ноября 2013

Вот несколько простых решений. Все, что вам нужно, это накачать 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;
    }

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...