์์ฝ
์๋๋ก์ด๋ ์ ๋ ฅ์ฐฝ์์ ์ด๋ชจ์ง ํ๋๋ฅผ ํ ๊ธ์๋ก ์ธ์ํ์ง ๋ชปํ๋ ๊ฒฝ์ฐ๊ฐ ์์ด์.
๊ทธ๋์ ์ด๋ชจ์ง ํ๋๋ฅผ ํ ๊ธ์๋ก ์ฌ๊ธฐ๊ณ ๊ธ์ ์๋ฅผ ์ธ๊ธฐ ์ํด์๋ ์์ ์ด ํ์ํฉ๋๋ค. ์ ๋ ์ปค์คํ InputFilter๋ฅผ ๋ง๋ค์์ด์.
๊ธฐํ์ ์ ๋ ฅ์ฐฝ์ ๊ธ์์ ์ ํ์ด ์์์ต๋๋ค. ์ผ๋ฐ ๊ธ์, ์ซ์, ๊ธฐํธ, ์ด๋ชจ์ง๋ฅผ ํฌํจํด์ ๊ธ์์ ์ ํ์ด ์์๊ณ , ๋น์ฐํ ์ง๊ด์ ์ผ๋ก ์ด๋ชจ์ง ํ๋๋ ํ ๊ธ์ ์๋๊ฒ ์ต๋๊น. ๋๋ฌด๋ ์ฝ๊ฒ ์๊ฐํ๊ณ ๊ธฐํ๋๋ก ์์ ํ๋ค๊ณ ์๊ฐํ๋๋ฐ, ํ ์คํธ๋ฅผ ํด๋ณด๋ ์ด๋ชจ์ง๋ฅผ ๊ธ์ ํ๋ ์ด์์ผ๋ก ์ธ๋๊ตฐ์.๐ณ ๋๊ฐ ์ ๊ฐ ํ ์คํธํ๋ ์ด๋ชจ์ง๋ค์ ํ ์ด๋ชจ์ง๋น 2๊ธ์ ์ ๋๋ก ์ธ์ํ์ต๋๋ค.
iOS์์๋ ์ด๋ชจ์ง ํ๋๋ฅผ ํ ๊ธ์๋ก ์ธ์ํด์ ์ด๊ฒ๊ณผ ๊ด๋ จํด ๋ฌธ์ ๊ฐ ์์ด ์ฝ๊ฒ ๊ฐ๋๋ฐ, ์๋๋ก์ด๋๋ ๊ทธ๋ ์ง ๋ชปํด์ ์์ผ๋ก ํดํด๊ฑฐ๋ฆฌ๊ณ ๐คจ๐คจ์ ๊ทธ๋ฐ์ง, ๊ทธ๋ฆฌ๊ณ ํด๊ฒฐ ๋ฐฉ๋ฒ์ด ์๋์ง ๊ตฌ๊ธ์๊ฒ ๋ฌผ์ด๋ดค์ต๋๋ค.
๊ทธ๋ฆฌ๊ณ ์ด๋ ๊ฒ ์ข์ ๊ธ์ ์ฐพ์๋์ต๋๋ค!!
๊ธ์์๋ฅผ ์ธ๋ 7๊ฐ์ง ๋ฐฉ๋ฒ - LINE ENGINEERING
์๋ ํ์ธ์. ๋ผ์ธํ๋ฌ์ค ๊ฐ๋ฐ์ค์์ ์ผํ๊ณ ์๋ ๋ฐ์์ง์ ๋๋ค. ์ด ๋ธ๋ก๊ทธ์์๋ ๊ธ์ ์๋ฅผ ์ธ๋ ๋ฐฉ๋ฒ์ ๋ํด์ ์๊ธฐํด๋ณด๊ณ ์ ํฉ๋๋ค. ๋ผ์ธ ์๋น์ค์์๋ ํ๋กํ์ด๋ฆ, ๊ทธ๋ฃน์ด๋ฆ, ์ํ๋ฉ์์ง ๋ฑ
engineering.linecorp.com
๋ผ์ธ ๊ฐ๋ฐ์๋ถ๋ค ๋ฉ์ ธ์!๐ ์ ๊ธ์ ์์ฑํ์ ๋ฐ์์ง๋ ๋๋ฌด๋๋ฌด ๋ฉ์ ธ์!๐ ๋๋ฌด ๋ฉ์ ธ์!!!!๐๐๐๐
๊ธ์์ ์ ์, ๊ธ์ ์์ ์ ์์ ๋ฐ๋ผ ๊ธ์ ์นด์ดํธํ๋ ๋ฐฉ๋ฒ์ด ๋ค๋ฅด๋ค๋ ๊ฒ ์ ๊ธฐํ์ด์.
์ ๊ฐ ์ธ๊ณ ์ ํ๋ ์ด๋ชจ์ง๋ Grapheme์ผ๋ก, ๋ฌธ์ ์ฒด๊ณ์์ ํํํ ์ ์๋ ๊ธ์์ ์ต์ ๋จ์๋ค๋ก ์ธ์ผ ํ๋๊ตฌ๋๋ฅผ ์ ๊ธ๋ฅผ ๋ณด๊ณ ์์์ด์. ๋ํ BreakIterator์ ์กด์ฌ์ ๋ํด์๋ ์๊ฒ ๋์์ด์.
BreakIterator (Java 2 Platform SE 5.0)
java.text ํด๋์ค BreakIterator java.lang.Object java.text.BreakIterator ๋ชจ๋ ๊ตฌํ๋ ์ธํฐํ์ด์ค: Cloneable public abstract class BreakIteratorextends Object implements Cloneable BreakIterator ํด๋์ค๋ ํ ์คํธ๋ด์ ๊ฒฝ๊ณ์ ์์น๋ฅผ ์ฐพ
cris.joongbu.ac.kr
์ ๋ ์ ๋ ฅ์ฐฝ์ ๊ธ์์ ์ ํ์ด ๊ฑธ๋ฆฌ๋๋ก ํํฐ๋ฅผ ๋ฃ๊ณ ์ ํ๊ณ , ์์์ ์ฐพ์ ๊ฒ๋ค์ ํตํด์ ์๋ ๊ฒ ์ฝ๋๋ฅผ ์งฐ์ด์.
๊ธฐ์กด InputFilter.LengthFilter๋ฅผ ์ฐธ๊ณ ํ๋ต๋๋ค.
class TextEmojiLengthFilter(private val maxLength: Int) : InputFilter.LengthFilter(maxLength) {
override fun filter(
source: CharSequence?,
start: Int,
end: Int,
dest: Spanned?,
dstart: Int,
dend: Int
): CharSequence? {
Timber.d("source : $source, start: $start, end: $end")
Timber.d("dest : $dest, dstart: $dstart, dend: $dend")
val destLength = getLength(dest)
var keep = maxLength - (destLength - (dend - dstart))
Timber.d("์
๋ ฅ ๊ฐ๋ฅ ๋ฒ์ keep : $keep")
val lengthAndBoundary = getLengthAndLastBoundary(source, keep)
val sourceLength = lengthAndBoundary.first
Timber.d("์
๋ ฅ๋ ๋ฌธ์ ๊ธธ์ด : $sourceLength")
if(keep <= 0) {
return ""
} else if(keep >= sourceLength) {
return null
} else {
val boundary = lengthAndBoundary.second
Timber.d("๋ง์ง๋ง ๋ฒ์ : $boundary")
val result = source!!.subSequence(start, boundary)
Timber.d("๊ฒฐ๊ณผ : $result")
return result
}
}
private fun getLength(text: CharSequence?): Int {
var count = 0
text?.let { s ->
val it = BreakIterator.getCharacterInstance()
it.setText(s.toString())
while (it.next() != BreakIterator.DONE) {
count++
}
Timber.d("getLength $s > ์ด๋ชจํฐ์ฝ ํฌํจ ๊ธ์ ์ : $count")
}
return count
}
private fun getLengthAndLastBoundary(text: CharSequence?, keepLength : Int): Pair<Int, Boundary> {
var count = 0
var boundary = 0
text?.let { s ->
val it = BreakIterator.getCharacterInstance()
it.setText(s.toString())
while (it.next() != BreakIterator.DONE) {
count++
if (count == keepLength) {
boundary = it.current()
}
}
Timber.d("getLengthAndLastBoundary $s > ์ด๋ชจํฐ์ฝ ํฌํจ ๊ธ์ ์ : $count")
}
return Pair(count, boundary)
}
}
typealias Boundary = Int
์ ๊ฐ ์ง ์ฝ๋์์ ์์ ์ฌํญ์ด๋ ๊ฐ์ ์ฌํญ์ด ์์ผ๋ฉด ๊ผญ๊ผญ ๋๊ธ๋ก ์๋ ค์ฃผ์ธ์! ๋ถํ๋๋ฆฝ๋๋ค!๐
InputFilter ๋ง๊ณ ๋ ๋ค๋ฅธ ๋ฐฉ๋ฒ์ผ๋ก ์ ๋ ฅ์ฐฝ(EditText)์ ์ด๋ชจ์ง ํฌํจ ๊ธ์์๋ฅผ ์ ํํ ์ ์๋ ๋ฐฉ๋ฒ์ด ์๋ค๋ฉด ๊ณต์ ๋ถํ๋๋ ค์๐๐
๊ฐ์ฌํฉ๋๋ค!. ๋.
'๊ณณ๊ฐ์์ ์ธ์ฌ๋๋ค > ์ค๋ฌด' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Android][Compose][์ฝ์ง ๊ตํ] Compose Migration + Expandable List (0) | 2023.02.23 |
---|---|
[Kotlin] Calendar extensions (0) | 2023.01.12 |
[Android][Kotlin] ์ฌ์ฉ์๊ฐ ์ ํํ ํญ์ ๋ฌธ๊ตฌ์ ๋ณผ๋ ์ฒ๋ฆฌ (0) | 2023.01.12 |
[Kotlin] BaseUseCase (0) | 2023.01.12 |
[Android][Kotlin] ๋ฌธ๊ตฌ ๋ถ๋ถ ์ ๋ณ๊ฒฝ SpannedString (0) | 2023.01.12 |