From 4e9e3065ea405a9fbb8e4a1267f03af19d5cdaf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=80=E8=84=B8=E6=87=B5=E9=80=BC?= <934249779@qq.com> Date: Wed, 4 Dec 2019 17:44:21 +0800 Subject: [PATCH] feat: add custom listview SliverChildBuilderDelegate demo (#124) --- example/lib/listview.dart | 62 +++++++--- .../lib/my_sliver_child_builder_delegate.dart | 110 ++++++++++++++++++ 2 files changed, 154 insertions(+), 18 deletions(-) create mode 100644 example/lib/my_sliver_child_builder_delegate.dart diff --git a/example/lib/listview.dart b/example/lib/listview.dart index e49b23b0..8e9db2ae 100644 --- a/example/lib/listview.dart +++ b/example/lib/listview.dart @@ -1,4 +1,5 @@ import 'package:fijkplayer/fijkplayer.dart'; +import 'package:fijkplayer_example/my_sliver_child_builder_delegate.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; @@ -10,42 +11,67 @@ class ListScreen extends StatefulWidget { } class _ListScreenState extends State { - static int listItemCount = 8; - List players; + static int listItemCount = 15; + final Map _players = Map(); @override void initState() { super.initState(); - players = List(); - for (int i = 0; i < listItemCount; i++) { - players.add(FijkPlayer()); - } } @override Widget build(BuildContext context) { return Scaffold( appBar: FijkAppBar.defaultSetting(title: "List View"), - body: ListView.builder( - itemCount: listItemCount, - itemExtent: 240, - itemBuilder: (BuildContext context, int index) { - FijkPlayer p = players[index]; + body: ListView.custom( + childrenDelegate: MySliverChildBuilderDelegate( + (BuildContext context, int index) { + if (!_players.containsKey(index)) { + _players[index] = FijkPlayer(); + _players[index].setDataSource("asset:///assets/butterfly.mp4"); + } FijkLog.i("build list item $index", tag: "list"); - p.setDataSource("asset:///assets/butterfly.mp4", autoPlay: true); - return FijkView( - player: p, + return Column( + children: [ + Container( + height: 240.0, + child: FijkView( + player: _players[index], + ), + ), + SizedBox(height: 12.0) + ], ); - }), + }, + childCount: listItemCount, + onItemVisibilityState: setVideoVisibilityState, + ), + //设置cacheExtent 为0.0 不然获取到的显示隐藏的index不准确 + cacheExtent: 0.0, + ), ); } + void setVideoVisibilityState(List exposure, List hidden) { + exposure.forEach((index) { + //显示的indexs + if (_players[index]?.state == FijkState.idle) { + _players[index]?.setDataSource("asset:///assets/butterfly.mp4"); + } + }); + hidden.forEach((index) async { + //隐藏的indexs + _players[index]?.stop(); + _players[index]?.reset(); + }); + } + @override void dispose() { super.dispose(); - for (var p in players) { + _players.forEach((i, p) { p.release(); - } - players.clear(); + }); + _players.clear(); } } diff --git a/example/lib/my_sliver_child_builder_delegate.dart b/example/lib/my_sliver_child_builder_delegate.dart new file mode 100644 index 00000000..f68cad44 --- /dev/null +++ b/example/lib/my_sliver_child_builder_delegate.dart @@ -0,0 +1,110 @@ +import 'package:flutter/material.dart'; + +typedef void OnItemVisibilityState(List exposure, List hidden); +///通过SliverChildBuilderDelegate 对比Item得出滑动隐藏显示的Item下标 +class MySliverChildBuilderDelegate extends SliverChildBuilderDelegate { + MySliverChildBuilderDelegate(Widget Function(BuildContext, int) builder, + {int childCount, + bool addAutomaticKeepAlives = true, + bool addRepaintBoundaries = true, + this.onItemVisibilityState}) + : super(builder, + childCount: childCount, + addAutomaticKeepAlives: addAutomaticKeepAlives, + addRepaintBoundaries: addRepaintBoundaries); + + final OnItemVisibilityState onItemVisibilityState; + final visibilityMonitor = VisibilityMonitor(); + + @override + void didFinishLayout(int firstIndex, int lastIndex) { + visibilityMonitor.update( + VisibilityState( + firstIndex: firstIndex, + lastIndex: lastIndex, + ), + onItemVisibilityState); + } +} + +class VisibilityState { + const VisibilityState({this.firstIndex, this.lastIndex}); + + final int firstIndex; + final int lastIndex; +} + +class ChangeSet { + final List exposure = []; + final List hidden = []; + + bool get empty => exposure.length == 0 && hidden.length == 0; +} + +class VisibilityMonitor { + VisibilityState lastState; + + addSequenceToList(List list, int sequenceStart, int sequenceEnd) { + if (sequenceStart <= sequenceEnd) { + for (var i = sequenceStart; i <= sequenceEnd; i++) { + list.add(i); + } + } else { + for (var i = sequenceEnd; i >= sequenceStart; i--) { + list.add(i); + } + } + } + + update( + VisibilityState newState, OnItemVisibilityState onItemVisibilityState) { + if (lastState != null && + newState.firstIndex == lastState.firstIndex && + newState.lastIndex == lastState.lastIndex) { + return; + } + + final changeSet = ChangeSet(); + + if (lastState == null) { + addSequenceToList( + changeSet.exposure, newState.firstIndex, newState.lastIndex); + } else if (newState.firstIndex > lastState.lastIndex) { + addSequenceToList( + changeSet.exposure, newState.firstIndex, newState.lastIndex); + addSequenceToList( + changeSet.hidden, lastState.firstIndex, lastState.lastIndex); + } else if (newState.lastIndex < lastState.firstIndex) { + addSequenceToList( + changeSet.exposure, newState.lastIndex, newState.firstIndex); + addSequenceToList( + changeSet.hidden, lastState.lastIndex, lastState.firstIndex); + } else { + if (newState.firstIndex < lastState.firstIndex) { + addSequenceToList( + changeSet.exposure, lastState.firstIndex - 1, newState.firstIndex); + } + + if (newState.firstIndex > lastState.firstIndex) { + addSequenceToList( + changeSet.hidden, lastState.firstIndex, newState.firstIndex - 1); + } + + if (newState.lastIndex > lastState.lastIndex) { + addSequenceToList( + changeSet.exposure, lastState.lastIndex + 1, newState.lastIndex); + } + + if (newState.lastIndex < lastState.lastIndex) { + addSequenceToList( + changeSet.hidden, lastState.lastIndex, newState.lastIndex + 1); + } + } + + lastState = newState; + + if (!changeSet.empty && onItemVisibilityState != null) { + onItemVisibilityState(changeSet.exposure, changeSet.hidden); + } + } +}