Android實(shí)現(xiàn)無(wú)限循環(huán)滾動(dòng)彈幕的代碼示例
,文中通過(guò)代碼示例講解的非常詳細(xì),對(duì)大家實(shí)現(xiàn)循環(huán)彈幕有一定的幫助,需要的朋友可以參考下
先上效果圖

要實(shí)現(xiàn)上面的效果,我們先拆分下實(shí)現(xiàn)要素:
- 1、彈幕布局是從屏幕的右側(cè)向左側(cè)滾動(dòng),單個(gè)彈幕之間的間距是固定的,并且滾動(dòng)速度需要?jiǎng)蛩?/li>
- 2、彈幕要支持無(wú)限滾動(dòng),出于性能要求,如果不在屏幕內(nèi)的,應(yīng)該移除,不能無(wú)限追加到內(nèi)存里面。
思路
1、對(duì)于滾動(dòng)和超出屏幕后移除,可以使用動(dòng)畫(huà)來(lái)實(shí)現(xiàn),動(dòng)畫(huà)從屏幕右邊開(kāi)始移動(dòng)到屏幕左邊,監(jiān)聽(tīng)如果已經(jīng)動(dòng)畫(huà)結(jié)束,則remove掉布局。
2、無(wú)限循環(huán)效果,可以使用兩個(gè)鏈表實(shí)現(xiàn),一個(gè)保存加入到屏幕的彈幕數(shù)據(jù)(A),另一個(gè)保存未添加到屏幕的彈幕數(shù)據(jù)(B)。讓進(jìn)入屏幕前將布局從B中poll出來(lái),添加到A中。反之,屏幕移除的時(shí)候從A中poll出來(lái),添加到B中。
實(shí)現(xiàn)
1.無(wú)線滾動(dòng) 需要兩個(gè)鏈表 mDanMuList初始包含所有彈幕數(shù)據(jù) mVisibleDanMuList每次取出mDanMuList第一條數(shù)據(jù) 填充到彈幕 直到彈幕動(dòng)畫(huà)結(jié)束后,再重新添加到mDanMuList里
//為展示在屏幕上的彈幕數(shù)據(jù) private val mDanMuList = LinkedList<BulletChatDetail>()
//屏幕中展示的彈幕數(shù)據(jù) private val mVisibleDanMuList = LinkedList<BulletChatDetail>()
fun enqueueDanMuList(danMuList: List<BulletChatDetail>?) {
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 {
//這里是用來(lái)處理布局的交替工作,前面分析有說(shuō)明 mVisibleDanMuList.add(this)
createDanMuItemView(this)
}
}
}
})
} else {
if (mIsRunning.get().not()) {
mDanMuList.poll()?.apply {
//這里是用來(lái)處理布局的交替工作,前面分析有說(shuō)明
mVisibleDanMuList.add(this)
createDanMuItemView(this)
}
}
}
}
2.添加動(dòng)畫(huà) 每個(gè)彈幕默認(rèn)都是頭像加描述組成 所以自定義一個(gè)DanMuItemView 內(nèi)部就一個(gè)填充數(shù)據(jù)的方法
class DanMuItemView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : LinearLayoutCompat(context, attrs, defStyleAttr) {
var danMu: BulletChatDetail? = null
private val mBinding: DanmuItemBinding
init {
mBinding = DanmuItemBinding.inflate(
LayoutInflater.from(context), this, true )
}
fun setDanMuEntity(danmu: BulletChatDetail?) {
this.danMu = danmu
mBinding.tvDanMuItem.text = danmu?.bulletChat mBinding.ivDanMuItem.setCircleImageUrl(danmu?.userLogo?.toCompleteImageUrl(3))
measure(0, 0)
}
}
3.監(jiān)聽(tīng)viewtree樹(shù),當(dāng)布局初始化完成后,創(chuàng)建彈幕view,并開(kāi)啟動(dòng)畫(huà) DanMuItemView的位置 默認(rèn)在-danMuItemView.measuredWidth 負(fù)彈幕view的寬度
private fun createDanMuItemView(DanMu: BulletChatDetail) {
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)
}
4 .開(kāi)啟動(dòng)畫(huà) view默認(rèn)有動(dòng)畫(huà)的方法 我們已知view移動(dòng)的距離為 屏幕寬度+彈幕view 需要?jiǎng)蛩?20.0f // 你想要的目標(biāo)速度,單位是像素每秒 所以時(shí)間得動(dòng)態(tài)計(jì)算 并且我們知道每個(gè)彈幕間ui的間距 所以監(jiān)聽(tīng)彈幕view勻速動(dòng)畫(huà)所在的位置 當(dāng)滾動(dòng)進(jìn)入屏幕的距離剛好是自身+間距的20dp時(shí),就開(kāi)啟第二個(gè)彈幕item的動(dòng)畫(huà) 這樣就實(shí)現(xiàn)了無(wú)限彈幕
val targetSpeedPxPerSecond = 120.0f // 你想要的目標(biāo)速度,單位是像素每秒
val totalTranslateX = ((mWidth + danMuItemView.measuredWidth)).toFloat()
val duration = (totalTranslateX / targetSpeedPxPerSecond * 1000).toLong() // 轉(zhuǎn)換為毫秒
var isInit = false
danMuItemView.animate()
//注意這邊設(shè)置的便宜量是容器布局的寬度+彈幕item布局的寬度,這樣就確保滾動(dòng)值剛好是從屏幕右側(cè)外到屏幕左側(cè)外 .translationXBy(-totalTranslateX)
.setDuration(duration)
.setInterpolator(LinearInterpolator())
.setUpdateListener {
val danMuTranslateX =(mWidth + danMuItemView.measuredWidth) * (it.animatedValue as Float)
//這里是關(guān)鍵,用來(lái)確保每個(gè)item布局的間距一致,判斷如果滾動(dòng)進(jìn)入屏幕的距離剛好是自身+20dp,也就是剛好空出來(lái)了20dp之后,緊接著下一個(gè)彈幕布局開(kāi)始添加并動(dòng)起來(lái)。
if (danMuTranslateX >= danMuItemView.measuredWidth +
DensityUtils.dp2px(15f) && isInit.not()) {
isInit = true
if (mDanMuList.size == 0) {
mHasStopRun = true
}else{
mHasStopRun = false
mDanMuList.poll()?.apply {
mVisibleDanMuList.add(this)
createDanMuItemView(this)
}
}
}
}
.setListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
super.onAnimationEnd(animation)
if (mIsRunning.get().not()) {
mIsRunning.set(true)
}
//很重要,在動(dòng)畫(huà)結(jié)束,也就是布局從屏幕移除之后,切記從布局中移除掉,
//并且進(jìn)行一波數(shù)據(jù)交替,方便實(shí)現(xiàn)無(wú)線循環(huán)
danMuItemView.danMu?.let {
mVisibleDanMuList.remove(it)
mDanMuList.add(it)
}
YzLog.e("mHasStopRun->>>>${mHasStopRun}")
if (mHasStopRun) {
createDanMu()
}else{
removeView(danMuItemView)
}
}
}).start()
當(dāng)數(shù)據(jù)只有一兩個(gè)的時(shí)候,需要添加一個(gè)變量來(lái)控制 比如如果彈幕數(shù)據(jù)源只有兩條數(shù)據(jù),當(dāng)兩條彈幕更好在同一屏幕出現(xiàn)此時(shí)彈幕數(shù)據(jù)源為空,可見(jiàn)數(shù)據(jù)源為兩條,當(dāng)?shù)诙l彈幕滾動(dòng)到符合添加第三條數(shù)據(jù)的位置時(shí),此時(shí)數(shù)據(jù)源已經(jīng)沒(méi)有數(shù)據(jù)了,需要將參數(shù)置為true,等到第二條數(shù)據(jù)動(dòng)畫(huà)結(jié)束時(shí),重新走添加彈幕邏輯。具體事項(xiàng)在上面代碼已經(jīng)展示
以上就是Android實(shí)現(xiàn)無(wú)限循環(huán)滾動(dòng)彈幕的代碼示例的詳細(xì)內(nèi)容,更多關(guān)于Android無(wú)限循環(huán)滾動(dòng)彈幕的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android簡(jiǎn)單實(shí)現(xiàn)文件下載
這篇文章主要為大家詳細(xì)介紹了Android簡(jiǎn)單實(shí)現(xiàn)文件下載,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09
Android使用fastjson庫(kù)解析json字符串實(shí)戰(zhàn)
fastjson是一個(gè)Java語(yǔ)言編寫(xiě)的高性能功能完善的JSON庫(kù),它采用一種“假定有序快速匹配”的算法,把JSON?Parse的性能提升到極致,是目前Java語(yǔ)言中最快的JSON庫(kù),Fastjson接口簡(jiǎn)單易用,已經(jīng)被廣泛使用在緩存序列化、協(xié)議交互、Web輸出、Android客戶端等多種應(yīng)用場(chǎng)景2023-11-11
Android Studio三方引用報(bào)錯(cuò)但是項(xiàng)目可以運(yùn)行的解決方案
今天小編就為大家分享一篇關(guān)于Android Studio三方引用報(bào)錯(cuò)但是項(xiàng)目可以運(yùn)行的解決方案,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-03-03
Android仿騰訊QQ實(shí)現(xiàn)滑動(dòng)刪除 附源碼下載
仿騰訊QQ滑動(dòng)刪除操作,這篇文章主要為大家詳細(xì)介紹了ListView滑動(dòng)刪除的具體操作方法,感興趣的小伙伴們可以參考一下2016-07-07
RxJava實(shí)戰(zhàn)之訂閱流基本原理示例解析
這篇文章主要為大家介紹了RxJava實(shí)戰(zhàn)之訂閱流基本原理示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12
Flutter 實(shí)現(xiàn)酷炫的3D效果示例代碼
這篇文章主要介紹了Flutter 實(shí)現(xiàn)酷炫的3D效果,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-07-07
Android中使用Canvas繪制南丁格爾玫瑰圖(Nightingale rose diagram)
這篇文章主要介紹了Android中使用Canvas繪制南丁格爾玫瑰圖(Nightingale rose diagram),本文直接給出實(shí)現(xiàn)代碼和運(yùn)行效果圖,需要的朋友可以參考下2015-03-03
Android使用AlertDialog實(shí)現(xiàn)彈出菜單的詳細(xì)過(guò)程
在Android經(jīng)常會(huì)用到AlertDialog,下面這篇文章主要給大家介紹了關(guān)于Android使用AlertDialog實(shí)現(xiàn)彈出菜單的詳細(xì)過(guò)程,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-04-04

