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)