시스템 오버레이 창 만들기 (항상 상단)


285

항상 모든 창 위에 항상있는 항상 작동하는 버튼 / 클릭 가능한 이미지를 만들려고합니다.

개념 증명은

나는 성공했고 지금 서비스를 운영하고 있습니다. 이 서비스는 항상 화면 왼쪽 상단에 일부 텍스트를 표시하지만 사용자는 정상적인 방식으로 나머지 앱과 자유롭게 상호 작용할 수 있습니다.

내가하고있는 일은 서브 클래스 ViewGroup이며 flag를 사용하여 루트 창 관리자에 추가합니다 TYPE_SYSTEM_OVERLAY. 이제이 텍스트 대신 터치 이벤트를 수신 할 수있는 버튼 / 클릭 가능한 이미지를 추가하고 싶습니다. 전체에 대해 "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;
    }
}

2
@coreSOLO, 나는 당신이 대답하기를 바랍니다. 이상하게 비슷한 질문은 아무런 대답도 얻지 못했습니다. :) +1
st0le

3
@ : 나는 해결책을 찾아 당신과 함께 공유 I 때까지 내 낮과 밤을 보낼거야 약속 st0le
coreSOLO

1
@ st0le 그래 아마 당신과 내가 함께 파고, 나에게 당신의 질문의 링크를주고 당신이 얼마나 많은 진전을 이루 었는지 알려주세요 ...
coreSOLO


3
당신 onDraw()이 부름 받고 있다는 것은 놀라운 일입니다 . dispatchDraw()대신 재정의 해서는 안 됩니까? 참조 groups.google.com/forum/?fromgroups#!topic/android-developers/...
FAIZAL

답변:


158

이것은 어리석은 해결책 일 수 있습니다. 그러나 작동합니다. 개선 할 수 있으면 알려주십시오.

귀하의 서비스 생성시 : 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" />

1
@ pengwang : 어떤 오류가 발생합니까? 이 코드는 독립적이지 않으므로 적절한 프로젝트에서 적절한 파일 이름으로 프로젝트에 배치해야합니다.
Sarwar Erfan


6
전체 코드가 정확한지, 난 내 실수를 발견, 내가 링크를 잊지 <사용-권한은 안드로이드 : 이름 = "android.permission.SYSTEM_ALERT_WINDOW"/> Sarwar 에판 내가 당신의 대답을 편집 할 생각은 더 멋진 될 수 있습니다
pengwang

1
이 답변은 한 가지를 제외하고는 매우 유용합니다. 코드에 사용 된 WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH 문서에 따르면 "전체 업 / 이동 / 다운 제스처를받지 않습니다". 즉, 스 와이프 또는 ACTION_DOWN 이외의 다른 것을 감지 할 수 없습니다. Wave Launcher는 이와 유사한 개념을 사용하고 스 와이프를 감지 할 수 있기 때문에 스 와이프를 감지 할 수 있다는 것을 알고 있습니다. 아무도 이것이 어떻게 작동하는지 설명 할 수 있습니까?
Brian

6
@SarwarErfan-2.3에서 완벽하게 코킹을 사용했지만 onTouch 이벤트는 Android 4.0, 1nd 4.1에서 괴롭히지 않습니다. 도와주세요
Sanjay Singh

66

@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"/>

5
내 화면이
멈췄습니다

좋은 포스트. 향후 릴리스에서도이 기능이 비활성화되지 않았기를 바랍니다.
Cookster

