首頁 > 軟體

Android Flutter實現彈幕效果

2022-06-18 14:01:33

前言

需求要點如下:

  • 彈幕行數為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定時每秒鐘執行一次scrollControlleranimateTo方法移動偏移量並且偏移量不斷累加。

其次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;
}

點選事件

一切都實現得很順利最終就是彈幕點選實現。但實際上當ListViewscrollController在執行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();
          });
        },
      );
    },
  ),
);

到此這篇關於Android Flutter實現彈幕效果的文章就介紹到這了,更多相關Flutter彈幕效果內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com