Android Banner - ViewPager 02

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()
        }
    }

热门相关:最强狂兵