애니메이션이 작동하지 않습니다 ... :( 그리고 4.2.X의 어떤 관점에서도 클릭을 잡을 수 없습니다. 어떤 제안이 있습니까?
Kamran Ahmed

작동하지만 해당 바로 가기 (예 : 시스템 수준 경고)를 사용하면 다른 대화 상자가 표시되지 않습니다. 왜 ?
Chimu

@Arju 무슨 뜻인가요? 당신은 그것을 제거하려고합니까?
amirlazarovich

18

안드로이드 4.x의 시작, 안드로이드 팀은 새로운 기능을 추가하여 잠재적 인 보안 문제를 해결 adjustWindowParamsLw()이 추가됩니다있는 FLAG_NOT_FOCUSABLE, FLAG_NOT_TOUCHABLE제거 FLAG_WATCH_OUTSIDE_TOUCH를위한 플래그를 TYPE_SYSTEM_OVERLAY창.

즉, TYPE_SYSTEM_OVERLAY창은 ICS 플랫폼에서 터치 이벤트를 수신하지 않으며 물론 TYPE_SYSTEM_OVERLAYICS 및 향후 장치 에 사용할 수있는 솔루션이 아닙니다.


4
대체 대안은 무엇입니까?
radhoo

1
이 ICS에 대한 질문을 참조하십시오 stackoverflow.com/questions/9656185/type-system-overlay-in-ics
clemp6r

13

저는 Tooleap SDK 개발자 중 한 사람입니다 . 또한 개발자가 항상 상단 창과 버튼에 표시 할 수있는 방법을 제공하며 비슷한 상황을 처리했습니다.

여기서 답변하지 않은 한 가지 문제는 Android "보안 버튼"입니다.

보안 단추에는 filterTouchesWhenObscured속성이 있습니다. 즉, 창 아래에 배치 된 경우 해당 창에 접촉이없는 경우에도 상호 작용할 수 없습니다. 안드로이드 문서 인용 :

다른 보이는 창으로보기 창을 가리면 터치를 필터링할지 여부를 지정합니다. true로 설정하면 토스트, 대화 상자 또는 다른 창이보기 창 위에 나타날 때마다보기에 터치가 수신되지 않습니다. 자세한 내용은 {@link android.view.View} 보안 설명서를 참조하십시오.

이러한 단추의 예로는 타사 APK를 설치하려고 할 때 설치 단추가 있습니다. 다음 행을보기 레이아웃에 추가하면 모든 앱에서 이러한 단추를 표시 할 수 있습니다.

android:filterTouchesWhenObscured="true"

"보안 단추"위에 항상 맨 위 창을 표시하면 해당 오버레이를 클릭 할 수없는 경우에도 오버레이로 덮여있는 모든 보안 단추 부분이 터치를 처리하지 못합니다. 따라서 이러한 창을 표시하려는 경우 사용자가 창을 이동하거나 닫을 수있는 방법을 제공해야합니다. 오버레이의 일부가 투명하다면, 기본 앱의 특정 버튼이 갑자기 작동하지 않는 이유가 사용자에게 혼란을 줄 수 있다는 점을 고려하십시오.


내 질문에 대답 해 주시겠습니까 : stackoverflow.com/questions/28964771/…
Nauman Afzaal

안드로이드 10에는 오버레이보기가 위에 표시 할 수없는 몇 가지 활동이 있습니다. 이유를 말하거나 내 질문을 참조하십시오. 매우 도움이 될 것입니다 stackoverflow.com/questions/59598264/…
Mateen Chaudhry

12

상단 이미지 버튼에서 항상 작동

우선 내 영어에 대해 죄송합니다

코드를 편집하고 터치 이벤트를 수신하는 작업 이미지 버튼으로 배경 요소를 터치 제어하지 않습니다.

또한 다른 요소에서 터치 청취자를 제공합니다.

버튼 앨링먼트는 아래쪽과 왼쪽입니다

alingments를 chage 할 수 있지만 if 요소에서 touch 이벤트에서 cordinats를 chaging해야

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

이것은 작동하지만 이미지 주위를 클릭하지 않고 이미지를 클릭했는지 어떻게 확인할 수 있습니까?
Liam W

@LiamW, 터치의 좌표는이 예제에서 kangaroo.getWidth ()와 비교됩니다. 이미지를 그리는 위치에 따라 자신의 코드에서 비교할 대상을 추론 할 수 있습니다.
예브게니 심킨

아주 좋아요! 완벽하게 작동합니다.
예브게니 심킨

이것이 작동하는 최소 안드로이드 버전은 무엇입니까? 나는 이것을 작성하는 데 노력하고 있지만 안드로이드에 익숙하지 않으므로 초보자 시간이 있습니다.
Noitidart

6

실제로 TYPE_SYSTEM_OVERLAY 대신 WindowManager.LayoutParams.TYPE_SYSTEM_ERROR를 시도 할 수 있습니다. 그것은 해킹처럼 들릴 수 있지만 모든 것을 맨 위에 표시하고 여전히 터치 이벤트를 얻을 수 있습니다.


1
나는 그것을 작동시키기 위해 모든 것을 시도했지만 성공하지 못했습니다 .... TYPE_SYSTEM_ERROR를 추가하면 작업이 완료되었습니다 .... 실제로 TYPE_SYSTEM_OVERLAY는 터치를 차단했습니다 ..... + 1.
Robi Kumar Tomar

6

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

5

이 시도. 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

이것은 나를 위해 작동하지 않습니다, 내 화면이 배경과 상호 작용하지 않습니다. 당신이 알고있는 다른 방법은 ...
Vinuthan

@Vinuthan 당신의 출력은 무엇
입니까

아니요, 응답하지 않습니다. 배경과 상호 작용할 수 있도록 활동을 화면의 절반 이상으로 채웠지만 배경과 상호 작용할 수 없습니다.
Vinuthan

FLAG_WATCH_OUTSIDE_TOUCH로 인해 배경과 상호 작용할 수 없습니다.
Richard Le Mesurier

1
TYPE_PHONE은 저에게 효과적이었습니다. 이제 터치가 전달됩니다. 적어도 버튼을 탭할 수 있습니다.) 감사합니다!
vir us

