Android实现无限循环滚动弹幕的代码示例
,文中通过代码示例讲解的非常详细,对大家实现循环弹幕有一定的帮助,需要的朋友可以参考下
先上效果图
要实现上面的效果,我们先拆分下实现要素:
- 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无限循环滚动弹幕的资料请关注脚本之家其它相关文章!
相关文章
Android传递Bitmap对象在两个Activity之间
这篇文章主要介绍了Android传递Bitmap对象在两个Activity之间的相关资料,需要的朋友可以参考下2016-01-01Android 简单的弹出框(在屏幕中间,传string[],根据内容框框大小自适应)
这篇文章主要介绍了Android 简单的弹出框(在屏幕中间,传string[],根据内容框框大小自适应),需要的朋友可以参考下2017-04-04
最新评论