24.01.19 'ㄱ''ㄴ''ㄷ''ㄹ'헤더 추가

KSang·2024년 1월 22일
0

TIL

목록 보기
36/101

연락처 가나다순 헤더

연락처를 보면 가나다라 순으로 정렬되면서 헤더에 초성 'ㄱ''ㄴ'등이 들어간걸 볼 수 있다.

정렬을 할때 처음엔 sort를 쓰긴했는데 그러면 한글은 후순위로 쭉 밀리게 된다

어떻게 해결할까?

일단 정렬하는 함수를 만들어줬다

    fun sortContacts(contacts: MutableList<User>): List<User> {
        val collator = java.text.Collator.getInstance(Locale.KOREAN)
        return contacts.sortedWith { contact1, contact2 ->
            collator.compare(contact1.name, contact2.name)
        }
    }

Java에서 Collator라는 클래스를 이용하면 되는데

Collator는 문자열을 특정 로케일에 맞게 비교하고 정렬한다

여기서는 한국어를 비교하는데 사용했다

compare메소드를 사용해서 두 문자를 비교한다

문자열이 동일하면 0, 첫번째 문자열이 두번째 문자열보다 앞서면 음수, 뒤면 양수를 반환하는식으로 정렬하는데

이렇게 각문자열을 비교해서 한글순으로 비교하게된다

정렬을 했으니 이걸 자음으로 변환 시켜보자

fun getKorean(name: String): String {
        if (name.isEmpty()) return "#"

        val firstChar = name.first()
        return when {
            firstChar in '가'..'깋' -> "ㄱ"
            firstChar in '나'..'닣' -> "ㄴ"
            firstChar in '다'..'딯' -> "ㄷ"
            firstChar in '라'..'맇' -> "ㄹ"
            firstChar in '마'..'밓' -> "ㅁ"
            firstChar in '바'..'빟' -> "ㅂ"
            firstChar in '사'..'싷' -> "ㅅ"
            firstChar in '아'..'잏' -> "ㅇ"
            firstChar in '자'..'짛' -> "ㅈ"
            firstChar in '차'..'칳' -> "ㅊ"
            firstChar in '카'..'킿' -> "ㅋ"
            firstChar in '타'..'팋' -> "ㅌ"
            firstChar in '파'..'핗' -> "ㅍ"
            firstChar in '하'..'힣' -> "ㅎ"
            firstChar.isDigit() -> "0"
            firstChar.isLetter() -> "A-Z"
            else -> "#"
        }
    }

데이터에서 name의 first에 오는 문자가 어디에 속했는지 범위를 정했고

..내에 속하면 "ㄱ"을 반환하는 식으로 구성을 했다.

getKorean에 넣으면 문자들은 전부 자음으로 변하게 되는 함수를 만들었고 이걸 이용해서 헤더를 만들것이다

class HeaderList {
    operator fun invoke(contacts: MutableList<User>): List<Any> {
        val header = mutableListOf<Any>()
        var lastHeader = ""

        contacts.forEach { user ->
            val initial = getKorean(user.name)
            if (initial != lastHeader) {
                header.add(initial)
                lastHeader = initial
            }
            header.add(user)
        }
        return header
    }

유저데이터의 리스트를 받아서 이걸 전부 getKorean에 넣어준다

그냥 넣기만하면 자음들이 겹칠테니 스택방식을 이용해서 같은글자는 한번만 받고 유저가 어디에 속한지 알아야하니 유저의 정보도 같이 받는다

그러면 ,,,,,,,, 이런식의 배열이 완성되는데

프래그먼트에서 타입이 String인지 User인지 구별해주면된다

                createHeader(UserList.userList).map {
                    when (it) {
                        is String -> ContactViewType.Header(it)
                        is User -> ContactViewType.ContactUser(it)
                        else -> ContactViewType.Header("error")
                    }
                }

여기서 뷰타입을 지정해주면 끝

여기서 숫자는 한글순으로 비교해도 숫자가 더 앞서기 때문에 추가적으로 정렬을하는 함수를 써야할것 같다고 느꼈지만 일단 생략

오른쪽위 아이콘을 보면 그리드 배열로 바꿔주는 아이콘이 있는데

이걸 그냥 넣으면

헤더유저유저유저
유저유저

처럼 이상하게 된다

헤더는 한줄을 전부 채우고 유저들이

헤더
유저유저유저유저

이런식으로 되게하려면 Grid레이아웃 매니저에서

spansize를 변경시켜줘야한다

spansize를 간략하게 설명한다면

그리드 레이아웃에서 이 녀석이 몇칸을 차지하는지 정의하는것이라고 보면된다

예를 들어 spancount가 4면 각행에 들어갈 수 있는 아이템의 갯수는 기본적으로 4칸인데

spansize를 2로설정하면 혼자서 2칸을 4로 설정하면 혼자서 모든칸을 차지 할수있다.

앞서 전에 정의한 어댑터 인터페이스를 보면

interface AdapterInterface {
    fun updateColor(newColorTheme: ColorTheme)
    fun changeLayout(layoutType: LayoutType)
    fun notifyDataSetChanged()
    fun getItemViewType(position: Int): Int
}

getItemViewType을 받았는데 이걸 어댑터에서 보면

override fun getItemViewType(position: Int): Int {
//        return when (mItem[position]) {
        return when (filteredList[position]) {
            is ContactViewType.ContactUser -> ITEM_VIEW_TYPE_ITEM
            is GridUser -> ITEM_VIEW_TYPE_GRID
            is ContactViewType.Header -> ITEM_VIEW_TYPE_HEAD
            is ContactViewType.MyProfile -> Item_VIEW_TYPE_SELF
        }
    }

이렇게 리사이클러뷰의 아이템을 정의한걸 볼 수 있다

여기서 각타입을 정의한부분을 보면

companion object {
        private const val ITEM_VIEW_TYPE_GRID = 0
        private const val ITEM_VIEW_TYPE_ITEM = 1
        private const val ITEM_VIEW_TYPE_HEAD = 2
        private const val Item_VIEW_TYPE_SELF = 3
    }

이런식으로 숫자로 되어있는걸 볼 수있는데

어댑터의 getItemViewType을 불러내면

override fun onLayoutTypeChanged(type: LayoutType) {
        val context = binding.recyclerView.context
        when (type) {
            LayoutType.GRID -> {
                val gridManager = GridLayoutManager(context, 4)
                gridManager.spanSizeLookup = object :SpanSizeLookup() {
                    override fun getSpanSize(position: Int): Int {
                        return when (adapter?.getItemViewType(position)){
                            0 -> 1
                            else -> 4
                        }
                    }

                }
                binding.recyclerView.layoutManager = gridManager
            }
            else -> {
                binding.recyclerView.layoutManager = LinearLayoutManager(context)
            }
        }
    }

이런식으로 adapter?.getItemViewType(position) 타입의 숫자를 받고 값이 0이면 아이템 하나당 1칸
타입의 숫자가 0이아니면 아이템 하나당 4칸을 차지하게해서

그리드 배열에서도

이런식으로 헤더를 구별해서 나타낼 수 있다.

0개의 댓글