티스토리 뷰

안녕하세요

몇달만이지 모르겠습니다만.. 이번에는 최상위에 떠 있는 뷰를 만들어 보겠습니다

최상위에 떠있는 뷰란.. Q슬라이드나 뭐 이런거 처럼 화면에 떠 있는 뷰를 뜻합니다




프로젝트 생성 및 레이아웃은?

최상위에 떠있는 뷰는 액티비티를 종료해도 화면 위에 남아야 합니다

그러므로 꼭 서비스를 이용해서 만들어야 합니다


메인 레이아웃은 서비스 시작/종료 버튼만 만들어 줍시다

(원래 시작/종료를 2개 만들어서 터치O서비스, 터치X서비스 이렇게 만들어야 하지만 코드의 간결성을 위해 하나만 했습니다, 아래에 언급할겁니다)



코드 보기


그리고.. 최상위에 떠다니는 "뷰" 이므로 화면위에 있을 "뷰"를 만들어 줘야 하는데요

두가지로 나눌수 있습니다


하나는 터치가 되는 뷰, 나머지는 터치가 안되는 뷰



전자는 터치이벤트를 받을수 있어서 팝업 동영상등에, 뒤는 터치를 못받으므로 스크린필터? 이런 용도로 씁니다



물론 둘다 만들어 볼겁니다 ㅎㅎ




터치이벤트를 못받는 뷰와 관련된 레이아웃 파일 입니다

이름 : always_on_top_view_not_touch.xml



코드 보기


간결하게 TextView 하나만 있습니다

이 TextView는 나중에 중앙에 항상 뜨도록 만들겠습니다



또하나는 터치를 받을수 있는 뷰인데요

간단하게 이미지뷰 하나만 뒀습니다


이름 : always_on_top_view_touch.xml



터치 이벤트를 받아서 뭐할건가.. 생각해봤는데요

그냥 움직여보겠습니다




최상단 액티비티를 띄우려면 권한이 필요해요

화면에 뷰를 추가하기 위한 권한을 추가해 줍시다


<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />





서비스 생성하기

최상단 뷰를 만들기 위해 서비스를 사용해야 합니다

이 강좌에서는 터치가 되는것, 안되는것 두가지를 만들 예정이므로 서비스를 2개 생성해야 합니다


구체적인 생성법은 아래 링크를 참고해 주세요


[Development/App] - #23 Service (서비스)에 대해 알아보자



[java] : 파일 생성

AlwaysTopServiceTouch.java - 서비스 생성 (링크 참조해서 생성하세요)

AlwaysTopServiceNotTouch.java - 서비스 생성 (링크 참조해서 생성하세요)


[AndroidManifest.xml] : 코드 추가

<service android:name="itmir.tistory.examplewindowview.AlwaysTopServiceNotTouch" />

<service android:name="itmir.tistory.examplewindowview.AlwaysTopServiceTouch" />


[MainActivity.java] : 메소드 추가

public void mStart(View v) {

    startService(new Intent(this, AlwaysTopServiceNotTouch.class));

}


public void mStop(View v) {

    stopService(new Intent(this, AlwaysTopServiceNotTouch.class));

}




AlwaysTopServiceNotTouch 서비스를 봐주세요

자....... 서비스까지 잘 생성하셨으리라 믿고 넘어가겠습니다



본격적으로 자바소스를 알아보기 전에 그림으로 알아볼까 합니다




모식도(?)입니다



private View mView;

private WindowManager mManager;


화면에 최상단 뷰를 추가하기 위해서 WindowMananger를 사용할겁니다



서비스의 onCreate()메소드 입니다


@Override
public void onCreate() {
    super.onCreate();

    LayoutInflater mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    mView = mInflater.inflate(R.layout.always_on_top_view_not_touch, null);

    WindowManager.LayoutParams mParams = new WindowManager.LayoutParams(
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.WRAP_CONTENT,

            WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,

            WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,

            PixelFormat.TRANSLUCENT);

    mManager = (WindowManager) getSystemService(WINDOW_SERVICE);
    mManager.addView(mView, mParams);
}


하나씩 확인해 보겠습니다

5~6번은 전에 본적이 있으실겁니다


커스텀 알림때 배운 레이아웃 인플레이터입니다


[Development/App] - #17 커스텀 알림(Alert) 띄우기



(이제야 우려먹네요;)



19~20부분에서 뷰를 추가하는데요

그림이 뭐낙 잘만들어서(?) 이해하시는데 큰 어려움은 없을거라 믿고.....



8~17이 문제네요

API를 봅시다


WindowManager.LayoutParams mParams = new WindowManager.LayoutParams(w, h, _type, _flags, _format)


