Android Flutter实现弹幕效果
作者:JulyYu 发布时间:2022-07-17 16:15:17
前言
需求要点如下:
弹幕行数为3行,每条弹幕相互依靠但不存在重叠
每条弹幕可交互点击跳转
滚动速度恒定 触摸不可暂停播放
弹幕数据固定一百条且支持轮询播放
弹幕排序规则如下:
1 4 7
2 5 8
3 6 9
通用弹幕实现方案
Flutter Dev Package
已有开源弹幕实现组件,这里举例barrage_page
的实现方式(大多数实现底层逻辑基本一样)。
基本架构采用Stack然后向布局中提交弹幕布局,添加时设置好弹幕偏移量来设置弹幕位置。
Stack(fit: StackFit.expand, children: <Widget>[
widget.child,
_controller.isEnabled
? Stack(
fit: StackFit.loose,
children: <Widget>[]
..addAll(_widgets.values ?? const SizedBox()))
: const SizedBox(),
]);
});
弹幕效果代码
但因为每条弹幕可能会出现重叠情况无法合理定位每条弹幕的位置因此放弃该方案。
PS:widget只有在build到布局后才能获取到它基础信息(相对位置信息,宽高等)就无法计算出所有弹幕的位置信息。
ListView弹幕方案实现
最先想到使用瀑布流flutter_staggered_grid_view
实现弹幕布局但由于组件暂时不支持横向布局就放弃了。
基本框架
采用三个ListView实现每一行弹幕效果。虽然不太推荐以这种形式实现但从快速实现效果来说是比较简单便捷兜底方案。(可以实现但不推荐)
Container(
height: 200,
child: Column(
children: [
Expanded(
child: ListView.builder(
scrollDirection: Axis.horizontal,
controller: scrollController1,
itemBuilder: (context, index) {
return Common.getWidget(index,
height: 30, width: random.nextInt(100).toDouble());
},
),
),
Expanded(
child: ListView.builder(
scrollDirection: Axis.horizontal,
controller: scrollController2,
itemBuilder: (context, index) {
return Common.getWidget(index,
height: 30, width: random.nextInt(100).toDouble());
},
)),
Expanded(
child: ListView.builder(
scrollDirection: Axis.horizontal,
controller: scrollController3,
itemBuilder: (context, index) {
return Common.getWidget(index,
height: 30, width: random.nextInt(100).toDouble());
},
))
],
),
)
轮播滚动
添加定时器periodic
定时每秒钟执行一次scrollController
的animateTo
方法移动偏移量并且偏移量不断累加。
其次ListView
支持无限滑动只要ListView.builder
不设置itemCount
就能实现。
Timer _timer;
scroll = () {
offset += 100;
scrollController1.animateTo(offset,
duration: Duration(seconds: 1), curve: Curves.linear);
scrollController2.animateTo(offset,
duration: Duration(seconds: 1), curve: Curves.linear);
scrollController3.animateTo(offset,
duration: Duration(seconds: 1), curve: Curves.linear);
};
_timer = Timer.periodic(Duration(seconds: 1), (timer) {
scroll();
});
轮询算法
ListView
支持无限滑动后itemBuilder
回调下标Index
会超出数据源最大值。因此数据源也需要支持无限轮询来配合列表滚动。start
表示弹幕开始取值,这里设置为(0,1,2);index
表示itemBuilder
回调下标Index
。
int findIndex(int start, int index) {
index = start + index * 3;
if (expressList.length < index) {
index = index % (expressList.length - 1); // 取余
} else if (expressList.length == index) { // 是否是最后一个数据
index = start;
if (index >= expressList.length) { // 还需要判断数据源是否比start还小
index = (index % expressList.length - 1);
}
}
return index;
}
点击事件
一切都实现得很顺利最终就是弹幕点击实现。但实际上当ListView
的scrollController
在执行animateTo
时其实点击操作是失效的,ListView
无法响应点击事件。只有当animateTo
操作结束之后再执行点击才能执行点击。因此若要实现这个功能只能先将Timer
暂停再执行一次点击,再一次点击不可能是用户再去触发,这里只能采用模拟点击形式实现。
PS:ListView
无法响应点击事件具体原因还待研究,个人猜测列表做动画时对外部触摸事件进行了屏蔽处理。
GestureDetector(
onTapUp: (details){
// 点击抬起之后暂停定时器
_timer?.cancel();
// 模拟一次点击
Timer(Duration(milliseconds: 100),() {
GestureBinding.instance.handlePointerEvent(PointerAddedEvent(pointer: 0,position: details.globalPosition));
GestureBinding.instance.handlePointerEvent(PointerDownEvent(pointer: 0,position: details.globalPosition));
GestureBinding.instance.handlePointerEvent(PointerUpEvent(pointer: 0,position: details.globalPosition));
});
},
child: ListView.builder(
controller: scrollController,
physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
return GestureDetector(
behavior: HitTestBehavior.opaque,
child: Common.getWidget(index),
onTap: () {
// 内部响应点击事件 然后重新设置定时器滚动列表
_timer = Timer.periodic(Duration(seconds: 1), (timer) {
scroll();
});
},
);
},
),
);
来源:https://juejin.cn/post/7110108145292165150


