드디어 마지막 시간입니다..! 오늘은 지난번에 정의했던 HangulMaker를 이용하여 ChunjiinMaker를 만들어보도록 하겠습니다.
우선 이전 키보드들과 마찬가지로 KeyboardChunjiin을 작성합니다.
val firstLineText = listOf<String>("ㅣ", "·", "ㅡ","DEL")
val secondLineText = listOf<String>("ㄱㅋ", "ㄴㄹ", "ㄷㅌ", "Enter")
val thirdLineText = listOf<String>("ㅂㅍ","ㅅㅎ","ㅈㅊ",".,?!")
val fourthLineText = listOf<String>("한/영", "ㅇㅁ", "space", "!#1")
지금까지 잘 따라오셨다면 KeyboardEnglish 또는 KeyboardKorean을 참고하여 작성하실 수 있으리라 믿습니다..!
그런 뒤에 getMyOnclickListener를 통하여 다음과 같이 ChunjiinMaker를 사용할 수 있습니다.
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))
chunjiinMaker.clear()
}
when (actionButton.text.toString()) {
"한/영" -> {
keyboardInterationListener.modechange(0)
chunjiinMaker.clear()
chunjiinMaker.clearChunjiin()
}
"!#1" -> {
keyboardInterationListener.modechange(2)
chunjiinMaker.clear()
chunjiinMaker.clearChunjiin()
}
".,?!" -> {
chunjiinMaker.commonKeywordCommit()
}
else -> {
playClick(actionButton.text.toString().toCharArray().get(0).toInt())
try{
val myText = Integer.parseInt(actionButton.text.toString())
chunjiinMaker.directlyCommit()
inputConnection.commitText(actionButton.text.toString(), 1)
}catch (e: NumberFormatException){
chunjiinMaker.commit(actionButton.text.toString().toCharArray().get(0))
}
}
}
})
actionButton.setOnClickListener(clickListener)
return clickListener
}
물론 chunjiinMaker를 newInstance에서 초기화해야겠죠 ??
여기서 중요한 점은 천지인 키보드의 첫번째 문자만을 commit한다는 점입니다. 그리고 나머지 처리는 모두 ChunjiinMaker가 담당하도록 합니다.
ChunjiinMaker는 다음과 같이 HangulMaker를 상속받을 수 있도록 작성합니다.
class ChunjiinMaker(val inputConnection: InputConnection): HangulMaker(inputConnection)
그리고 필드는 다음과 같이 구상중인 문자를 담을 testChar와 각각 관련있는 리스트, 모든 리스트를 포함하는 리스트, 그리고 현재 입력중인 상태의 리스트 크게 네가지로 구분지을 수 있습니다.
var testChar:Char = '\u0000'
var isComposingMoum = false
var onlyMoum = false
var gkList = listOf<Char>('ㄱ','ㅋ','ㄲ')
var nlList = listOf<Char>('ㄴ','ㄹ')
var dtList = listOf<Char>('ㄷ','ㅌ','ㄸ')
var bpList = listOf<Char>('ㅂ', 'ㅍ', 'ㅃ')
var shList = listOf<Char>('ㅅ', 'ㅎ', 'ㅆ')
var jchList = listOf<Char>('ㅈ','ㅊ','ㅉ')
var aiueomList = listOf<Char>('ㅇ','ㅁ')
val commonKeywords = listOf<String>(".",",","?","!")
var wholeList:List<List<Char>> = listOf(gkList, nlList, dtList, bpList, shList, jchList, aiueomList)
var myList:List<Char>? = null
var listIndex = 0
var junFlagChunjiin = '\u0000'
var keywordIndex = 0
var keywordExpect = false
var stateThreeDot = false
가장 중요한 commit함수입니다.
override fun commit(c:Char){
if(keywordExpect){
inputConnection.finishComposingText()
keywordExpect = false
}
if(c == 'ㅣ' || c == '·' || c == 'ㅡ'){//모음구성
if(super.state == 0){//모음만으로 구성된 글자 ex) ㅠㅠㅠㅠㅠㅠㅠ
onlyMoum = true
if(testChar == '\u0000'){
testChar = c
inputConnection.setComposingText(testChar.toString(), 1)
}
else if(combination(c)){
inputConnection.setComposingText(testChar.toString(), 1)
}
else{
inputConnection.commitText(testChar.toString(), 1)
testChar = c
inputConnection.setComposingText(testChar.toString(), 1)
}
junFlagChunjiin = '\u0000'
}
else if(!isComposingMoum){
onlyMoum = false
testChar = c
if(c == '·'){
if(super.state == 3){
//종성까지 추가된 상태
inputConnection.setComposingText(super.makeHan().toString() + testChar, 2)
stateThreeDot = true
}
else if(!super.junAvailable()){//더이상 추가될 수 없는 모음일 경우 ex) 왜 + ·
super.directlyCommit()
inputConnection.setComposingText(testChar.toString(), 1)
}
else{
inputConnection.setComposingText(super.makeHan().toString() + testChar, 2)
super.state = 2
}
}
else{
super.commit(testChar)
}
listIndex = 0
isComposingMoum = true
}
else{
if(combination(c)){//이중모음으로 선언 가능한 경우
if(testChar == '‥'){
inputConnection.setComposingText(super.makeHan().toString() + testChar, 2)
}
else if(super.state == 2){//모음이 기대되는 상태
if(super.isDoubleJun()){//이중모음인 경우 두 모음을 모두 지운다.
super.delete()
super.delete()
}
else{//이중모음이 아닌 경우
super.delete()
}
super.commit(testChar)
super.junFlag = junFlagChunjiin//이중 모음일 경우 이전 모음을 설정한다.
junFlagChunjiin = '\u0000'//초기화
}
else{
super.commit(testChar)
// isComposingMoum = false
}
}
else{//이 + ㅣ와 같은 경우 이전 글자를 commit하고 모음으로만 구성된다고 설정한다.
super.directlyCommit()
testChar = c
inputConnection.setComposingText(testChar.toString(), 1)
isComposingMoum = false
onlyMoum = true
}
}
}
else if(myList == null){//첫입력
if(onlyMoum){//모음으로만 구성된 문자를 작성중이었다면 commit한다.
inputConnection.commitText(testChar.toString(), 1)
}
onlyMoum = false
testChar = c
for(list in wholeList){//전체 리스트를 순회하며 현재 텍스트를 포함하는 리스트를 찾는다.
if(list.indexOf(testChar) >= 0){
myList = list
listIndex = 1
}
}
super.commit(testChar)
isComposingMoum = false
}
else if(myList?.indexOf(c)!! >= 0){//현재 작성중인 문자에서 파생될수 있는 문자를 출력 ex) ㄱ -> ㅋ
if(onlyMoum){
inputConnection.commitText(testChar.toString(), 1)
}
onlyMoum = false
if(listIndex == myList?.size){//더이상 파생할 수 없을 경우 첫번째 문자로 돌아간다. ex) ㄲ -> ㄱ
listIndex = 0
}
testChar = myList?.get(listIndex)!!
listIndex++
if(super.state == 1 || super.state == 3){//단어를 대체하기 위하여 delete한 뒤 새로운 문자를 commit한다.
super.delete()
super.commit(testChar)
}
else{
super.commit(testChar)
}
isComposingMoum = false
}
else{
onlyMoum = false
testChar = c
for(list in wholeList){
if(list.indexOf(testChar) >= 0){
myList = list
listIndex = 1
}
}
super.commit(c)
isComposingMoum = false
}
}
모든 상태와 무관하게 작동할 수 있도록 코드를 작성하였지만, 아직은 계속해서 오류를 발견하고 수정해 나가는 단계입니다. 크게 모음구성, 자음의 첫입력, 한 곳을 두 번 이상 클릭하여 발생할 수 있는 자음 입력, 비슷한 자음이 아닌 다른 자음을 입력하는(myList가 변경되는)입력의 네가지로 구성되어 있습니다. 자세한 코드는 주석을 확인해주세요.
다음은 이중모음을 나타낼 수 있는 조합인지 여부를 판단하는 combination함수 입니다.
fun combination(c:Char):Boolean{//모음을 구성하기 위한 조합 성공시 true리턴
when(testChar){
'ㅣ' -> {
if(c == '·'){
testChar = 'ㅏ'
return true
}
else{
return false
}
}
'·' -> {
if(c == 'ㅡ'){
testChar = 'ㅗ'
return true
}
else if(c == 'ㅣ'){
testChar = 'ㅓ'
return true
}
else if(c == '·'){
testChar = '‥'
return true
}
else{
return false
}
}
'‥' -> {
if(c == 'ㅣ'){
testChar = 'ㅕ'
return true
}
else if(c == 'ㅡ'){
testChar = 'ㅛ'
return true
}
else{
return false
}
}
'ㅡ' -> {
if(c == 'ㅣ'){
testChar = 'ㅢ'
junFlagChunjiin = 'ㅡ'
return true
}
else if(c == '·'){
testChar = 'ㅜ'
return true
}
else{
return false
}
}
'ㅏ' -> {
if(c == '·'){
testChar = 'ㅑ'
return true
}
else if(c == 'ㅣ'){
testChar = 'ㅐ'
return true
}
else {
return false
}
}
'ㅓ' -> {
if(c == 'ㅣ'){
testChar = 'ㅔ'
return true
}
else{
return false
}
}
'ㅑ' -> {
if(c == 'ㅣ'){
testChar = 'ㅒ'
return true
}
else{
return false
}
}
'ㅕ' -> {
if(c == 'ㅣ'){
testChar = 'ㅖ'
return true
}
else{
return false
}
}
'ㅜ' -> {
if(c == '·'){
testChar = 'ㅠ'
return true
}
if(c == 'ㅣ'){
testChar = 'ㅟ'
junFlagChunjiin = 'ㅜ'
return true
}
else{
return false
}
}
'ㅠ' -> {
if(c == 'ㅣ'){
testChar = 'ㅝ'
junFlagChunjiin = 'ㅜ'
return true
}
else{
return false
}
}
'ㅗ' -> {
if(c == 'ㅣ'){
testChar = 'ㅚ'
junFlagChunjiin = 'ㅗ'
return true
}
else{
return false
}
}
'ㅚ' -> {
if(c == '·'){
testChar ='ㅘ'
junFlagChunjiin = 'ㅗ'
return true
}
return false
}
'ㅘ' -> {
if(c == 'ㅣ'){
testChar = 'ㅙ'
junFlagChunjiin = 'ㅗ'
return true
}
return false
}
'ㅝ' -> {
if(c == 'ㅣ'){
testChar = 'ㅞ'
junFlagChunjiin = 'ㅜ'
return true
}
return false
}
else -> {
return false
}
}
}
각각의 문자를 하드매핑하여 나타낼 수 있는 문자가 나온다면 true를 리턴할 수 있도록 하였습니다. 여기서 주의할 점은 이중모음으로 나타내는 문자라면 이전 문자를 따로 분류하기 위하여 junFlagChunjiin이라는 필드에 앞의 모음을 저장하는 것을 볼 수 있습니다. 이는 commit시에 hangulMaker에게 Flag를 전달하기 위함입니다.
다음은 directlyCommit함수입니다.
override fun directlyCommit(){
super.directlyCommit()
inputConnection.finishComposingText()
clearChunjiin()
}
HangulMaker의 directlyCommit을 실행하며 자기 자신도 초기화할 수 있도록 작성합니다.
그런 뒤에 delete함수를 작성합니다.
override fun delete(){
if(onlyMoum){//현재 커서가 모음으로만 구성된 문자일 경우
inputConnection.setComposingText("", 1)
clearChunjiin()
}
else if(stateThreeDot){//HangulMaker의 3번상태(자음+모음+자음)상태에서 .기호가 들어와 있는 상태
inputConnection.setComposingText(super.makeHan().toString(), 1)
clearChunjiin()
stateThreeDot = false
}
else if(super.state == 2 && super.isDoubleJun()){//상태 2이면서 이중모음이 들어와 있는 상태
super.delete()
setTestCharBefore()
}
else {
clearChunjiin()
super.delete()
}
listIndex = 0
}
마지막으로 특수문자 입력 함수, 초기화함수, isEmpty함수, 이중모음 이전의 상태를 반환하는 함수를 작성하면 ChunjiinMaker의 작성이 완료됩니다.
fun commonKeywordCommit(){//특수문자 입력 시
directlyCommit()
if(keywordIndex == commonKeywords.size){
keywordIndex = 0
}
inputConnection.setComposingText(commonKeywords[keywordIndex++], 1)
keywordExpect = true
}
fun clearChunjiin(){
testChar = '\u0000'
isComposingMoum = false
myList = null
listIndex = 0
onlyMoum = false
}
fun isEmpty():Boolean{
if(super.state == 0 && testChar == '\u0000'){
return true
}
return false
}
fun setTestCharBefore(){//이중모음 이전의 상태를 반환
if(testChar == 'ㅚ' || testChar == 'ㅘ' || testChar == 'ㅙ'){
testChar = 'ㅗ'
}
else if(testChar == 'ㅟ' || testChar == 'ㅝ' || testChar == 'ㅞ'){
testChar = 'ㅜ'
}
else if(testChar == 'ㅢ'){
testChar = 'ㅡ'
}
}
그동안 긴 글 읽어주셔서 감사드립니다. 궁금하신 점이나 틀린점이 있다면 언제든지 알려주시면 감사하겠습니다. 글이 도움이 되셨다면 하단의 공감버튼을 눌러주시면 감사하겠습니다.
'IT 프로그래밍-Android' 카테고리의 다른 글
[Android] 서버에서 데이터 불러오기 (0) | 2019.12.23 |
---|---|
[Android] 키보드 앱 만들기 후기 (12) | 2019.11.20 |
[Android] 커스텀 키보드 만들기(3/4) 외전 - HangulMaker (2) | 2019.11.08 |
[Android] 커스텀 키보드 만들기(3/4) - 쿼티 키보드 만들기 (7) | 2019.11.08 |
[Android] 커스텀 키보드 만들기(2/4) - 영문 키보드 만들기 (1) | 2019.11.05 |