저 순서대로 집어넣어 주는건데요


  • WindowManager.LayoutParams.WRAP_CONTENT

이건 WRAP_CONTENT가 눈에 잘 들어오실탠대 생각하시는 그거 맞습니다


  • WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY

이건 항상 최상위에 있도록 해주는 타입입니다

반대로 아래에서 사용하게될 WindowManager.LayoutParams.TYPE_PHONE은 터치 이벤트도 받을수 있습니다


  • WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH

이건 뷰를 제외한 나머지 부분의 터치를 가능하게 해준다...라고 알고있습니다


  • PixelFormat.TRANSLUCENT

이건 API를 보면 _format이라 되어있는데 dev.android.com찾아봐도 뭔지 모르겠내요

(아시는분 계시다면 덧글로 꼭 알려주세요!)



그럼.. 이제 터치 못받는건 마무리 해볼께요



아래는 스크린샷 입니다


   




AlwaysTopServiceTouch 서비스를 봐주세요

중요한건 위에서 다 언급했기 때문에 바뀐 점만 언급하고 마치려 합니다


mView.setOnTouchListener(mViewTouchListener);

mParams = new WindowManager.LayoutParams(
        WindowManager.LayoutParams.WRAP_CONTENT,
        WindowManager.LayoutParams.WRAP_CONTENT,
        WindowManager.LayoutParams.TYPE_PHONE,
        WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
        PixelFormat.TRANSLUCENT);

mParams.gravity = Gravity.TOP | Gravity.LEFT;


TYPE_PHONE과 FLAG가 변경되었네요


  • WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE

포커스를 가지지 않게 합니다




그리고 mView.setOnTouchListener(mViewTouchListener); 이부분에서 mViewTouchListener를 언급하지 않았네요


private float mTouchX, mTouchY;
private int mViewX, mViewY;

private OnTouchListener mViewTouchListener = new OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {

        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:

            mTouchX = event.getRawX();
            mTouchY = event.getRawY();
            mViewX = mParams.x;
            mViewY = mParams.y;

            break;

        case MotionEvent.ACTION_UP:
            break;

        case MotionEvent.ACTION_MOVE:
            int x = (int) (event.getRawX() - mTouchX);
            int y = (int) (event.getRawY() - mTouchY);

            mParams.x = mViewX + x;
            mParams.y = mViewY + y;

            mManager.updateViewLayout(mView, mParams);

            break;
        }

        return true;
    }
};


뭐.. 이런구조인데요

중요한건 터치이벤트 리스너를 받을수 있고, 그 이벤트를 받아서 mParams을 수정하고, updateViewLayout()로 위치를 수정한다 라는 원리입니다



   




예제 다운로드




마치며

강좌 완성도를 점차 올리고 싶은데 시간도 없고.. 내용도 어렵고 해서 강좌를 자주 올리지 못하네요..

그리고 제 필력이 약해서 정확하게 이해하실수 있으실지도 모르겠습니다


예제소스도 함께 첨부했으니 이해가 안되신다면 소스 확인해보세요~



글 한번 쓰는거 너무 오래걸리고 힘드네요...

특히 그림 만드는거..



이 글이 유용하시다면 덧글과 제 블로그에 있는 수입원(?) 한번씩 터치해주시면 감사드리겠습니다..

미밴드 사고싶어서 빨리 모였으면 하네요 ㅎㅎ..




참조

http://blog.daum.net/mailss/18

http://blog.daum.net/mailss/35



