当前位置: 代码网 > it编程>App开发>Android > Android实现无限循环滚动弹幕的代码示例

Android实现无限循环滚动弹幕的代码示例

2024年09月07日 Android 我要评论
先上效果图要实现上面的效果,我们先拆分下实现要素:1、弹幕布局是从屏幕的右侧向左侧滚动,单个弹幕之间的间距是固定的,并且滚动速度需要匀速2、弹幕要支持无限滚动,出于性能要求,如果不在屏幕内的,应该移除

先上效果图

要实现上面的效果,我们先拆分下实现要素:

  • 1、弹幕布局是从屏幕的右侧向左侧滚动,单个弹幕之间的间距是固定的,并且滚动速度需要匀速
  • 2、弹幕要支持无限滚动,出于性能要求,如果不在屏幕内的,应该移除,不能无限追加到内存里面。

思路

  • 1、对于滚动和超出屏幕后移除,可以使用动画来实现,动画从屏幕右边开始移动到屏幕左边,监听如果已经动画结束,则remove掉布局。

  • 2、无限循环效果,可以使用两个链表实现,一个保存加入到屏幕的弹幕数据(a),另一个保存未添加到屏幕的弹幕数据(b)。让进入屏幕前将布局从b中poll出来,添加到a中。反之,屏幕移除的时候从a中poll出来,添加到b中。

实现

1.无线滚动 需要两个链表 mdanmulist初始包含所有弹幕数据 mvisibledanmulist每次取出mdanmulist第一条数据 填充到弹幕 直到弹幕动画结束后,再重新添加到mdanmulist里

//为展示在屏幕上的弹幕数据 private val mdanmulist = linkedlist<bulletchatdetail>()
//屏幕中展示的弹幕数据 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 {                       
					//这里是用来处理布局的交替工作,前面分析有说明                         					                           mvisibledanmulist.add(this)
                        createdanmuitemview(this)
                    }                
				}
            }
        })
    } else {
        if (misrunning.get().not()) {
            mdanmulist.poll()?.apply {                 
			//这里是用来处理布局的交替工作,前面分析有说明                 
			mvisibledanmulist.add(this)
            createdanmuitemview(this)
            }        
			}
    }
}

2.添加动画 每个弹幕默认都是头像加描述组成 所以自定义一个danmuitemview 内部就一个填充数据的方法

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.监听viewtree树,当布局初始化完成后,创建弹幕view,并开启动画 danmuitemview的位置 默认在-danmuitemview.measuredwidth 负弹幕view的宽度

private fun createdanmuitemview(danmu: bulletchatdetail) {
    val danmuitemview = danmuitemview(context).apply {         
	setdanmuentity(danmu)
    }     //这里将布局添加之后,默认便宜到屏幕右侧出屏幕,造成布局总是从右👉移动到👈左的效果。    
	val param = layoutparams(danmuitemview.measuredwidth, danmuitemview.measuredheight)
    param.gravity = gravity.center_vertical    
	param.leftmargin = mwidth     
	startdanmuanimate(danmuitemview)
    addview(danmuitemview, param)
}

4 .开启动画 view默认有动画的方法 我们已知view移动的距离为 屏幕宽度+弹幕view 需要匀速120.0f // 你想要的目标速度,单位是像素每秒 所以时间得动态计算 并且我们知道每个弹幕间ui的间距 所以监听弹幕view匀速动画所在的位置 当滚动进入屏幕的距离刚好是自身+间距的20dp时,就开启第二个弹幕item的动画 这样就实现了无限弹幕

val targetspeedpxpersecond = 120.0f // 你想要的目标速度,单位是像素每秒 
val totaltranslatex = ((mwidth + danmuitemview.measuredwidth)).tofloat()
val duration = (totaltranslatex / targetspeedpxpersecond * 1000).tolong() // 转换为毫秒
var isinit = false 
danmuitemview.animate()
       //注意这边设置的便宜量是容器布局的宽度+弹幕item布局的宽度,这样就确保滚动值刚好是从屏幕右侧外到屏幕左侧外         .translationxby(-totaltranslatex)
        .setduration(duration)
        .setinterpolator(linearinterpolator())
        .setupdatelistener {              
		val danmutranslatex =(mwidth + danmuitemview.measuredwidth) * (it.animatedvalue as float)
        //这里是关键,用来确保每个item布局的间距一致,判断如果滚动进入屏幕的距离刚好是自身+20dp,也就是刚好空出来了20dp之后,紧接着下一个弹幕布局开始添加并动起来。             
			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)
                }
                //很重要,在动画结束,也就是布局从屏幕移除之后,切记从布局中移除掉,                 
				//并且进行一波数据交替,方便实现无线循环                
				danmuitemview.danmu?.let {                    
				mvisibledanmulist.remove(it)
                    mdanmulist.add(it)
                }                
				yzlog.e("mhasstoprun->>>>${mhasstoprun}")
                if (mhasstoprun) {
                    createdanmu()
                }else{
                    removeview(danmuitemview)
                }
            }
        }).start()

当数据只有一两个的时候,需要添加一个变量来控制 比如如果弹幕数据源只有两条数据,当两条弹幕更好在同一屏幕出现此时弹幕数据源为空,可见数据源为两条,当第二条弹幕滚动到符合添加第三条数据的位置时,此时数据源已经没有数据了,需要将参数置为true,等到第二条数据动画结束时,重新走添加弹幕逻辑。具体事项在上面代码已经展示

以上就是android实现无限循环滚动弹幕的代码示例的详细内容,更多关于android无限循环滚动弹幕的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com