3

다음은 간단한 해결책입니다. 목록 어댑터에서와 마찬가지로 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;
    }

}

3

그 일을하는 도서관을 찾았습니다. https://github.com/recruit-lifestyle/FloatingView

루트 폴더에 샘플 프로젝트가 있습니다. 나는 그것을 실행했으며 필요에 따라 작동합니다. 다른 앱인 경우에도 배경을 클릭 할 수 있습니다.

여기에 이미지 설명을 입력하십시오


추천 해 주셔서 감사합니다. 저는이 위대한 프로젝트를 찾으려고 노력했습니다.
8:27에


1

내 코드를 사용해보십시오. 최소한 오버레이로 문자열을 제공하므로 버튼이나 이미지로 대체 할 수 있습니다. 당신은 이것이 나의 최초의 안드로이드 앱 LOL이라고 믿지 않을 것입니다. 어쨌든 당신이 나보다 안드로이드 앱에 더 경험이 있다면, 시도하십시오

  • "새 WindowManager.LayoutParams"에서 매개 변수 2와 3 변경
  • 다른 이벤트 접근을 시도하십시오

나는 결국 이것을 추구 할 시간을 찾지
못했다

1

여전히이 스레드를 읽고이 작업을 수행 할 수없는 경우 모션 이벤트를 가로 채는이 방법을 버그로 간주하고 android> = 4.2에서 수정하는 것이 유감입니다.

가로 채기 한 모션 이벤트는 ACTION_OUTSIDE로 동작이 있지만 getX 및 getY에서 0을 반환합니다. 즉, 화면에서 모든 모션 위치를 볼 수 없으며 아무것도 할 수 없습니다. 나는 의사가 x와 y를 얻을 것이라고 말했지만 사실은 그렇지 않을 것입니다. 이것은 키 로거를 차단하는 것으로 보입니다.

해결 방법이있는 사람이 있으면 의견을 남겨주세요.

ref : KitKat 4.4.2에서 ACTION_OUTSIDE가 왜 0을 반환합니까?

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


1

서비스를 사용하면 다음을 달성 할 수 있습니다.

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

1

@Sarwar Erfan의 답변은 Android에서 WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY를 사용하여 WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH를 사용하지 않고도 더 이상 창에 창을 추가 할 수 없으므로 더 이상 작동하지 않습니다.

이 문제에 대한 해결책을 찾았습니다. 다음 질문에서 확인할 수 있습니다.

WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY를 사용하여 창에보기를 추가하면 터치 이벤트가 발생하지 않습니다.


추천 솔루션도 마시멜로에서 작동하지 않습니다
Ahmad Shahwaiz

@AhmadShahwaiz 링크를 다시 확인하십시오. 언급 한 문제에 대해 의견을 추가했습니다
patilmandar2007

예, 이제 작동합니다. 그러나 startActivityForResult (intent, OVERLAY_REQ_CODE)를 호출 한 후 콜백 응답을받지 못했습니다. protected void onActivityResult (int requestCode, int resultCode, Intent data) 메서드에서.
Ahmad Shahwaiz

@AhmadShahwaiz onActivityResult 메소드에서 콜백을받는 활동을 사용하여 링크에 언급 된 변경 사항을 호출해야합니다. 서비스에서 위의 권한을 확인하는 경우 서비스에서 시작하기 위해 더미 투명 활동을 추가해야 할 수 있습니다.이 활동은 사용자에게 권한을 요청한 다음 onActivityResult 더미 활동에서 사용자로부터 결과를 얻습니다. 사용자 응답에 따라 창에보기를 추가하거나 표시하지 않으려면 서비스에 알리십시오
patilmandar2007

1

이것은 오래된 질문이지만 최근에 Android는 Bubbles를 지원합니다. Bubbles는 곧 출시 될 예정이지만 현재 개발자들은이를 사용할 수 SYSTEM_ALERT_WINDOW있습니다. (Facebook Messenger와 MusiXMatch는 같은 개념을 사용합니다)과 같은 앱.

기포

버블은 알림 API를 통해 생성되며 알림을 정상적으로 보냅니다. 버블 링을 원하면 추가 데이터를 첨부해야합니다. Bubbles에 대한 자세한 내용은 Bubbles 공식 Android 개발자 안내서를 참조하십시오 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.