Android/Custom View

[안드로이드] 스크롤이 가능한 탭 커스텀 뷰

c0de_h0ng 2022. 12. 29. 11:57
728x90

구글에서는 material 디자인 컴포넌트를 지원하고 있다. 이러한 컴포넌트의 장점은 '적은 코드로 많은 기능을 구성할 수 있다' 이다. 대표적으로 TabLayout이 그 예이다. TabLayout은 Viewpager와 연동도 가능하며 Tab 자체에 스타일 xml이나 코드로 커스텀할 수 있어 Tab UI에 많이 사용된다. 하지만 내가 만든 것이 아니라 material 디자인 컴포넌트 규칙을 따라야할 때는 커스텀하기가 영 힘든 일이다.

불편한 거 내가 처음부터 커스텀해보지 해서 이번에 ScrollTablayout을 만들어 보았다.

ScrollTabLayout

ScrollTabLayout의 결과물을 보면 TabLayout과 거의 흡사하다. 다만 여기서 tab의 디자인이라던가 텍스트 폰트 등은 쉽게 커스텀할 수 있도록 제작하였다.

ScrollTabLayout의 특징

  • 탭 스타일 및 텍스트를 쉽게 커스텀 가능
  • 탭을 눌렀을 때 해당 탭 아이템이 UI 중간에 오도록 설정
  • 탭을 눌렀을 때 select 이벤트를 enable하는 옵션

설정

fun init(
        inputTabList: List<Triple<String?, String?, String?>>?,
        enableEventIdList: List<String>? = null,
        isRefresh: Boolean = true,
        selectBackground: Int? = null,
        unselectBackground: Int? = null,
        selectTabFont: Typeface? = null,
        unselectTabFont: Typeface? = null,
        selectTextColor: Int? = null,
        unselectTextColor: Int? = null,
        tabTextSize: Int? = null,
        clickEvent: (index: Int, categoryItem: ScrollTabItem?, scrollX: Int) -> Unit
) {
  
}
  • inputTabList
    • 탭 정보
    • Triple을 사용해서 탭 정보 3가지
      • 아래 탭 정보 ScrollTabItem에서 언급
  • enableEventIdList
    • 탭 클릭 이벤트 enable할 탭 설정
  • isRefresh
    • 탭 새로고침 여부
  • selectBackground, unselectBackground
    • 탭 배경
  • selectTabFont, unselectTabFont
    • 탭 텍스트 폰트
  • selectTextColor, unselectTextColor
    • 탭 텍스트 색상
  • tabTextSize
    • 탭 텍스트 사이즈

탭 정보 ScrollTabItem

data class ScrollTabItem(
    var id: String? = null,
    var name: String? = null,
    var link: String? = null
)

탭 정보 id, name, link가 있으며 id는 구분값, name은 탭 텍스트, link는 탭을 눌렀을 때 이동시켜야할 경우의 link

ScrollTabItem 리스트로 컨버팅

fun convertScrollTabList(inputTabList: List<Triple<String?, String?, String?>>?): List<ScrollTabItem> {
   if (inputTabList.isNullOrEmpty()) return mutableListOf()

      val result = mutableListOf<ScrollTabItem>()

      inputTabList.forEach { inputTab ->
          val scrollTabItem = ScrollTabItem()

          val id = inputTab.first
          val name = inputTab.second
          val link = inputTab.third

          if (!id.isNullOrEmpty()) scrollTabItem.id = id
          if (!name.isNullOrEmpty()) scrollTabItem.name = name
          if (!link.isNullOrEmpty()) scrollTabItem.link = link
          result.add(scrollTabItem)
    }

    return result

}

만약 서버로 부터 데이터를 받을 경우에는 다양한 형태의 데이터 구조로 받게 되는데 ScrollTabItem 데이터 형태에 맞게 컨버팅하는 함수이다.

탭 설정

val menuBinding = InterlibItemScrollTabBinding.inflate(LayoutInflater.from(context)).apply {
    tvDummyTab.text = item.name
    tvTab.text = item.name
    tvDummyTab.typeface = selectTabFont
}

setTabStyle(menuBinding, index == 0)


private fun setTabStyle(itemBinding: InterlibItemScrollTabBinding?, isSelected: Boolean) {
     // TextView
     itemBinding?.tvTab?.typeface = if (isSelected) selectTabFont else unselectTabFont
     itemBinding?.tvTab?.setTextColor(ContextCompat.getColor(context, if (isSelected) selectTextColor else unselectTextColor))

     // Background
     itemBinding?.clTabBg?.setBackgroundResource(if (isSelected) selectBackground else unselectBackground)
}

private fun setSelectedIndex(index: Int) {
     val id = tabList.getOrNull(index)?.id ?: ""
     if (!enableEventIdList?.firstOrNull { id == it }.isNullOrEmpty()) return

     if (scrollTabBinding.llTabList.childCount > 0) {
         scrollTabBinding.llTabList.children.forEachIndexed { i, view ->
             setTabStyle(DataBindingUtil.bind(view), index == i)
         }
     }
}


menuBinding.root.setOnClickListener { menu ->
   setSelectedIndex(index)
   val deviceCenterX = DeviceUtil.getDeviceWidth(context) / 2
   val location = IntArray(2)
   menu.getLocationInWindow(location)
   val menuCenterX = location[0] + menu.width / 2
   val moveX = menuCenterX - deviceCenterX                       // 스크롤이 움직여야하는 x좌표
   val centerX = scrollTabBinding.hsScrollTab.scrollX + moveX
   clickScrollX = centerX
  listener?.clickCategory(index, item, centerX)                                  
}

 

적용

init(categoryList) { index, categoryItem, scrollX ->
}
setSelectedIndex(scrollTab.backupTabPosition, scrollTab.backupTabScrollX)