中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Android怎么實現循環輪播跑馬燈效果

發布時間:2023-05-04 15:33:18 來源:億速云 閱讀:99 作者:iii 欄目:開發技術

這篇“Android怎么實現循環輪播跑馬燈效果”文章的知識點大部分人都不太理解,所以小編給大家總結了以下內容,內容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“Android怎么實現循環輪播跑馬燈效果”文章吧。

支持暫停,恢復,view自定義和池化回收復用。使用上,只需要引入xml,并綁定factory即可,內部會在attach時自動開始

 <MarqueeAnimalView
        android:id="@+id/marqueeView"
        android:layout_width="200dp"
        android:layout_height="30dp"
        android:background="@color/color_yellow" />
val list = mutableListOf("我是跑馬燈1", "我不是跑馬燈", "你猜我是不是跑馬燈")
var position = 0
view.marqueeView.setFactory(object : PoolViewFactory {
    override fun makeView(layoutInflater: LayoutInflater, parent: ViewGroup): View {
        val view = TextView(this@ViewActivity)
        view.setPadding(0, 0, 20.dp(), 0)
        view.textSize = 12f
        view.setTextColor(ResourceUtil.getColor(R.color.white))
        return view
    }
    override fun setAnimator(objectAnimator: ObjectAnimator, width: Int, parentWidth: Int) {
        objectAnimator.duration = (parentWidth + width) * 5L
    }
    override fun setView(view: View): Boolean {
        (view as? TextView)?.text = list[position++ % list.size]
        return true
    }
})

池化思路

參考Message的思路,對view進行回收復用,避免內存持續增長,增大GC壓力

private fun obtain(): View? {
    synchronized(sPoolSync) {
        if (!isAttachedToWindow) {
            return null
        }
        if (queue.isNotEmpty()) {
            return queue.poll()
        }
    }
    return factory?.makeView(layoutInflater, this@MarqueeAnimalView)?.apply {
        addView(this, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
    }
}
private fun recycle(view: View) {
    synchronized(sPoolSync) {
        if (queue.size < MAX_POOL_SIZE) {
            queue.offer(view)
        }
    }
}

創造工廠

這里的思路源于ViewSwitchFactory

interface PoolViewFactory {
    fun makeView(layoutInflater: LayoutInflater, parent: ViewGroup): View
    fun setAnimator(objectAnimator: ObjectAnimator, width: Int, parentWidth: Int)
    /**
     * 返回值,代表view是否需要重新測量
     */
    fun setView(view: View): Boolean
}

輪詢切換

這里根據對動畫進行初始化,并設置合適的監聽。此時需要獲取當view和parent的width,以用于標定始末位置,需要注意x軸的正負方向。animators用于存儲開始的動畫,這也是設計時存在的遺留問題,因為主動取消所有動畫,但view->animator是單向綁定關系,所以需要保存發生的動畫

private val animators = hashMapOf<String, ObjectAnimator>()
private fun next(view: View?) {
    view ?: return
    if (factory?.setView(view) == true) {
        view.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED))
    }
    val width = view.measuredWidth
    val parentWidth = measuredWidth
    val targetValue = parentWidth - width
    val animator = ObjectAnimator.ofFloat(view, PROPERTY_NAME, parentWidth.toFloat(), -width.toFloat()).apply {
        // null即為默認線性插值器
        interpolator = null
        addUpdateListener(
            RecyclerAnimatorUpdateListener(targetValue) {
                next(obtain())
                removeUpdateListener(it)
            }
        )
        addListener(this@MarqueeAnimalView)
        factory?.setAnimator(this, width, parentWidth)
    }
    animators["${view.hashCode()}-${animator.hashCode()}"] = animator
    animator.start()
}

動畫監聽

當動畫結束時,需要對view進行回收,并對動畫移除。取消動畫時,需要將view強制歸位

同時,為了方便使用,OnAttachStateChangeListener使得整體動畫更加平滑,也避免了view不可見時,動畫仍然在持續執行浪費資源。當然如fragment不可見時的監聽需要完善

override fun onAnimationEnd(animation: Animator?) {
    (animation as? ObjectAnimator)?.let { animator ->
        (animator.target as? View)?.let { view ->
            animators.remove("${view.hashCode()}-${animator.hashCode()}")
            recycle(view)
        }
        // target釋放
        animator.target = null
    }
}
override fun onAnimationCancel(animation: Animator?) {
    (animation as? ObjectAnimator)?.let { animator ->
        (animator.target as? View)?.let { view ->
            view.translationX = measuredWidth.toFloat()
        }
    }
}
override fun onViewAttachedToWindow(v: View?) {
    if (animators.isNotEmpty()) {
        resume()
    } else {
        start()
    }
}
override fun onViewDetachedFromWindow(v: View?) {
    pause()
}

對外能力

fun start() {
    if (measuredWidth == 0) {
        this.post {
            // 如果測量還未完成,那就等待post后發起
            next(obtain())
        }
        return
    }
    next(obtain())
}
fun stop() {
    val it = animators.values.iterator()
    while (it.hasNext()) {
        val i = it.next()
        it.remove()
        i.cancel()
    }
}
fun pause() {
    for (i in animators.values) {
        i.pause()
    }
}
fun resume() {
    for (i in animators.values) {
        i.resume()
    }
}

以上就是關于“Android怎么實現循環輪播跑馬燈效果”這篇文章的內容,相信大家都有了一定的了解,希望小編分享的內容對大家有幫助,若想了解更多相關的知識內容,請關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

安义县| 江孜县| 白城市| 石城县| 灵武市| 千阳县| 汽车| 松溪县| 沧源| 土默特左旗| 周口市| 石门县| 罗城| 康保县| 天等县| 建宁县| 平阴县| 泸西县| 南阳市| 遂宁市| 财经| 博湖县| 清徐县| 湛江市| 资中县| 唐山市| 房山区| 航空| 宜春市| 枝江市| 农安县| 镇原县| 略阳县| 固阳县| 垣曲县| 白玉县| 峡江县| 延川县| 鹰潭市| 金平| 绥芬河市|