๊ฐœ๋ฐœ/Android

[Android] Kotlin Extension Function์„ ์‚ฌ์šฉํ•˜์—ฌ ๋”๋ธ” ํด๋ฆญ ๋ฐฉ์ง€ํ•˜๊ธฐ

๋„๋ฆฌ ๐ŸŸ 2021. 4. 8. 23:18

์ž‘์„ฑ์ผ: 2019.11.28


์ฝ”ํ‹€๋ฆฐ ํ™•์žฅ ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด View ๋”๋ธ” ํด๋ฆญ ๋ฐฉ์ง€๋ฅผ ์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ณดํ†ต ๋”๋ธ” ํด๋ฆญ ๋ฐฉ์ง€๋ฅผ ์œ„ํ•ด ํŠน์ • ์‹œ๊ฐ„์„ ์ •ํ•ด๋‘๊ณ  ํ•œ ๋ฒˆ ํด๋ฆญ ํ›„ ํ•ด๋‹น ์‹œ๊ฐ„ ์ด๋‚ด์˜ ํด๋ฆญ ์ด๋ฒคํŠธ๋Š” ๋ฌด์‹œํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ง์ ‘ ์ „์—ญ Util ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ RxBinding์˜ throttle์„ ์ด์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

Util ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์€ onClick()๋งˆ๋‹ค boilerplate๊ฐ€ ์ƒ๊ธฐ๊ฒŒ ๋˜๊ณ , ๊นœ๋นกํ•˜๊ณ  ์ถ”๊ฐ€๋ฅผ ํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ๋„ ์ƒ๊ธฐ๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค.

backButton.setOnClickListener { 
    if (ExampleUtils.isDoubleClick()) return@setOnClickListener
    onBackPressed() 
}

RxBinding ์‚ฌ์šฉ ๋˜ํ•œ clicks()์™€ ํ•จ๊ป˜ throttle()๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ๋Š” ๊ฒƒ์„ ๊นœ๋นกํ•  ์ˆ˜ ์žˆ๊ณ , 3rd party ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

backButton.clicks() 
    .throttleFirst(500, TimeUnit.MILLISECONDS)
    .subscribe { onBackPressed() }

 

Kotlin ํ™•์žฅ ํ•จ์ˆ˜๋กœ ๋”๋ธ” ํด๋ฆญ ๋ฐฉ์ง€ ๊ตฌํ˜„ํ•˜๊ธฐ

1. ClickListener ๊ตฌํ˜„ํ•˜๊ธฐ

View.OnClickListener๋ฅผ ๊ตฌํ˜„ํ•˜์—ฌ ์œ„์—์„œ ์„ค๋ช…ํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ ์˜ˆ์™ธ์ฒ˜๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ์ €์˜ ๊ฒฝ์šฐ RxBinding์„ ์‚ฌ์šฉํ•  ํ•„์š”๋ฅผ ๋Š๋ผ์ง€ ๋ชปํ•ด์„œ ์ง์ ‘ ์‹œ๊ฐ„ ์ธก์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.
์ฝ”ํ‹€๋ฆฐ์€ ํ•จ์ˆ˜๋„ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ƒ์„ฑํ•˜์ง€ ์•Š๊ณ  ์ƒ์„ฑ์ž๋กœ ์ง์ ‘ ํด๋ฆญ ์ฒ˜๋ฆฌ ํ•จ์ˆ˜๋ฅผ ๋ฐ›๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค.

class OnSingleClickListener(private val onSingleClick: (View) -> Unit) : View.OnClickListener { 
    companion object { 
        private const val CLICK_INTERVAL = 500 
    }
    
    private var lastClickedTime: Long = 0L 
    
    override fun onClick(v: View?) { 
        if (isSafe() && v != null) { 
            lastClickedTime = System.currentTimeMillis()
            onSingleClick(v) 
        }  
    } 
    
    private fun isSafe(): Boolean { 
        return System.currentTimeMillis() - lastClickedTime > CLICK_INTERVAL 
    } 
}

2. ํ™•์žฅ ํ•จ์ˆ˜ ์ •์˜ํ•˜๊ธฐ

๋งŒ์•ฝ ํ™•์žฅ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด, View.setOnClickListener๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋งˆ๋‹ค OnSingleClickListener๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ๋„ฃ์–ด์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค.

fun View.setOnSingleClickListener(onSingleClick: (View) -> Unit) {
    val singleClickListener = OnSingleClickListener { onSingleClick(it) } 
    setOnClickListener(singleClickListener) 
}

3. ์‚ฌ์šฉํ•  ๋•Œ๋Š” setOnClickListener์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•ด์š”.

์‹ค์ œ๋กœ View์—์„œ๋Š” ๊ธฐ์กด setOnClickListener๋ฅผ ์‚ฌ์šฉํ–ˆ๋˜ ๊ฒƒ๊ณผ ๋™์ผํ•˜๊ฒŒ ์“ธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

textView.setOnSingleClickListener { onClickItem() }

์ด๋ ‡๊ฒŒ ์“ฐ๋”๋ผ๋„ setOnSafeClickListener ๋Œ€์‹  ๊ทธ๋ƒฅ setOnClickListener๋ฅผ ์‚ฌ์šฉํ•ด๋ฒ„๋ฆฌ๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋ถ„๋ช…ํžˆ ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
ํ•˜์ง€๋งŒ refactor tool(ctrl + shift + r)์„ ์ด์šฉํ•ด setOnClickListener๋ฅผ setOnSafeClickListener๋กœ ๋ฐ”๊พธ๊ธฐ๋งŒ ํ•˜๋ฉด ๋˜๋‹ˆ ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•๋“ค๋ณด๋‹ค ์ˆ˜์ •์ด ๋น„๊ต์  ํŽธ๋ฆฌํ•ฉ๋‹ˆ๋‹ค.


์ถ”๊ฐ€๋กœ ํ™•์žฅ ํ•จ์ˆ˜๋ฅผ ๋งŽ์ด ์‚ฌ์šฉํ•˜๊ฒŒ ๋œ๋‹ค๋ฉด ์ข…๋ฅ˜๋ณ„๋กœ Extention Function์„ ๋ชจ์•„์„œ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์„ ์ถ”์ฒœํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค๋ฉด, ViewExtentions ์—๋Š” View ๊ด€๋ จ ํ™•์žฅ ํ•จ์ˆ˜๋“ค / CollectionExtensions์—๋Š” collection ๊ด€๋ จ ํ™•์žฅ ํ•จ์ˆ˜๋“ค ์ด๋ ‡๊ฒŒ์š”. ํ™•์žฅ ํ•จ์ˆ˜๊ฐ€ ๋Š˜์–ด๋‚œ๋‹ค๋ฉด, TextViewExtensions, ButtonExtensions / ListExtensions, MapExtensions ๋“ฑ ์ฒ˜๋Ÿผ ๋” ์ƒ์„ธํ•˜๊ฒŒ ์ชผ๊ฐค ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด์™ธ์—๋„ ํ™•์žฅ ํ•จ์ˆ˜๋ฅผ ์ž˜ ํ™œ์šฉํ•œ๋‹ค๋ฉด ์ด๋ฆ„์ฒ˜๋Ÿผ ํ™•์žฅ์„ฑ์žˆ๋Š” ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ด ๊ฐ€๋Šฅํ•  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค!!