Android Banner

2023-04-06 06:01:05

Android Banner - ViewPager 02

現在來給viewpager實現的banenr加上自動輪播

自動輪播的原理,使用handler的延遲訊息來實現。

自動輪播實現如下內容

  1. 開始輪播&停止輪播

  2. 可設定輪播時長、輪播方向

  3. 通過自定義屬性來設定輪播時長,方向

  4. 感知生命週期,可見時開始輪播,不可見時停止輪播

  5. 感知手指觸控,觸控按下時停止輪播,擡起重新計時

開始&停止輪播

banner對外提供介面,開始輪播

fun startLoop(){
}
fun stopLoop(){
}

定義handler實現輪播

    // 建立handler
    fun startLoop() {
        if (loopHandler == null) {
            loopHandler = Handler(Looper.getMainLooper()) { message ->
                return@Handler when (message.what) {
                    LOOP_NEXT -> {
                        // 定義訊息處理
                        loopNext()
                        true
                    }
                    else -> false
                }
            }
        }
        // 移除正在輪播的訊息
        loopHandler?.removeMessages(LOOP_NEXT)
        // 傳送延遲輪播的訊息
        loopHandler?.sendEmptyMessageDelayed(LOOP_NEXT, mLoopDuration)
    }

    private fun loopNext() {
        val count = adapter?.count ?: 0
        // 當pager數量為0或者1時,不用輪播
        if (count in 0..1) return
        val curr = when (currentItem) {
            in 0..count - 2 -> {
                currentItem + 1
            }
            count - 1 -> 0
            else -> 0
        }
        setCurrentItem(curr, true)
        loopHandler?.sendEmptyMessageDelayed(LOOP_NEXT, mLoopDuration)
    }

可設定輪播時長、輪播方向

定義介面

    /**
     * 設定輪播時長,有效資料必須大於0,否則使用預設資料5S
     * @param duration Long
     */
    fun setLoopDuration(duration: Long) {
        if (duration < 0) {
            // 小於0的資料認為是非法資料,使用預設設定
            return
        }
        this.mLoopDuration = duration
    }

    /**
     * 設定輪播方向,預設[LoopOrientation.LTR]
     * @param orientation Int
     */
    fun setLoopOrientation(@LoopOrientation orientation: Int) {
        this.mLoopOrientation = orientation
    }

輪播處理引數

    private fun loopNext() {
        val count = adapter?.count ?: 0
        // 當pager數量為0或者1時,不用輪播
        if (count in 0..1) return
        val curr = when (mLoopOrientation) {
            LoopOrientation.RTL -> {
                when (currentItem) {
                    in 1..count - 1 -> {
                        currentItem - 1
                    }
                    else -> count - 1 // 0
                }
            }
            else -> {
                when (currentItem) {
                    in 0..count - 2 -> {
                        currentItem + 1
                    }
                    else -> 0 // count - 1
                }
            }
        }
        setCurrentItem(curr, true)
        mLoopHandler?.sendEmptyMessageDelayed(LOOP_NEXT, mLoopDuration)
    }

通過自定義屬性來設定輪播時長,方向

<resources>
    <declare-styleable name="VPBanner">
        <attr name="vp_loop_duration" format="integer" />
        <attr name="vp_loop_orientation" format="enum" >
            <enum name="ltr" value="1" />
            <enum name="rtl" value="0" />
        </attr>
        <attr name="vp_auto_loop" format="boolean" />
    </declare-styleable>
</resources>

讀取屬性

    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
        // 讀取自定義的屬性
        val typedArray = context.obtainStyledAttributes(attrs, R.styleable.VPBanner)
        this.mLoopDuration = typedArray.getInt(
            R.styleable.VPBanner_vp_loop_duration,
            DEFAULT_LOOP_DURATION
        ).toLong()
        this.mAutoLoop = typedArray.getBoolean(R.styleable.VPBanner_vp_auto_loop, false)
        this.mLoopOrientation =
            typedArray.getInt(R.styleable.VPBanner_vp_loop_orientation, LoopOrientation.LTR)

        Log.d("VPBanner","ld:${this.mLoopDuration},al:$mAutoLoop,lo:$mLoopOrientation")

        typedArray?.recycle()
    }

感知生命週期,可見時開始輪播,不可見時停止輪播

實現生命週期感知

class VPBanner : ViewPager, DefaultLifecycleObserver {
    override fun onResume(owner: LifecycleOwner) {
        Log.d(TAG, "onResume")
        if (this.mAutoLoop) {
            startLoop()
        }
    }

    override fun onPause(owner: LifecycleOwner) {
        Log.d(TAG, "onResume")
        stopLoop()
    }
}

感知手指觸控,觸控按下時停止輪播,擡起重新計時

重寫onTouchEvent方法

   override fun onTouchEvent(ev: MotionEvent?): Boolean {
        when (ev?.action) {
            MotionEvent.ACTION_DOWN -> stopLoop()
            MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
                prepareLoop()
            }
        }
        return super.onTouchEvent(ev)
    }

    private fun prepareLoop() {
        if (this.mAutoLoop && this.mResumed) {
            startLoop()
        }
    }