HandlerThread는 Thread 클래스를 상속하고 내부에서 Looper.prepare()와 Looper.loop()를 실행하는 Looper 쓰레드입니다. 

 

Looper는 자신이 속한 Thread의 Message Queue에 추가되는 message를 기다리다가 꺼내서 이를 처리할 Handler에 디스패치합니다. 스레드에 Looper를 설정하려면 정적 메서드 prepare()와 loop()를 호출해야 합니다. 단, 안드로이드 MainThread는 Looper가 기본적으로 설정되어 있습니다.

 

단일스레드에서 순차적인 작업이 필요할 때 HandlerThread를 사용합니다.

 

 

화면을 터치하면 이벤트라는 것이 발생하게 되는데 이 이벤트는 리스너라는 것을 이용하여 다룰 수 있습니다.

 

 

터치 이벤트 이외에도 단말의 방향을 가로 방향으로 돌렸을 때도 이벤트가 발생하고 키패드에서 키를 하나 눌렀을 때도 이벤트가 발생합니다.

 

 

이런 이벤트를 어떻게 처리할 수 있는지 알아봅시다.

 

 

버튼을 클릭했을 때 버튼 클릭한 것을 이벤트라고 하는 걸로 만들어서 전달받을 수 있습니다.

전달받아서 처리할 수 있는데 이것을 이벤트 리스너라고 하는걸 등록해서 처리할 수 있습니다.

이게 버튼뿐만 아니라 뷰라고 하는 거에 대해서 거의 대부분 다 적용할 수 있습니다.

 

 

그래서 기본적인 이벤트 처리방법을 이해하면 굉장히 다양한 기능을 만들 수 있습니다.

 

 

이벤트는 가장 단순한게 버튼을 클릭했을 때를 생각해볼 수 있습니다.

버튼을 클릭하면 onClick이라는 이벤트가 발생됩니다. 그래서 그때 이 버튼에다가 OnClickListener라고 하는 리스너를 설정하면 이쪽으로 이벤트를 전달해줍니다. 

 

 

그런데 이 클릭은 좀 더 세분화해보면 터치 이벤트로 나눠볼 수 있습니다.

사용자가 스마트폰 단말의 화면에다가 손가락으로 터치를 하면 손가락으로 누를 때, 누른 상태에서 움직일 때, 손가락을 뗐을 때로 구분할 수 있습니다. 

 

 

그 다음에 키패드에서 키를 하나 눌렀을 때 우리가 어떤 키를 눌렀는지 확인할 수 있는 방법도 있습니다. 그리고 손가락을 눌러서 끌어당길 때 그 때 빨리할 수도 있고 느리게 할 수도 있을텐데 그런 것들을 빨리하거나 느리게 할 때 얼마나 빨리했는지 이런것들을 일일이 계산해야 하는데 제스처라고 하는걸 쓰면 계산하는 걸 알아서 내부에서 해줍니다.

 

 

그래서 제스처 이벤트라고 하는걸 처리하는 방법이 있습니다.

 

 

뷰마다 포커스가 주어질 수 있는데 이건 실제로 쓰는 경우는 쓰게 될 일은 많지 않지만 포커스라고 하는게 있습니다.

그리고 단말의 방향을 가로로 돌렸을 때 그때 받을 수 있는 이벤트도 있습니다.

 

 

그럼 간단히 뷰를 터치했을 때 발생하는 터치 이벤트에 대한 터치 이벤트 리스너를 만들어보겠습니다.

view에 onTouchListener를 만들어줍니다. 그러면 event.action으로 터치 정보가 넘어오고 MotionEvent를 이용해 각각의 상태를 구분할 수 있습니다. 그리고 마지막에 true로 정상적으로 처리됐음을 알려줍니다.

