Android實現(xiàn)橫向無限循環(huán)滾動的單行彈幕效果
本期將帶領(lǐng)大家實現(xiàn)一個這樣的效果,支持無限循環(huán)的單行彈幕效果。

實現(xiàn)思路分析
要實現(xiàn)上面的效果,我們先拆分下實現(xiàn)要素:
1、彈幕布局是從屏幕的右側(cè)向左側(cè)滾動,單個彈幕之間的間距是固定的(設(shè)計要求)
2、彈幕要支持無限滾動,出于性能要求,如果不在屏幕內(nèi)的,應(yīng)該移除,不能無限追加到內(nèi)存里面。
拆分完需求要素之后,針對上面的需求要素,做一下思路解答:
1、對于滾動和超出屏幕后移除,可以使用動畫來實現(xiàn),動畫從屏幕右邊開始移動到屏幕左邊,監(jiān)聽如果已經(jīng)動畫結(jié)束,則remove掉布局。
2、無限循環(huán)效果,可以使用兩個鏈表實現(xiàn),一個保存加入到屏幕的彈幕數(shù)據(jù)(A),另一個保存未添加到屏幕的彈幕數(shù)據(jù)(B)。讓進入屏幕前將布局從B中poll出來,添加到A中。反之,屏幕移除的時候從A中poll出來,添加到B中。
代碼實現(xiàn)
首先創(chuàng)建出來一個彈幕數(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 = "我是一個彈幕~~~~~哈哈哈哈哈哈" + danmu.userName
measure(0, 0)
}
}
接下來就是彈幕布局的容器類,用來控制動畫和數(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>()
//判斷是否在運行
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布局的寬度,這樣就確保滾動值剛好是從屏幕右側(cè)外到屏幕左側(cè)外
.translationXBy((-(mWidth + danMuItemView.measuredWidth)).toFloat())
.setDuration(6000)
.setInterpolator(LinearInterpolator())
.setUpdateListener {
val danMuTranslateX =
(mWidth + danMuItemView.measuredWidth) * (it.animatedValue as Float)
//這里是關(guān)鍵,用來確保每個item布局的間距一致,判斷如果滾動進入屏幕的距離剛好是自身+20dp,也就是剛好空出來了20dp之后,緊接著下一個彈幕布局開始添加并動起來。
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)
}
//很重要,在動畫結(jié)束,也就是布局從屏幕移除之后,切記從布局中移除掉,
//并且進行一波數(shù)據(jù)交替,方便實現(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è)出屏幕,造成布局總是從右👉移動到👈左的效果。
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ā)之橫向滾動/豎向滾動的ListView(固定列頭)
- Android程序開發(fā)之ListView實現(xiàn)橫向滾動(帶表頭與固定列)
- 詳解Android使GridView橫向水平滾動的實現(xiàn)方式
- Android使用GridView實現(xiàn)橫向滾動效果
- Android開發(fā)實現(xiàn)橫向列表GridView橫向滾動的方法【附源碼下載】
- Android GridView實現(xiàn)橫向列表水平滾動
- Android自定義ViewGroup實現(xiàn)可滾動的橫向布局(2)
- Android實現(xiàn)自定義的彈幕效果
- 實例解析如何在Android應(yīng)用中實現(xiàn)彈幕動畫效果
- Android 實現(xiàn)仿網(wǎng)絡(luò)直播彈幕功能詳解及實例
相關(guān)文章
Android開發(fā)實現(xiàn)判斷通知欄是否打開及前往設(shè)置頁面的方法
這篇文章主要介紹了Android開發(fā)實現(xiàn)判斷通知欄是否打開及前往設(shè)置頁面的方法,涉及Android通知欄的打開、判斷、設(shè)置等相關(guān)操作技巧,需要的朋友可以參考下2018-01-01
Android自定義textview實現(xiàn)豎直滾動跑馬燈效果
這篇文章主要為大家詳細(xì)介紹了Android自定義textview實現(xiàn)豎直滾動跑馬燈效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-06-06
Adapter實現(xiàn)ListView帶多選框等狀態(tài)的自定義控件的注意事項
Android本身為ListView提供了幾個方便的Adapter,比如ArrayAdapter、SimpleCurrentAdapter等等,接下來介紹自定義Adapter實現(xiàn)ListView帶多選框等狀態(tài)控件的注意事項,感興趣的朋友可以詳細(xì)了解下,或許對你有所幫助2013-01-01
android studio3.0.1無法啟動Gradle守護進程的解決方法
這篇文章主要為大家詳細(xì)介紹了android studio3.0.1無法啟動Gradle守護進程的解決方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-08-08
Android實現(xiàn)語音數(shù)據(jù)實時采集、播放
這篇文章主要介紹了android實現(xiàn)語音數(shù)據(jù)實時采集、播放的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-12-12
Android項目開發(fā) 教你實現(xiàn)Periscope點贊效果
這篇文章主要為大家分享了Android項目開發(fā),一步一步教你實現(xiàn)Periscope點贊效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2015-12-12
淺談Android Studio 3.0 工具新特性的使用 Android Profiler 、Device File
這篇文章主要介紹了淺談Android Studio 3.0 工具新特性的使用 Android Profiler 、Device File Explorer的相關(guān)資料,需要的朋友可以參考下2017-11-11

