Android實(shí)現(xiàn)橫向無限循環(huán)滾動(dòng)的單行彈幕效果
本期將帶領(lǐng)大家實(shí)現(xiàn)一個(gè)這樣的效果,支持無限循環(huán)的單行彈幕效果。
實(shí)現(xiàn)思路分析
要實(shí)現(xiàn)上面的效果,我們先拆分下實(shí)現(xiàn)要素:
1、彈幕布局是從屏幕的右側(cè)向左側(cè)滾動(dòng),單個(gè)彈幕之間的間距是固定的(設(shè)計(jì)要求)
2、彈幕要支持無限滾動(dòng),出于性能要求,如果不在屏幕內(nèi)的,應(yīng)該移除,不能無限追加到內(nèi)存里面。
拆分完需求要素之后,針對上面的需求要素,做一下思路解答:
1、對于滾動(dòng)和超出屏幕后移除,可以使用動(dòng)畫來實(shí)現(xiàn),動(dòng)畫從屏幕右邊開始移動(dòng)到屏幕左邊,監(jiān)聽如果已經(jīng)動(dòng)畫結(jié)束,則remove掉布局。
2、無限循環(huán)效果,可以使用兩個(gè)鏈表實(shí)現(xiàn),一個(gè)保存加入到屏幕的彈幕數(shù)據(jù)(A),另一個(gè)保存未添加到屏幕的彈幕數(shù)據(jù)(B)。讓進(jìn)入屏幕前將布局從B中poll出來,添加到A中。反之,屏幕移除的時(shí)候從A中poll出來,添加到B中。
代碼實(shí)現(xiàn)
首先創(chuàng)建出來一個(gè)彈幕數(shù)據(jù)對象類
data class Danmu( //頭像 var headerUrl: String? = null, //昵稱 var userName: String? = null, //信息 var info: String? = null, )
要被使用的彈幕itemView
class DanmuItemView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : LinearLayoutCompat(context, attrs, defStyleAttr) { private var danmuItemView: TextView? = null var danmu: Danmu? = null init { LayoutInflater.from(context).inflate(R.layout.danmu_item, this, true) danmuItemView = findViewById(R.id.tvDanmuItem) } fun setDanmuEntity(danmu: Danmu) { this.danmu = danmu danmuItemView?.text = "我是一個(gè)彈幕~~~~~哈哈哈哈哈哈" + danmu.userName measure(0, 0) } }
接下來就是彈幕布局的容器類,用來控制動(dòng)畫和數(shù)據(jù)交替。注意代碼中有很有用的注釋
class DanmuView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : FrameLayout(context, attrs, defStyleAttr) { private var mWidth = 0 //為展示在屏幕上的彈幕數(shù)據(jù) private val mDanMuList = LinkedList<Danmu>() //屏幕中展示的彈幕數(shù)據(jù) private val mVisibleDanMuList = LinkedList<Danmu>() //判斷是否在運(yùn)行 private val mIsRunning = AtomicBoolean(false) override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { super.onMeasure(widthMeasureSpec, heightMeasureSpec) mWidth = measuredWidth } /** * 添加彈幕數(shù)據(jù) */ fun enqueueDanMuList(danMuList: ArrayList<Danmu>) { danMuList.forEach { if (this.mDanMuList.contains(it).not()) { this.mDanMuList.add(it) } } if (mWidth == 0) { viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener { override fun onGlobalLayout() { mWidth = measuredWidth viewTreeObserver.removeOnGlobalLayoutListener(this) if (mIsRunning.get().not()) { mDanMuList.poll()?.apply { //這里是用來處理布局的交替工作,前面分析有說明 mVisibleDanMuList.add(this) createDanMuItemView(this) } } } }) } else { if (mIsRunning.get().not()) { mDanMuList.poll()?.apply { //這里是用來處理布局的交替工作,前面分析有說明 mVisibleDanMuList.add(this) createDanMuItemView(this) } } } } private fun startDanMuAnimate(danMuItemView: DanmuItemView) { var isInit = false danMuItemView.animate() //注意這邊設(shè)置的便宜量是容器布局的寬度+彈幕item布局的寬度,這樣就確保滾動(dòng)值剛好是從屏幕右側(cè)外到屏幕左側(cè)外 .translationXBy((-(mWidth + danMuItemView.measuredWidth)).toFloat()) .setDuration(6000) .setInterpolator(LinearInterpolator()) .setUpdateListener { val danMuTranslateX = (mWidth + danMuItemView.measuredWidth) * (it.animatedValue as Float) //這里是關(guān)鍵,用來確保每個(gè)item布局的間距一致,判斷如果滾動(dòng)進(jìn)入屏幕的距離剛好是自身+20dp,也就是剛好空出來了20dp之后,緊接著下一個(gè)彈幕布局開始添加并動(dòng)起來。 if (danMuTranslateX >= danMuItemView.measuredWidth + Utils.convertDpToPixel(20F) && isInit.not()) { isInit = true mDanMuList.poll()?.apply { mVisibleDanMuList.add(this) createDanMuItemView(this) } } } .setListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator?) { if (mIsRunning.get().not()) { mIsRunning.set(true) } //很重要,在動(dòng)畫結(jié)束,也就是布局從屏幕移除之后,切記從布局中移除掉, //并且進(jìn)行一波數(shù)據(jù)交替,方便實(shí)現(xiàn)無線循環(huán) danMuItemView.danmu?.let { mVisibleDanMuList.remove(it) mDanMuList.add(it) } removeView(danMuItemView) } }).start() } private fun createDanMuItemView(danMu: Danmu) { val danMuItemView = DanmuItemView(context).apply { setDanmuEntity(danMu) } //這里將布局添加之后,默認(rèn)便宜到屏幕右側(cè)出屏幕,造成布局總是從右👉移動(dòng)到👈左的效果。 val param = LayoutParams(danMuItemView.measuredWidth, danMuItemView.measuredHeight) param.gravity = Gravity.CENTER_VERTICAL param.leftMargin = mWidth startDanMuAnimate(danMuItemView) addView(danMuItemView, param) } }
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- android開發(fā)之橫向滾動(dòng)/豎向滾動(dòng)的ListView(固定列頭)
- Android程序開發(fā)之ListView實(shí)現(xiàn)橫向滾動(dòng)(帶表頭與固定列)
- 詳解Android使GridView橫向水平滾動(dòng)的實(shí)現(xiàn)方式
- Android使用GridView實(shí)現(xiàn)橫向滾動(dòng)效果
- Android開發(fā)實(shí)現(xiàn)橫向列表GridView橫向滾動(dòng)的方法【附源碼下載】
- Android GridView實(shí)現(xiàn)橫向列表水平滾動(dòng)
- Android自定義ViewGroup實(shí)現(xiàn)可滾動(dòng)的橫向布局(2)
- Android實(shí)現(xiàn)自定義的彈幕效果
- 實(shí)例解析如何在Android應(yīng)用中實(shí)現(xiàn)彈幕動(dòng)畫效果
- Android 實(shí)現(xiàn)仿網(wǎng)絡(luò)直播彈幕功能詳解及實(shí)例
相關(guān)文章
Android開發(fā)實(shí)現(xiàn)判斷通知欄是否打開及前往設(shè)置頁面的方法
這篇文章主要介紹了Android開發(fā)實(shí)現(xiàn)判斷通知欄是否打開及前往設(shè)置頁面的方法,涉及Android通知欄的打開、判斷、設(shè)置等相關(guān)操作技巧,需要的朋友可以參考下2018-01-01Android自定義textview實(shí)現(xiàn)豎直滾動(dòng)跑馬燈效果
這篇文章主要為大家詳細(xì)介紹了Android自定義textview實(shí)現(xiàn)豎直滾動(dòng)跑馬燈效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06Adapter實(shí)現(xiàn)ListView帶多選框等狀態(tài)的自定義控件的注意事項(xiàng)
Android本身為ListView提供了幾個(gè)方便的Adapter,比如ArrayAdapter、SimpleCurrentAdapter等等,接下來介紹自定義Adapter實(shí)現(xiàn)ListView帶多選框等狀態(tài)控件的注意事項(xiàng),感興趣的朋友可以詳細(xì)了解下,或許對你有所幫助2013-01-01android studio3.0.1無法啟動(dòng)Gradle守護(hù)進(jìn)程的解決方法
這篇文章主要為大家詳細(xì)介紹了android studio3.0.1無法啟動(dòng)Gradle守護(hù)進(jìn)程的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-08-08Android 5秒學(xué)會(huì)使用手勢解鎖功能
本文講述的是一個(gè)手勢解鎖的庫,可以定制顯示隱藏宮格點(diǎn)、路徑、并且?guī)в行【艑m格顯示圖,和震動(dòng)!讓你學(xué)會(huì)使用這個(gè)簡單,高效的庫,好了,具體內(nèi)容詳情大家通過本文學(xué)習(xí)吧2017-12-12Android實(shí)現(xiàn)語音數(shù)據(jù)實(shí)時(shí)采集、播放
這篇文章主要介紹了android實(shí)現(xiàn)語音數(shù)據(jù)實(shí)時(shí)采集、播放的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12Android項(xiàng)目開發(fā) 教你實(shí)現(xiàn)Periscope點(diǎn)贊效果
這篇文章主要為大家分享了Android項(xiàng)目開發(fā),一步一步教你實(shí)現(xiàn)Periscope點(diǎn)贊效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2015-12-12淺談Android Studio 3.0 工具新特性的使用 Android Profiler 、Device File
這篇文章主要介紹了淺談Android Studio 3.0 工具新特性的使用 Android Profiler 、Device File Explorer的相關(guān)資料,需要的朋友可以參考下2017-11-11Android實(shí)現(xiàn)3D推拉門式滑動(dòng)菜單源碼解析
這篇文章主要為大家詳細(xì)解析了Android實(shí)現(xiàn)3D推拉門式滑動(dòng)菜單源碼以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。2017-11-11