티스토리 뷰

반응형

Stack Overflow에 자주 검색, 등록되는 문제들과 제가 개발 중 찾아 본 문제들 중에서 나중에도 찾아 볼 것 같은 문제들을 정리하고 있습니다.

Stack Overflow에서 가장 먼저 확인하게 되는 가장 높은 점수를 받은 Solution과 현 시점에 도움이 될 수 있는 가장 최근에 업데이트(최소 점수 확보)된 Solution을 각각 정리하였습니다.

 

아래 word cloud를 통해 이번 포스팅의 주요 키워드를 미리 확인하세요.

Creating a system overlay window (always on top)

시스템 오버레이 윈도우(항상 다른 앱 위에) 만들기

 문제 내용 

I'm trying to create an always-op-top button/clickable-image which stays on top of all the windows all the time.

저는 항상 모든 윈도우 위에 머무르는 상단 버튼/클릭 가능한 이미지를 만들려고 합니다.

 

The proof of concept is

제 컨셉은 아래와 같습니다.

 

I have been successful and have a running service now. The service displays some text on top left corner of screen all the time while user can freely interact with rest of apps in normal manner.

저는 컨셉 적용에 성공했고 지금 실행중인 서비스가 있습니다. 이 서비스는 사용자가 정상적인 방식으로 나머지 앱과 자유롭게 상호 작용할 수 있는 동안 화면 왼쪽 상단에 일부 텍스트를 항상 표시합니다.

 

What I'm doing is subclass ViewGroup and add it to root window manager with flag TYPE_SYSTEM_OVERLAY. Now I want to add a button/clickable-image in place of this text which can receive touch events on itself. I tried overriding "onTouchEvent" for the whole ViewGroup but it does not receive any event.

내가 하고 있는 일은 ViewGroup을 하위 클래스로 만들고 플래그 TYPE_SYSTEM_OVERLAY를 사용하여 루트 윈도우 매니저에 추가하는 것입니다. 이제 이 텍스트 대신 자체적으로 터치 이벤트를 수신할 수 있는 버튼/클릭 가능한 이미지를 추가하고 싶습니다. 전체 ViewGroup에 대해 "onTouchEvent"를 재정의하려고 시도했지만 어떤 이벤트도 수신되지 않습니다.

 

How can I receive events only on certain parts of my always-on-top view group? Kindly suggest.

최상단 뷰 그룹의 특정 부분에서만 이벤트를 수신하려면 어떻게 해야 합니까? 제안해주세요.

 

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

 

 

 높은 점수를 받은 Solution 

This might be a stupid solution. But it works. If you can improve it, please let me know.

이것은 어리석은 해결책일지도 모른다. 하지만 그것은 효과가 있다. 당신이 개선할 수 있다면, 저에게 알려주세요.

 

OnCreate of your Service: I have used WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH flag. This is the only change in service.

서비스 생성 시: 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);
    }

 

Now, you will start getting each and every click event. So, you need to rectify in your event handler.

이제 모든 클릭 이벤트를 받기 시작합니다. 따라서 이벤트 핸들러에서 수정해야 합니다.

 

In your ViewGroup touch event

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

 

Also you may need to add this permission to your manifest:

또한 매니페스트에 다음 권한을 추가해야 할 수도 있습니다.
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

 

 

 가장 최근 달린 Solution 

The TYPE_SYSTEM_OVERLAY constant was deprecated since API level 26. Use TYPE_APPLICATION_OVERLAY instead.

TYPE_SYSTEM_OVERLAY 상수는 API 수준 26 이후 더 이상 사용되지 않습니다. 대신 TYPE_APPLICATION_OVERLAY를 사용합니다.

 

For users below and above Android 8:

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

 

 

출처 : https://stackoverflow.com/questions/4481226/creating-a-system-overlay-window-always-on-top

반응형
댓글
공지사항
최근에 올라온 글