저작자 표시 비영리 변경 금지
신고
댓글
  • 이전 댓글 더보기
  • Favicon of http://blog.daum.net/aldkzm zelaw 항상 잘 보고 있습니다.
    혹시 터치하면 아이콘이 바꿀 수 없을까요?
    단순히 움직이는 것이 아닌 아이콘이 다른 그림으로 바뀌는거요.
    2015.09.21 16:09 신고
  • Favicon of http://itmir.tistory.com Mir(whdghks913) 터치 이벤트를 받으면 mManager.updateViewLayout 이부분을 이용하시면 될것 같습니다~ 2015.09.23 15:27 신고
  • 팬팬 안녕하세요. 서비스로 잠금화면을 구현하려고 하는데요 지금 최상위 뷰로 이미지를 띄우셨는데 이 클래스 안에 onclicklistener을 사용해서 버튼을 추가할 수 있을까요? 해보니까 activity를 사용하지 않아서 받아오지를 못하는것 같습니다 ㅠㅠ 2015.11.12 11:20 신고
  • Favicon of http://itmir.tistory.com Mir(whdghks913) setOnTouchListener로도 안되나요? 2015.11.13 13:34 신고
  • 지나가던이 감사합니다 2015.11.18 16:49 신고
  • 전땡칠 궁금한 점이 있습니다~
    저렇게 always on을 해 놓으면 어플을 항상 구동을 한다는 것일텐데
    그러면 HW적으로 배터리소모와는 관계가 있을까요?
    2016.02.19 16:13 신고
  • 지나가는 사람 컴포지션 시 연산량 증가로 인해 매우 살짝 증가합니다. 계속 updatelayout 하지 않는다면 미미할것으로 보입니다. 2016.03.02 20:34 신고
  • 비밀댓글입니다 2016.03.28 16:56
  • Favicon of http://itmir.tistory.com Mir(whdghks913) 상단바와 같은 앱은 순정에서 구현이 불가능할겁니다. 시스탬 API를 쓸텐데 이게 3rd-party앱에선 못쓰거든요 2016.03.28 19:41 신고
  • 비밀댓글입니다 2016.04.12 23:14
  • Favicon of http://itmir.tistory.com Mir(whdghks913) 윗 답글에 이미 답변하였습니다. 2016.04.12 23:16 신고
  • 비밀댓글입니다 2016.04.12 23:19
  • Favicon of http://itmir.tistory.com Mir(whdghks913) 상태바를 대체하는 앱이 있긴 하던데 그 작동원리는 따로 찾아보지 않아 잘 모르겠습니다.. 작년이라면 앱을 뜯어보라도 할텐데 올해는 여유가 없어서요.. 2016.04.12 23:33 신고
  • jjunest 미르님 빠른답변 다시한번 감사드립니다! 2016.04.12 23:20 신고
  • rojhw 안녕하세요. 질문이 있어 댓글 남깁니다. Lock 걸렸을떄 아이콘이 안보이게 하는방법은 없을까요? 2016.05.10 17:45 신고
  • 굼금 안녕하세요 항상 미르님 좋은 글 잘 보고있습니다.
    혹시 뷰 아래의 액티비티가 잠시 onPause 상태가 되거나 다른 액티비티로 넘어갔을때 이벤트를 감지해서 뷰를 없앨 수 있는 방법이 있을까요?
    2016.06.08 17:46 신고
  • Favicon of http://itmir.tistory.com Mir(whdghks913) onPause를 감지하는건 힘들 것 같습니다만 다른 액티비티가 실행되는 것을 서비스로 확인하는 것은 가능합니다. 최상단 액티비티에 대해 검색해보세요. 2016.06.08 20:23 신고
  • 굼금 서비스에서 최상단 액티비티를 받아 올 수는 있는데 화면 전환 이벤트를 어떻게 받아야 할지 감이 안오네요 ㅠㅠ
    접근성 서비스를 이용하고 있는데 홈 버튼이나 메뉴 버튼을 눌러 액티비티를 벗어나게 되면 접근성 이벤트가 발생되지 않네요 ㅠㅠ
    2016.06.09 16:24 신고
  • 쿠니 상세한 설명 감사합니다^^!
    터치 이벤트가 적용되어있는 서비스 버튼에서 버튼이 마음대로 움직이지 않고 왼쪽 벽에 붙어서 위아래로만 왔다갔다 거리길래 봤더니
    터치 이벤트 있는 버튼에서는
    mParams.gravity = Gravity.TOP | Gravity.LEFT;
    이 부분은 빼야 버튼이 마음껏 움직이네요! 참고해주세요^^!
    2016.07.27 21:21 신고
  • 프로그래밍 초보자 권한을 메인엑티비티.XML에다가 추가했는데 왜 오류가 날까요...? 2016.09.07 17:43 신고
  • Favicon of http://itmir.tistory.com Mir(whdghks913) 어떤 오류가 발생하시나요? 2016.09.07 20:18 신고
  • 김병희 다운해서 돌리니 상쾌하게 잘 돌아가네요.
    글자 깨지는 건 소스파일 열어서 붙이고 ...
    반드시 찾아야 했던 소스인데,
    정말로 감사합니다.
    정리해서 제 블로그에 올려도 될까요?
    즐겁고 건강하시기 바랍니다.
    2016.09.08 02:19 신고
  • Favicon of http://itmir.tistory.com Mir(whdghks913) 제 블로그 링크를 참고글로 넣어주시면 가져가시는데 아무런 지장 없습니다~ 2016.09.09 00:59 신고
  • 김병희 저는 고정해놓고 쓰기 때문에 필요한 소스만 추려서 다 구현했습니다.
    다시 한 번 감사를 드립니다.
    그런데 둥둥이를 오른쪽 위로 배치하니 액션바 우상단으로 붙어버립니다.
    mParams.gravity = Gravity.RIGHT | Gravity.TOP;
    마진을 주어서 액션바와 제가 만든 툴바 아래 정도 위치를 잡아주려는데, 구글링해서도 방법을 찾지 못했습니다.
    그럴싸한 안내글들을 따라서 해 보아도, 제 실력으로 성공하지 못했습니다.
    도움 주시면 고맙겠습니다.
    2016.09.10 01:44 신고
  • Favicon of http://itmir.tistory.com Mir(whdghks913) app:layout_anchor="@id/mAppBar"
    app:layout_anchorGravity="bottom|right|end"

    이것이 김병희님께서 목표하신 방법이 아닐까 생각됩니다.
    구글 플레이에서 "성적표"앱을 검색해보시면 제가 만든 앱을 보실 수 있는데요. 메인 화면에 들어가시면 플로팅 액션 버튼의 위치를 확인하실 수 있습니다.
    2016.09.10 13:33 신고
  • 김병희 가르쳐주신 앱을 설치하여 모양을 잘 보았습니다.

    AlwaysTop.java
    // mParams.gravity = Gravity.RIGHT | Gravity.CENTER;
    주석처리하고

    항상 위에 보일 onscreen.xml 이미지 레이아웃에
    xmlns:app="http://schemas.android.com/apk/res-auto"
    app:layout_anchor="@id/toolbar"
    app:layout_anchorGravity="bottom|right|end" />
    속성을 주었는데,

    화면 한가운데 뜨는군요.

    혹시나 하고, 레이아웃을 (1) 루트뷰 안에 이미지뷰 (2) 이미지뷰만 - 두 가지로 테스트했습니다.
    혹시나 하고, 앵커를 (1) 메인의 Toolbar 아이디 (2) 툴바 담은 LinearLayout 아이디 - 두 가지로 테스트했습니다.

    선생님의 앱에는 잘 떠 있던데, 무엇을 잘못해서 그럴까요?

    그리고 자바소스 주석처리한 것도 자바에서 Gravity 지정하면 레이아웃의 Gravity 지정이 무시되는 듯하여 그랬는데, 주석처리하고 돌려도 마찬가지이군요.
    또 layout_anchor 는 자바소스에서 mParams... 방식으로 지정하는 방법을 찾지 못했습니다.

    다른 방식으로 타협 처리해도 되는 문제이지만, 선생님의 친절한 답변에 감사하고 이 기회에 하나 확실하게 배울 수 있을까 싶어 다시 질문 드립니다.
    바쁘시면 천천히 보아주셔도 됩니다.

    감사합니다.
    2016.09.11 02:59 신고
  • Favicon of http://itmir.tistory.com Mir(whdghks913) https://github.com/chrisbanes/cheesesquare

    이 예제를 다운받아 빌드해보시겠어요?
    2016.09.14 12:54 신고
  • 김병희 바쁘신 줄 알고 며칠 들리지도 않았는데 ...
    시간을 내어 친절하게 가르쳐 주셨군요.
    깃으로 풀해서 살펴보겠습니다.
    감사합니다.
    2016.09.19 08:46 신고
  • 김병희 뷰페이저의 appbar가 말려 올라가면서 앵커를 잃은 둥둥도 함께 사라지는 형상이군요.
    복잡한 코드 다 보지 않아서 잘못 파악한 것이지 모르지만,
    어쨌든 둥둥을 아무 곳에나 심어두고 layout_anchor layout_anchorGravity 속성으로 제어하는 레이아웃 소스 잘 보았습니다.
    감사합니다.
    2016.09.19 09:39 신고
  • 가나다 잘보고 잘 이해했습니다
    그래서 응용으로 터치 됐을때 다이얼 로그를 띄우게 할려 고 했지만 실행되지 않고 계속 중지 되버리네요(팅김?);;
    좀 도와주실수있나요?
    2016.12.28 23:00 신고
  • 안녕하세요 저는 WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, 부분에서 TYPE_SYSTEM_OVERLAY부분이 선으로 그어져있고요 앱 실행하면 텍스트가 안떠요
    어떻게 해결해야하나요?
    2017.09.17 23:07 신고
  • Favicon of http://itmir.tistory.com Mir(whdghks913) @Deprecated 되었나봅니다.(다른 코드로 대체되어 현재는 사용하지 않는 코드에 취소선이 그어집니다.)
    다른 해야할 일 때문에 제가 최근 안드로이드 API를 반영하지 못하고 있어서 이 글도 오래된 버전의 안드로이드에서 동작하는 낡은 코드를 설명하고 있습니다.
    제가 연말에 리뉴얼을 할 예정이지만, 그때까지는 다른 블로그도 참고해주시면 감사드리겠습니다. 정말 죄송합니다..
    2017.09.17 23:27 신고
댓글쓰기 폼