view.setOnTouchListener { v, event ->
    var action = event.action

    val curX = event.x
    val curY = event.y

    if (action == MotionEvent.ACTION_DOWN) {
        println("손가락 눌렸음 : $curX, $curY")
    } else if (action == MotionEvent.ACTION_MOVE) {
        println("손가락 움직임 : $curX, $curY")
    } else if (action == MotionEvent.ACTION_UP) {
        println("손가락 떼졌음 : $curX, $curY")
    }

    true
}

 

그럼 직접 터치하면 엄청 많이 호출되는 것을 볼 수 있습니다. 근데 이 엄청 많이 호출되는 것을 일일이 이게 얼마나 빠른 속도로 움직이냐라고 하는걸 가지고 기능을 만들어야 될 때가 있는데 일일이 하기 힘드니까 제스처라고 하는걸 가지고 자동으로 계산되게 만들 수 있습니다.

 

 

그럼 이번에는 제스처디텍터라는 것을 만들어 view2의 영역에서 테스트를 해보겠습니다.

detector = GestureDetector(
    this, object: GestureDetector.OnGestureListener {
        override fun onDown(e: MotionEvent?): Boolean {
            println("onDown() 호출됨")
            return true
        }

        override fun onShowPress(e: MotionEvent?) {
            println("onShowPress() 호출됨")
        }

        override fun onSingleTapUp(e: MotionEvent?): Boolean {
            println("onSingleTapUp() 호출됨")
            return true
        }

        override fun onFling(
            e1: MotionEvent?,
            e2: MotionEvent?,
            velocityX: Float,
            velocityY: Float
        ): Boolean {
            println("onFling() 호출 됨 : $velocityX, $velocityY")
            return true
        }

        override fun onScroll(
            e1: MotionEvent?,
            e2: MotionEvent?,
            distanceX: Float,
            distanceY: Float
        ): Boolean {
            println("onScroll() 호출됨 : $distanceX, $distanceY")
            return true
        }

        override fun onLongPress(e: MotionEvent?) {
            println("onLongPress() 호출됨")
        }
    })

view2.setOnTouchListener { v, event ->
    detector.onTouchEvent(event)
    true
}

 

이렇게 제스처디텍터를 사용하면 좀 더 편하게 처리할 수 있는 기능이 생깁니다. 예를들어 갤러리에서 사진을 볼 때 다음 사진을 보기위해 사진을 옆으로 스크롤해 넘길 때 끝까지 넘기는게 아니라 어느정도 넘기면 옆으로 탁 튕기면서 들어가는데 이 때 제스처이벤트를 사용하면 훨씬 쉽게 그런 기능을 구현할 수 있습니다. 

 

 

이번에는 키 이벤트에 대해서 알아보겠습니다.

키는 키패드를 얘기합니다. 키패드를 누르면 KEYCODE_HOME, KEYCODE_BACK, KEYCODE_1 등과 같은 키 값이 들어옵니다. 그래서 이 키 값을 전달받아서 처리하고 싶으면 onKeyDown이라던가 onKey와 같은 메서드를 재정의해서 사용하면 됩니다.  그럼 키 이벤트를 처리하는 코드를 만들어 보겠습니다.

override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
    if (keyCode == KeyEvent.KEYCODE_BACK) {
        toast("시스템 BACK 버튼 눌림")
        return true
    }
    return false
}

시스템 BACK키를 누르면 원래 화면이 없어지는데 이렇게하면 화면이 안없어지도록 만들 수 있습니다.

그래서 '한번 더 누르면 종료됩니다' 하는 메세지를 보여주는 그런 앱들을 볼 수 있습니다.

 

 

만든 코드는 아래에 첨부했습니다.

activity_my_event.xml

MyEvent.kt

Reference

 

 

새로운 앱을 디자인할 때, 어떤 색상을 활용하는 게 좋을까?

MONOCHROMATIC (단색)

  • 단색은 색상들을 동일한 기본 색으로부터 만들기 때문에 가장 만들기 간단한 Color Scheme(색 구성표)이다.
  • 단색은 안정감을 주는 효과가 있다.
  • 특히 Blue나 Green 색상이 가장 편한 느낌을 준다.
  • Adobe Color CC를 검색해서 도움을 받을 수 있다.

