您好,登錄后才能下訂單哦!
這篇“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怎么實現循環輪播跑馬燈效果”這篇文章的內容,相信大家都有了一定的了解,希望小編分享的內容對大家有幫助,若想了解更多相關的知識內容,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。