FAB๋ฅผ ์ด์ฉํ ํด๋ฆญ์ด๋ฒคํธ๋ก ๋ฉ๋ด๋ฅผ ์ฃผ๋ฅด๋ฅต ๋จ๊ฒ ๋ง๋ค์ด์ผํ๋ค.
์ด๋ฐ ์์ผ๋ก ๋ง๋๋ ๊ฒ์ด์๋๋ฐ, ์ด๊ฑด github์ ๊ฒ์ํ๋ฉด ์ต์๋จ์ ๋์ค๋ ์ ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค. ๊ทธ๋ฅ ์จ๋ ๋์ง๋ง, 3๋ ์ ์ด ๋ง์ง๋ง ์ ๋ฐ์ดํธ์ธ ๊ฒ๋ ์๊ณ , ์ด๋ฒ์ ํ๋ฒ ๋์ ํด๋ณด์๋ ์๋ฏธ์์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฐ์ง ์๊ณ ๊ตฌํํด๋ณด๊ธฐ๋ก ํ๋ค.
# ์ฒซ ์๋ - popup๋ฉ๋ด๋ฅผ ๋ฌ์๋ณด๊ธฐ
`/res/menu`์ ์ฌ์ฉํ ๋ฉ๋ด๋ค์ ์ ์ธํ๊ณ , ๊ตฌํํด๋ดค๋ค. ๋จ์ํ popup๋ฉ๋ด ์ด๋ฒคํธ์ show ํธ๋ฆฌ๊ฑฐ๋ฅผ fab ํด๋ฆญ์ผ๋ก ๋ฌ์๋ ํํ์๋ค...
์ด๋์๋ ๋ด๊ฐ ์ํ๋ ํํ๋ฅผ ๋ง๋ค ์ ์์๋ค.
ํผ๊ทธ๋ง ๋๋ก๋ฉด ์ด๋ฐ ๋ชจ์์ ๋ง๋ค์ด์ผํ๋๋ฐ, ์ข ์ฐพ์๋ณด๋ค ๋ณด๋ `ListPopupWindow`๋ผ๋ ๊ฑธ ์ฐพ์ ์ ์์๋ค. spinner์ ์ผ์ข ์ผ๋ก ํธ์คํธ๋ก ์ด๋ค ๋์์ ์ ํด์, ๊ทธ๊ฑธ ๊ธฐ์ค์ผ๋ก ๋ชฉ๋ก์ ๋ณด์ฌ์ฃผ๋ ์์ ฏ์ด๋ค.
# `ListPopupWindow`๋ก ๊ตฌํ
๋ชฉ๋ก์ adapter๋ก ๋ฐ๋๋ฐ, ListAdapter๋ฅผ ๋ฐ๋๋ค. ์ฒ์์ ์ด๊ฑฐ๋ณด๊ณ "์ด? RecyclerView์ ListAdapter์ธ๊ฐ?" ํ๋๋ฐ widget์ ListAdpater์๋ค. ์ด๋ฌ๋ฉด override ํด์ผํ๋ ํจ์๊ฐ ๋ง์์ ๊ทธ๋ฅ ListAdapter๋ฅผ ์์ํ ํด๋์ค์ธ BaseAdapter๋ฅผ ์ฌ์ฉํ๊ธฐ๋ก ํ๋ค.
class CustomPopupAdapter(private val items: List<String>) : BaseAdapter() {
override fun getCount(): Int = items.size
override fun getItem(position: Int): Any = items[position]
override fun getItemId(position: Int): Long = position.toLong()
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val holder: PopUpViewHolder = if (convertView == null) {
PopUpViewHolder(
ItemPopUpBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
} else {
convertView.tag as PopUpViewHolder
}
return holder.bind(items[position], position)
}
getView๋ view๋ฅผ ๋ฐํํด์ค์ผํ๋๋ฐ, ViewHolderํจํด์ ํ์ฉํ๋ค. ๋ค๋ง getView์ฌ์ View๋ฅผ ๋ฐํํด์ผ๋๋๋ฐ, ์ด๊ฑธ ViewHolder ํด๋์ค์์ ์ฌ์ฉํ๊ณ ์ถ์์ง๋ง ๋ฐฉ๋ฒ์ ๋ฑํ ๋ ์ฌ๋ฆฌ์ง ๋ชปํด holder์ `itemView`๋ฅผ ๋ฐํํ๋ ๋ฐฉ์์ ์ ํํ๋ค.
convertView๋ก ๋ทฐ๋ฅผ ์ฌ์ฌ์ฉํ๋๋ฐ, ๋งค๋ฒ ์๋ก ๋ง๋คํ์๊ฐ ์๋๋ก null์ผ๋๋ง inflateํ๋ค. ์ด๋ฏธ ๋ทฐ๊ฐ ๋ง๋ค์ด์ก๋ค๋ฉด tag์ ๋ด๊ฒจ์๋ ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ๋ค. tag๋ ์์จ๋ณธ ์ฌ๋๋ ์์ ๊ฑฐ๋ผ ์๊ฐ๋๋๋ฐ, ์ผ์ข ์ Any ๊ฐ์ ๋๋์ด๋ค.
/**
* Returns this view's tag.
*
* @return the Object stored in this view as a tag, or {@code null} if not
* set
*
* @see #setTag(Object)
* @see #getTag(int)
*/
@ViewDebug.ExportedProperty
@InspectableProperty
public Object getTag() {
return mTag;
}
๋ชจ๋ ๊ฐ์ฒด๋ฅผ ๋ด์ ์ ์๊ณ , ๋ฐ๋ผ์ ViewHolder ๊ฐ์ฒด ์ญ์ ๋ด์ ์ ์๋ค. ๊ทธ๋ฌ๋ฉด ์ด๋ฏธ ๋ด๊ฒจ์๋ ๊ฐ์ฒด๋ฅผ ๊บผ๋ด์ฐ๋ฉด ๋์ ์ฑ๋ฅ์ด ์ฌ๋ผ๊ฐ๋ค. holder ๊ฐ์ฒด์์ ์ฌ์ฉํ ๊ฐ๋ ๋ฃ์ด์ฃผ๋ฉด ๋๋๋ค.
์ด์ ViewHolder๋ฅผ ๋ณด์.
class PopUpViewHolder(private val binding: ItemPopUpBinding) {
private val itemView: View = binding.root
init {
itemView.tag = this
}
fun bind(item: String, position: Int): View {
binding.tvTitle.text = item
when (position) {
Ticket.ISSUED.state -> {
binding.ivIcon.setImageResource(R.drawable.ic_issued_ticket)
}
Ticket.UNISSUED.state -> {
binding.ivIcon.setImageResource(R.drawable.ic_unissued_ticket)
}
}
binding.executePendingBindings()
return itemView
}
}
์ฐ์ binding.root๋ฅผ ๋ฐํํ๋ฉด ์๋๋ค. tag๋ฅผ ์ฌ์ฉํด์ ๋ทฐ๋ฅผ ์ฌํ์ฉํด์ผ๋๊ณ , ๊ทธ tag์ ๋ด๊ธธ ์์๊ฐ ViewHolder์ธ๋ฐ tag๋ฅผ ์ง์ ํ์ง์๊ณ ๊ทธ๋ฅ binding.root๋ฅผ ์ฌ์ฉํ๋ฉด tag๋ฅผ ์ฐพ์ง๋ชปํด์ ์ฑ์ด ์ฃฝ์ด๋ฒ๋ฆฐ๋ค.
๊ทธ๋์ itemView๋ฅผ binding.root๋ก ํด์, tag๋ฅผ PopUpViewHolder๋ก ์ง์ ํด์ฃผ๋ ๊ฒ์ด๋ค. executePendingBindings๋ ๋ฐ์ธ๋ฉ ๊ณ์ lazy๋ผ๊ณ ์๊ฐํ๋ฉด ๋๋ค.
์ด์ ํธ์ถ๋ถ๋ฅผ ๋ณด๊ฒ ๋ค.
private val popUpAdapter by lazy {
CustomPopupAdapter(
listOf(
getString(R.string.schedule_ticket_issued),
getString(R.string.schedule_ticket_unissued)
)
)
}
private fun initFAB() {
val listPopupWindow = ListPopupWindow(binding.root.context)
listPopupWindow.apply {
setBackgroundDrawable(
ContextCompat.getDrawable(
binding.root.context,
android.R.color.transparent
)
)
setAdapter(popUpAdapter)
anchorView = binding.fabAddTicket
width =
binding.root.context.resources.getDimensionPixelSize(R.dimen.default_popup_width)
height = ListPopupWindow.WRAP_CONTENT
isModal = true
setOnItemClickListener { _, _, position, _ ->
when (position) {
Ticket.ISSUED.state -> {
Timber.tag("fab").d("issued")
}
Ticket.UNISSUED.state -> {
Timber.tag("fab").d("unissued")
}
}
listPopupWindow.dismiss()
}
}
binding.fabAddTicket.setOnClickListener {
listPopupWindow.show()
}
}
ListPopupWindow๊ฐ ๊ธฐ๋ณธํ ๋ง๋ฅผ ๊ฐ๊ณ ์๊ธฐ ๋๋ฌธ์ ๋ฐฐ๊ฒฝ๋ถํฐ ๋ ๋ ค์คฌ๋ค. ์ด๋ํฐ๋ฅผ ์ค์ ํด์ฃผ๊ณ , ํธ์คํธ๋ก ์ฌ์ฉํ fab๋ฅผ anchor๋ก ๋ฌ์์คฌ๋ค.
width๋ ์ฌ๊ธฐ์๋ ๋ณด์ด์ง ์์ง๋ง 200dp๋ก ์ค์ ํ๊ณ , ๋ชฉ๋ก์ด ์๋ ๋ค๋ฅธ ๋ถ๋ถ์ ๋๋ฅด๋ฉด ๋ซํ๋๋ก modal ๊ฐ์ true๋ก ์ง์ ํ๋ค.
์๋๋ ๊ฒฐ๊ณผ๋ฌผ์ด๋ค..! ๋๋ฆ ์ฑ๊ณต์ ์ธ ๋์ ์ด๋ผ๊ณ ์๊ฐํ๋ค.
๋์์ด ๋๋ค๋ฉด ๋๊ธ์ด๋ ๊ณต๊ฐ ๋ฒํผ ํ ๋ฒ์ฉ ๋๋ฅด๊ณ ๊ฐ์ฃผ์ธ์!