猜你喜欢
- tomcat中文乱码问题这几天测试的兄弟发现了项目中存在乱码问题 经过排查发现是tomcat中的问题 于是在server.xml中添加了如下
- 自动完成文本框(AutoCompleteTextView),用于实现允许用户输入一定字符后,显示一个下拉菜单,供用户从中选择,当用户选择某个
- List list=new ArrayList()是怎么回事首先明确List是接口,ArrayList是它的实现类以下两种方法都可以,但是不
- Spring Security是一款基于Spring框架的认证和授权框架,提供了一系列控制访问和保护应用程序的功能,同时也支持基于角色和权限
- 本文告诉大家如何使用相同权限调用cmd并且传入命令。如果想要用相同的权限运行一个程序,可以使用 ProcessStartInfo 的方法&n
- 前言在阅读Kotlin的代码时,经常有看到 :: 这个符号,这个符号专业术语叫做成员引用,在代码中使用可以简化代码,那到底怎么使用呢以及使用
- 线程池示例在分析线程池之前,先看一个简单的线程池示例。import java.util.concurrent.Executors;impor
- 0.前言文章需求:对于学生来说,目前网上确实没有比较统一而且质量好的支付教程。因为支付对个人开发者尤其是学生来说不太友好。因此,自己折腾两天
- RocketMq消息处理RocketMq消息处理整个流程如下:本系列RocketMQ4.8注释github地址,希望对大家有所帮助,要是觉得
- 什么是MD5?Message Digest Algorithm MD5(中文名为消息摘要算法第五版)为计算机安全领域广泛使用的一种散列函数,
- 系列文章已完成,目录如下:jdk-logging log4j logback日志系统实现机制原理介绍commons-lo
- 本文实例讲述了Android编程判断当前应用是否在后台运行的方法。分享给大家供大家参考,具体如下:/** 判断程序是否在后台运行 */pub
- 1.常见字符串编码常见的字符串编码有:LATIN1 只能保存ASCII字符,又称ISO-8859-1。UTF-8 变长字节编码,一个字符需要
- JVM(Java虚拟机)是一个抽象的计算模型。就如同一台真实的机器,它有自己的指令集和执行引擎,可以在运行时操控内存区域。目的是为构建在其上
- Spring session 获取当前账户登录数一、登录校验成功时,向session加入关键信息,代码如下:session.setAttri
- 问题之前项目能够正常运行,因为默认选择db0,后来新的需求来了,不是默认db0,而是给参数选择db。修改后代码如下,却报错NOAUTH Au
- 这篇文章主要介绍了Java List集合排序实现方法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的
- springboot 长轮询实现基于 @EnableAsync , @Sync@SpringBootApplication@EnableAs
- 1. 接口是一种规范很好,你已经知道接口是一种规范了!下面这张图是我们生活中遇到的接口:电源插座接口。2. 为什么需要规范呢?因为
- 对于获取了一大堆字符串但是又不想要里面的html标签怎么办?特别是像博客园这个富文本框中,可以带样式的,取出来的文章内容也是带样式的。但是在