ANALOGOUS(유사색)

  • 유사색은 서로 이질적이지 않은 유사한 색상으로부터 만든다.
  • 한 가지 색상이 대표색으로 사용되면서 다른 색들이 컬러를 풍부하게 만들어 준다.
  • 색이 진해 지거나 옅어질 때 어떤 방향으로 변화를 줄 것인지를 잘 선택해야 한다.
  • Calm이라는 명상 앱은 사용자에게 편안하고 평화로운 느낌을 주기 위해 blue와 green의 유사색을 사용한다.

COMPLEMENTARY(보색)

  • 보색들은 서로 강하게 대비를 이루며, 보는 사람의 주의를 끌기 위해 사용된다.
  • 강한 대비를 위해 눈에 띄는 색상과 그 색상에 대비를 이루는 색상을 사용하는 것이 좋다.
  • 예를 들면, 사람의 눈이 green으로 가득찬 사물을 볼 때, 약간의 red는 아주 눈에 잘 띄어 보인다.

CUSTOM COLOR SCHEME

  • 자신만의 컬러스킴을 만드는 쉬운 방법 중 하나는, 무채색인 팔레트에 밝은 엑센트 색상을 추가하는 것이다. 이러면 시각적으로 도드라져 보이게 할 수 있는 방법이다.

Image credit: Adobe Color CC

  • Dropbox 컬러스킴은 White 캔버스에 cool grey, blue 엑센트 색상을 적용했다.

ADOBE COLOR CC

ADOBE COLOR CC를 이용해 컬러 스킴을 훨씬 편하게 만들 수 있다.

  • 기존 Kuler로 많이 알려진 ADOBE COLOR CC는 컬러 선택을 매우 쉽게 만들어 준다. 간단한 클릭으로 기본 색상으로부터 팔레트의 모든 색상을 각각 편집할 수 있다.

색상 대비 효과 : 색상이 서로 쉽게 구분되어 보일 때 대비가 강하다고 말한다.

  • 대비가 약한 색상들을 사용하면 디자인이 아름답고 조화롭게 보이기 때문에 디자이너는 약한 대비를 선호하기도 한다. 그러나 아름다운 것이 가독성을 위해 항상 좋은 것은 아니다.
  • 텍스트에 색상을 적용할 때, 배경과 대비가 약한 색상을 적용한다면, 읽기가 매우 힘들어질 것이다. 특히 모바일 디바이스는 사용자가 화면 반사를 일으키는 밝은 야외에서 사용하는 경우가 많기 때문에 그러한 경우가 더 많다.
  • 요소들 사이에 충분한 대비를 가지도록 대비율(Contrast ratio)만 체크하면 된다. 대미율은 한 색상이 다른 색상에 비해 얼마나 다른지를 나타내는 지표이다.(보통 1:1 or 21:1로 표기됨) 두 숫자들의 차이가 클수록, 두 색상의 선명도 차이가 상대적으로 커진다.
  • W3C는 본문과 이미지 텍스트에서 아래의 대비율을 권장한다.
    • 작은 텍스트는 배경 대비 4.5:1의 대비율이 있어야 한다.
    • 큰 텍스트는(bold 14pt / regular 18pt이상) 배경 대비 3:1의 대비율이 있어야한다.
    • 이 가이드라인은 시력이 좋지 않거나 색맹인 사용자, 시야가 좋지 않은 상황에서 스크린에서 텍스트를 보고 읽을 수 있도록 해준다.

가독성이 좋은 텍스트를 적용하는 것 외에도, 대비는 화면의 특정 요소에 대해 사용자의 주의를 끌게 해 줄 수 있다.

일반적으로, 중요한 콘텐츠나 주요 요소를 강조하는데 강한 대비를 사용한다. 사용자가 어떤 것을 보거나 클릭하게 하고 싶다면, 눈에 띄게 만들어라!

 

Reference

 

 

 

 

 

 

 

 

+ Recent posts