IT 프로그래밍-Android

[Android] 커스텀 키보드 만들기(3/4) - 쿼티 키보드 만들기

godsangin 2019. 11. 8. 14:15
반응형

지난 시간에 이어 오늘은 한글 오토마타를 생성하여 한글 키보드를 만들어 보도록 하겠습니다.

한글 키보드는 대부분의 키보드가 쿼티 키보드를 기반으로 하기 때문에 쿼티 키보드 제작을 기반으로 하였습니다.

영어 키보드와 마찬가지로 작성한 코드에 commit할 때 HangulMaker라는 커스텀 클래스를 만들어 사용하는 방법입니다.

val numpadText = listOf<String>("1","2","3","4","5","6","7","8","9","0")
val firstLineText = listOf<String>("ㅂ","ㅈ","ㄷ","ㄱ","ㅅ","ㅛ","ㅕ","ㅑ","ㅐ","ㅔ")
val secondLineText = listOf<String>("ㅁ","ㄴ","ㅇ","ㄹ","ㅎ","ㅗ","ㅓ","ㅏ","ㅣ")
val thirdLineText = listOf<String>("CAPS","ㅋ","ㅌ","ㅊ","ㅍ","ㅠ","ㅜ","ㅡ","DEL")
val fourthLineText = listOf<String>("!#1","한/영",",","space",".","Enter")

<KeyboardKorean.kt>

위와 같이 키보드를 구상해 주시고 KeyboardEngilsh와 같은 setLayoutComponents함수를 이용하여 각각의 버튼들은 바인드합니다.(Keyboard라는 상위 클래스를 만들었으면 더 좋았겠네요..)

그리고 한글 키보드를 초기화할 함수를 작성합니다.

lateinit var koreanLayout: LinearLayout
var isCaps:Boolean = false
var buttons:MutableList<Button> = mutableListOf<Button>()
lateinit var hangulMaker: HangulMaker
lateinit var vibrator: Vibrator
lateinit var sharedPreferences:SharedPreferences
var inputConnection:InputConnection? = null
  set(inputConnection){
  field = inputConnection
  hangulMaker = HangulMaker(inputConnection!!)
}
fun init(){
        koreanLayout = layoutInflater.inflate(R.layout.keyboard_action, null) as LinearLayout
        keyboardInterationListener = keyboardInterationListener
        hangulMaker = HangulMaker(inputConnection!!)
        vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
        context = context

        sharedPreferences = context.getSharedPreferences("setting", Context.MODE_PRIVATE)

        val height = sharedPreferences.getInt("keyboardHeight", 150)
        val config = context.getResources().configuration
        sound = sharedPreferences.getInt("keyboardSound", -1)
        vibrate = sharedPreferences.getInt("keyboardVibrate", -1)

        val numpadLine = koreanLayout.findViewById<LinearLayout>(
            R.id.numpad_line
        )
        val firstLine = koreanLayout.findViewById<LinearLayout>(
            R.id.first_line
        )
        val secondLine = koreanLayout.findViewById<LinearLayout>(
            R.id.second_line
        )
        val thirdLine = koreanLayout.findViewById<LinearLayout>(
            R.id.third_line
        )
        val fourthLine = koreanLayout.findViewById<LinearLayout>(
            R.id.fourth_line
        )

        if(config.orientation == Configuration.ORIENTATION_LANDSCAPE){
            firstLine.layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, (height*0.7).toInt())
            secondLine.layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, (height*0.7).toInt())
            thirdLine.layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, (height*0.7).toInt())
        }else{
            firstLine.layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, height)
            secondLine.layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, height)
            thirdLine.layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, height)
        }

        myKeysText.clear()
        myKeysText.add(numpadText)
        myKeysText.add(firstLineText)
        myKeysText.add(secondLineText)
        myKeysText.add(thirdLineText)
        myKeysText.add(fourthLineText)

        layoutLines.clear()
        layoutLines.add(numpadLine)
        layoutLines.add(firstLine)
        layoutLines.add(secondLine)
        layoutLines.add(thirdLine)
        layoutLines.add(fourthLine)
        setLayoutComponents()

    }
    
    fun getLayout():LinearLayout{
        return koreanLayout
    }

