android - sobrepor - Criando uma janela de sobreposição do sistema(sempre na parte superior)



janela acústica de sobreposição tigre (10)

Eu estou tentando criar um botão sempre-op-top / imagem clicável que fica no topo de todas as janelas o tempo todo.

A prova de conceito é

Eu tenho tido sucesso e tenho um serviço em execução agora. O serviço exibe algum texto no canto superior esquerdo da tela o tempo todo, enquanto o usuário pode interagir livremente com o restante dos aplicativos da maneira normal.

O que estou fazendo é subclasse ViewGroup e adicioná-lo ao gerenciador de janelas raiz com a bandeira TYPE_SYSTEM_OVERLAY . Agora, quero adicionar um botão / imagem clicável no lugar desse texto, que pode receber eventos de toque em si mesmo. Eu tentei substituir "onTouchEvent" para todo o ViewGroup mas ele não recebe nenhum evento.

Como posso receber eventos apenas em algumas partes do meu grupo de visualização sempre no topo? Gentilmente 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;
    }
}

https://ffff65535.com


A partir do Android 4.x, a equipe do Android corrigiu um possível problema de segurança adicionando uma nova função adjustWindowParamsLw() na qual ele adicionará FLAG_NOT_FOCUSABLE , FLAG_NOT_TOUCHABLE e removerá os sinalizadores TYPE_SYSTEM_OVERLAY janelas TYPE_SYSTEM_OVERLAY .

Ou seja, uma janela TYPE_SYSTEM_OVERLAY não receberá nenhum evento de toque na plataforma ICS e, é claro, usar TYPE_SYSTEM_OVERLAY não é uma solução viável para ICS e dispositivos futuros.



Aqui está uma solução simples, Tudo que você precisa é inflar o layout XML, assim como você faz nos adaptadores de lista, apenas faça o layout XML para inflar. Aqui está o código que você precisa.

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

}

Bem, tente o meu código, pelo menos, dá-lhe uma seqüência de caracteres como sobreposição, você pode muito bem substituí-lo com um botão ou uma imagem. Você não vai acreditar que este é o meu primeiro aplicativo android LOL. De qualquer forma, se você é mais experiente com aplicativos Android do que eu, por favor, tente

  • alterando os parâmetros 2 e 3 em "new WindowManager.LayoutParams"
  • tente alguma abordagem de evento diferente

Encontrei uma biblioteca que faz exatamente isso: https://github.com/recruit-lifestyle/FloatingView

Há um projeto de amostra na pasta raiz. Eu corri e funciona conforme necessário. O plano de fundo é clicável, mesmo que seja outro aplicativo.


Eu sou um dos desenvolvedores do SDK da Tooleap . Nós também fornecemos uma maneira para os desenvolvedores exibirem sempre nas janelas e botões superiores, e lidamos com uma situação semelhante.

Um problema que as respostas aqui não abordaram é o dos "Botões Seguros" do Android.

Os botões filterTouchesWhenObscured têm o filterTouchesWhenObscured propriedade filterTouchesWhenObscured , o que significa que eles não podem ser interatuados, se colocados sob uma janela, mesmo que essa janela não receba toques. Citando a documentação do Android:

Especifica se é necessário filtrar toques quando a janela da vista é obscurecida por outra janela visível. Quando definida como true, a exibição não receberá toques sempre que um toast, uma caixa de diálogo ou outra janela aparecer acima da janela da exibição. Consulte a documentação de segurança {@link android.view.View} para mais detalhes.

Um exemplo de tal botão é o botão de instalação quando você tenta instalar os serviços de terceiros. Qualquer aplicativo pode exibir esse botão se adicionar ao layout da vista a seguinte linha:

android:filterTouchesWhenObscured="true"

Se você exibir uma janela sempre visível sobre um "Botão seguro", todas as partes de botão seguras cobertas por uma sobreposição não lidarão com nenhum toque, mesmo se essa sobreposição não for clicável. Portanto, se você planeja exibir essa janela, forneça uma maneira para o usuário movê-la ou descartá-la. E se uma parte da sua sobreposição for transparente, leve em consideração que o usuário pode estar confuso por que um determinado botão no aplicativo subjacente não está funcionando para ele de repente.


Na verdade, você pode tentar WindowManager.LayoutParams.TYPE_SYSTEM_ERROR em vez de TYPE_SYSTEM_OVERLAY. Pode soar como um hack, mas permite que você exiba a visualização sobre tudo e ainda receba eventos de toque.


Se alguém ainda estiver lendo este tópico e não conseguir fazer com que isso funcione, lamento informar que desta forma a interceptação do evento de movimento é considerada como bug e correção no android> = 4.2.

O evento de movimento que você interceptou, embora tenha ação como ACTION_OUTSIDE, retorna 0 em getX e getY. Isso significa que você não pode ver toda a posição de movimento na tela, nem pode fazer nada. Eu sei que o médico disse que vai chegar xey, mas a verdade é que não vai. Parece que isso é para bloquear key logger.

Se alguém tiver uma solução alternativa, deixe seu comentário.

ref: Por que ACTION_OUTSIDE retorna 0 toda vez no KitKat 4.4.2?

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


TRABALHANDO SEMPRE NO BOTÃO IMAGEM SUPERIOR

primeiro de tudo desculpe pelo meu Inglês

Eu edito seus códigos e faço o botão de imagem de trabalho que ouve seu evento de toque não dá controle de toque para seus elementos de fundo.

Também dá ouvintes de toque para fora de outros elementos

alingments botão são inferior e esquerda

você pode chagar alingments mas você precisa de chains cordinats em contato evento no if elemento

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

TYPE_SYSTEM_OVERLAY Esta constante foi descontinuada desde o nível da API 26. Use TYPE_APPLICATION_OVERLAY no lugar.





overlay