<KeyboardKorean.kt>

영어 키보드와 거의 유사하죠 ?? 여기서 중요한 점은 HangulMaker라는 클래스가 추가되었다는 것입니다.

그런 뒤에 getMyOnClickListener함수에서 다음과 같이

private fun getMyClickListener(actionButton:Button):View.OnClickListener{

        val clickListener = (View.OnClickListener {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                inputConnection?.requestCursorUpdates(InputConnection.CURSOR_UPDATE_IMMEDIATE)
            }
            playVibrate()
            val cursorcs:CharSequence? =  inputConnection?.getSelectedText(InputConnection.GET_TEXT_WITH_STYLES)
            if(cursorcs != null && cursorcs.length >= 2){

                val eventTime = SystemClock.uptimeMillis()
                inputConnection?.finishComposingText()
                inputConnection?.sendKeyEvent(KeyEvent(eventTime, eventTime,
                    KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL, 0, 0, 0, 0,
                    KeyEvent.FLAG_SOFT_KEYBOARD))
                inputConnection?.sendKeyEvent(KeyEvent(SystemClock.uptimeMillis(), eventTime,
                    KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL, 0, 0, 0, 0,
                    KeyEvent.FLAG_SOFT_KEYBOARD))
                hangulMaker.clear()
            }
            when (actionButton.text.toString()) {
                "한/영" -> {
                    keyboardInterationListener.modechange(0)
                    hangulMaker.clear()
                }
                "!#1" -> {
                    keyboardInterationListener.modechange(2)
                    hangulMaker.clear()
                }
                else -> {
                    playClick(actionButton.text.toString().toCharArray().get(0).toInt())
                    try{
                        val myText = Integer.parseInt(actionButton.text.toString())
                        hangulMaker.directlyCommit()
                        inputConnection?.commitText(actionButton.text.toString(), 1)
                    }catch (e:NumberFormatException){
                        hangulMaker.commit(actionButton.text.toString().toCharArray().get(0))
                    }
                    if(isCaps){
                        modeChange()
                    }
                }
            }
        })
        actionButton.setOnClickListener(clickListener)
        return clickListener
    }

<KeyboardKorean.kt>when절의 else구문과 다른 클릭 시에 hangulmaker를 초기화할 수 있도록 수정합니다. 위의 if(cursor...)는 포커스가 잡힌 글자의 개수에 따라 문자열이 삭제되고 새로 추가되어야하기 때문에 delete하는 코드를 포함하고 있습니다. 한글 입력시 숫자와 한글을 따로 처리해야 하기 때문에 저의 경우에는 NumberFormatException을 발생시켰습니다.(숫자로 변환될 수 없는 문자열이라면 hanguleMaker를 통하여 처리!)

 

HangulMaker편을 한번에 다루기에는 글이 너무 길어질거 같아 따로 정리해서 글을 작성하도록 하겠습니다. 오늘은 한글 키보드에만 집중해봅시다 !

한글입력에는 크게 초성, 중성, 종성으로 나누어져 있고, 현재 상태에서 들어오는 종류에 따라 각각 다른 상태로 이동하게 됩니다. 

한글 오토마타

저는 이러한 상태를 정의하고 현재 상태에서 입력되는 문자에 따라 글자를 완성 또는 변경하도록 HangulMaker를 만들었습니다. 이 다섯가지 상태를 제외한 이중모음, 이중자음과 같은 상황은 Flag를 통하여 처리하였습니다.(상태를 더 나누시는 것을 추천드립니다...)

이렇게 정의된 HangulMaker가 들어오는 문자에 따른 처리를 모두 담당하게 하도록 하여 작업을 조금 더 세분화할 수 있었습니다. 글이 너무 길어질 것 같아 HangulMaker를 따로 분리하여 게시하게 된 점 사과드립니다..ㅠㅠ

우선 이번 시간의 한글 키보드는 작성이 완료되었습니다. 궁금하신 점이 있다면 댓글 남겨주세요..!

앗.. 그리고 DELETE, ENTER, CAPS 등의 함수는 GitHub에 올라와 있는 전체 코드를 통하여 확인하시면 될거같습니다 !!(추후에 업데이트 하도록 하겠습니다.)