原创研发flutter3+getX+mediaKit跨平台仿抖音app短视频直播实战Flutter3-DouYin

flutter3_dylive使用最新跨平台技术flutter3.x+dart3+getx+get_storage+media_kit开发手机端仿抖音app小视频直播实战项目。实现了抖音全屏式上下滑动视频、左右滑动切换页面模块,直播间进场/礼物动画,聊天等模块。

技术栈

  • 编辑器:Vscode
  • 技术框架:Flutter3.19.2+Dart3.3.0
  • 路由/状态管理:get: ^4.6.6
  • 本地缓存:get_storage: ^2.1.1
  • 图片预览插件:photo_view: ^0.14.0
  • 刷新加载:easy_refresh^3.3.4
  • toast轻提示:toast^0.3.0
  • 视频套件:media_kit: ^1.1.10+1

flutter_douyin实现了类似抖音全屏沉浸式滑动效果(上下滑动视频、左右切换页面模块)。

如下图:实现了左右滑动的同时,顶部状态栏+Tab菜单+底部bottomNavigationBar导航栏三者联动效果。

项目结构目录

在项目创建前期,需要配置好Flutter SDK和Dart SDK开发环境。

https://flutter.dev/

https://www.dartcn.com/

https://pub.flutter-io.cn/

如果是使用Vscode编辑器开发flutter项目,大家需自行配置flutter/dart扩展语法插件。

项目中的一些技术知识点,有些是在之前的项目flutter3聊天实例中有介绍过,感兴趣的话可以去看看。

https://www.cnblogs.com/xiaoyan2017/p/18008370

https://www.cnblogs.com/xiaoyan2017/p/18048244

Flutter入口配置main.dart

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
import 'package:media_kit/media_kit.dart'; import 'utils/index.dart'; // 引入布局模板
import 'layouts/index.dart'; import 'binding/binding.dart'; // 引入路由管理
import 'router/index.dart'; void main() async {
// 初始化get_storage
await GetStorage.init(); // 初始化media_kit
WidgetsFlutterBinding.ensureInitialized();
MediaKit.ensureInitialized(); runApp(const MyApp());
} class MyApp extends StatelessWidget {
const MyApp({super.key}); @override
Widget build(BuildContext context) {
return GetMaterialApp(
title: 'FLUTTER3 DYLIVE',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFFFE2C55)),
useMaterial3: true,
// 修正windows端字体粗细不一致
fontFamily: Platform.isWindows ? 'Microsoft YaHei' : null,
),
home: const Layout(),
// 全局绑定GetXController
initialBinding: GlobalBindingController(),
// 初始路由
initialRoute: Utils.isLogin() ? '/' : '/login',
// 路由页面
getPages: routePages,
// 错误路由
// unknownRoute: GetPage(name: '/404', page: Error),
);
}
}

Flutter3底部导航栏

底部导航栏使用 bottomNavigationBar 组件实现页面模块切换。通过getx全局状态来联动控制底部导航栏背景颜色。

导航栏中间图标/图片按钮,使用了 Positioned 组件定位实现功能。

return Scaffold(
backgroundColor: Colors.grey[50],
body: pageList[pageCurrent],
// 底部导航栏
bottomNavigationBar: Theme(
// Flutter去掉BottomNavigationBar底部导航栏的水波纹
data: ThemeData(
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
hoverColor: Colors.transparent,
),
child: Obx(() {
return Stack(
children: [
Container(
decoration: const BoxDecoration(
border: Border(top: BorderSide(color: Colors.black54, width: .1)),
),
child: BottomNavigationBar(
backgroundColor: bottomNavigationBgcolor(),
fixedColor: FStyle.primaryColor,
unselectedItemColor: bottomNavigationItemcolor(),
type: BottomNavigationBarType.fixed,
elevation: 1.0,
unselectedFontSize: 12.0,
selectedFontSize: 12.0,
currentIndex: pageCurrent,
items: [
...pageItems
],
onTap: (index) {
setState(() {
pageCurrent = index;
});
},
),
),
// 自定义底部导航栏中间按钮
Positioned(
left: MediaQuery.of(context).size.width / 2 - 15,
top: 0,
bottom: 0,
child: InkWell(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Icon(Icons.tiktok, color: bottomNavigationItemcolor(centerDocked: true), size: 32.0,),
Image.asset('assets/images/applogo.png', width: 32.0, fit: BoxFit.contain,)
// Text('直播', style: TextStyle(color: bottomNavigationItemcolor(centerDocked: true), fontSize: 12.0),)
],
),
onTap: () {
setState(() {
pageCurrent = 2;
});
},
),
),
],
);
}),
),
);
import 'package:flutter/material.dart';
import 'package:get/get.dart'; import '../styles/index.dart';
import '../../controllers/page_video_controller.dart'; // 引入pages页面
import '../pages/index/index.dart';
import '../pages/video/index.dart';
import '../pages/live/index.dart';
import '../pages/message/index.dart';
import '../pages/my/index.dart'; class Layout extends StatefulWidget {
const Layout({super.key}); @override
State<Layout> createState() => _LayoutState();
} class _LayoutState extends State<Layout> {
PageVideoController pageVideoController = Get.put(PageVideoController()); // page索引
int pageCurrent = 0;
// page页面
List pageList = [const Index(), const FVideo(), const FLiveList(), const Message(), const My()];
// tabs选项
List pageItems = [
const BottomNavigationBarItem(
icon: Icon(Icons.home_outlined),
label: '首页'
),
const BottomNavigationBarItem(
icon: Icon(Icons.play_arrow_outlined),
label: '短视频'
),
const BottomNavigationBarItem(
icon: Icon(Icons.live_tv_rounded, color: Colors.transparent,),
label: ''
),
BottomNavigationBarItem(
icon: Stack(
alignment: const Alignment(4, -2),
children: [
const Icon(Icons.messenger_outline),
FStyle.badge(1)
],
),
label: '消息'
),
BottomNavigationBarItem(
icon: Stack(
alignment: const Alignment(1.5, -1),
children: [
const Icon(Icons.person_outline),
FStyle.badge(0, isdot: true)
],
),
label: '我'
)
]; // 底部导航栏背景色
Color bottomNavigationBgcolor() {
int index = pageCurrent;
int pageVideoTabIndex = pageVideoController.pageVideoTabIndex.value;
Color color = Colors.white;
if(index == 1) {
if([1, 2, 3].contains(pageVideoTabIndex)) {
color = Colors.white;
}else {
color = Colors.black;
}
}
return color;
}
// 底部导航栏颜色
Color bottomNavigationItemcolor({centerDocked = false}) {
int index = pageCurrent;
int pageVideoTabIndex = pageVideoController.pageVideoTabIndex.value;
Color color = Colors.black54;
if(index == 1) {
if([1, 2, 3].contains(pageVideoTabIndex)) {
color = Colors.black54;
}else {
color = Colors.white60;
}
}else if(index == 2 && centerDocked) {
color = FStyle.primaryColor;
}
return color;
} // ...
}

Flutter实现抖音全屏式滑动

如下图:实现类似抖音左右滑动全屏切换页面模块、上下滑动丝滑切换短视频。

采用 TabBar 和 PageView 联动页面滑动。

return Scaffold(
extendBodyBehindAppBar: true,
appBar: AppBar(
forceMaterialTransparency: true,
backgroundColor: [1, 2, 3].contains(pageVideoController.pageVideoTabIndex.value) ? null : Colors.transparent,
foregroundColor: [1, 2, 3].contains(pageVideoController.pageVideoTabIndex.value) ? Colors.black : Colors.white,
titleSpacing: 1.0,
leading: Obx(() => IconButton(icon: Icon(Icons.menu, color: tabColor(),), onPressed: () {},),),
title: Obx(() {
return TabBar(
controller: tabController,
tabs: pageTabs.map((v) => Tab(text: v)).toList(),
isScrollable: true,
tabAlignment: TabAlignment.center,
overlayColor: MaterialStateProperty.all(Colors.transparent),
unselectedLabelColor: unselectedTabColor(),
labelColor: tabColor(),
indicatorColor: tabColor(),
indicatorSize: TabBarIndicatorSize.label,
unselectedLabelStyle: const TextStyle(fontSize: 16.0, fontFamily: 'Microsoft YaHei'),
labelStyle: const TextStyle(fontSize: 16.0, fontFamily: 'Microsoft YaHei', fontWeight: FontWeight.w600),
dividerHeight: 0,
labelPadding: const EdgeInsets.symmetric(horizontal: 10.0),
indicatorPadding: const EdgeInsets.symmetric(horizontal: 5.0),
onTap: (index) {
pageVideoController.updatePageVideoTabIndex(index); // 更新索引
pageController.jumpToPage(index);
},
);
}),
actions: [
Obx(() => IconButton(icon: Icon(Icons.search, color: tabColor(),), onPressed: () {},),),
],
),
body: Column(
children: [
Expanded(
child: Stack(
children: [
/// 水平滚动模块
PageView(
// 自定义滚动行为(支持桌面端滑动、去掉滚动条槽)
scrollBehavior: PageScrollBehavior().copyWith(scrollbars: false),
scrollDirection: Axis.horizontal,
controller: pageController,
onPageChanged: (index) {
pageVideoController.updatePageVideoTabIndex(index); // 更新索引
setState(() {
tabController.animateTo(index);
});
},
children: [
...pageModules
],
),
],
),
),
],
),
);
PageVideoController pageVideoController = Get.put(PageVideoController());

List<String> pageTabs = ['热点', '长视频', '文旅', '商城', '关注', '同城服务', '推荐'];
final pageModules = [
const HotModule(),
const LongVideoModule(),
const TripModule(),
const MallModule(),
const FavorModule(),
const NearModule(),
const RecommendModule()
];
late final TabController tabController = TabController(initialIndex: pageVideoController.pageVideoTabIndex.value, length: pageTabs.length, vsync: this);
// 页面controller
late final PageController pageController = PageController(initialPage: pageVideoController.pageVideoTabIndex.value, viewportFraction: 1.0); @override
void dispose() {
tabController.dispose();
pageController.dispose();
super.dispose();
}

flutter短视频mini进度条

底部mini播放进度条一开始是采用自定义组件实现效果,不过不能拖动滚动条进度,后来就采用了Slider组件来实现。


// flutter滑动短视频模块  Q:282310962
return Container( color: Colors.black, child: Column( children: [ Expanded( child: Stack( children: [ /// 垂直滚动模块  PageView.builder( // 自定义滚动行为(支持桌面端滑动、去掉滚动条槽) scrollBehavior: PageScrollBehavior().copyWith(scrollbars: false), scrollDirection: Axis.vertical, controller: pageController, onPageChanged: (index) async { ... }, itemCount: videoList.length, itemBuilder: (context, index) { return Stack( children: [ // 视频区域  Positioned( top: 0, left: 0, right: 0, bottom: 0, child: GestureDetector( child: Stack( children: [ // 短视频插件  Visibility( visible: videoIndex == index, child: Video( controller: videoController, fit: BoxFit.cover, // 无控制条  controls: NoVideoControls, ), ), // 播放/暂停按钮  StreamBuilder( stream: player.stream.playing, builder: (context, playing) { return Visibility( visible: playing.data == false, child: Center( child: IconButton( padding: EdgeInsets.zero, onPressed: () { player.playOrPause(); }, icon: Icon( playing.data == true ? Icons.pause : Icons.play_arrow_rounded, color: Colors.white70, size: 70, ), ), ), ); }, ), ], ), onTap: () { player.playOrPause(); }, ), ), // 右侧操作栏  Positioned( bottom: 15.0, right: 10.0, child: Column( ... ), ), // 底部信息区域  Positioned( bottom: 15.0, left: 10.0, right: 80.0, child: Column( ... ), ), // 播放mini进度条  Positioned( bottom: 0.0, left: 10.0, right: 10.0, child: Visibility( visible: videoIndex == index && position > Duration.zero, child: Listener( child: SliderTheme( data: const SliderThemeData( trackHeight: 2.0, thumbShape: RoundSliderThumbShape(enabledThumbRadius: 4.0), // 调整滑块的大小 // trackShape: RectangularSliderTrackShape(), // 使用矩形轨道形状 overlayShape: RoundSliderOverlayShape(overlayRadius: 0), // 去掉Slider默认上下边距间隙 inactiveTrackColor: Colors.white24, // 设置非活动进度条的颜色 activeTrackColor: Colors.white, // 设置活动进度条的颜色 thumbColor: Colors.pinkAccent, // 设置滑块的颜色 overlayColor: Colors.transparent, // 设置滑块覆盖层的颜色  ), child: Slider( value: sliderValue, onChanged: (value) async { // debugPrint('当前视频播放时间$value');  setState(() { sliderValue = value; }); // 跳转播放时间 await player.seek(duration * value.clamp(0.0, 1.0)); }, onChangeEnd: (value) async { setState(() { sliderDraging = false; }); // 继续播放 if(!player.state.playing) { await player.play(); } }, ), ), onPointerMove: (e) { setState(() { sliderDraging = true; }); }, ), ), ), // 视频播放时间  Positioned( bottom: 90.0, left: 10.0, right: 10.0, child: Visibility( visible: sliderDraging, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Text(position.label(reference: duration), style: const TextStyle(color: Colors.white,),), Container( margin: const EdgeInsets.symmetric(horizontal: 7.0), child: const Text('/', style: TextStyle(color: Colors.white54, fontSize: 10.0,),), ), Text(duration.label(reference: duration), style: const TextStyle(color: Colors.white54,),), ], ), ), ), ], ); }, ), /// 固定层 // 红包  Positioned( left: 15.0, top: MediaQuery.of(context).padding.top + 20, child: Container( height: 40.0, width: 40.0, decoration: BoxDecoration( color: Colors.black12, borderRadius: BorderRadius.circular(100.0), ), child: UnconstrainedBox( child: Image.asset('assets/images/time-hb.png', width: 30.0, fit: BoxFit.contain,), ), ), ), ], ), ), ], ), );

flutter3直播模块

flutter直播模块包含了顶部信息、底部进场提示+商品层+弹幕层+操作栏等几个模块。

// 商品购买动效
Container(
...
), // 加入直播间动效
const AnimationLiveJoin(
joinQueryList: [
{'avatar': 'assets/images/logo.png', 'name': 'andy'},
{'avatar': 'assets/images/logo.png', 'name': 'jack'},
{'avatar': 'assets/images/logo.png', 'name': '一条咸鱼'},
{'avatar': 'assets/images/logo.png', 'name': '四季平安'},
{'avatar': 'assets/images/logo.png', 'name': '叶子'},
],
), // 送礼物动效
const AnimationLiveGift(
giftQueryList: [
{'label': '小心心', 'gift': 'assets/images/gift/gift1.png', 'user': 'Jack', 'avatar': 'assets/images/avatar/uimg2.jpg', 'num': 12},
{'label': '棒棒糖', 'gift': 'assets/images/gift/gift2.png', 'user': 'Andy', 'avatar': 'assets/images/avatar/uimg6.jpg', 'num': 36},
{'label': '大啤酒', 'gift': 'assets/images/gift/gift3.png', 'user': '一条咸鱼', 'avatar': 'assets/images/avatar/uimg1.jpg', 'num': 162},
{'label': '人气票', 'gift': 'assets/images/gift/gift4.png', 'user': 'Flower', 'avatar': 'assets/images/avatar/uimg5.jpg', 'num': 57},
{'label': '鲜花', 'gift': 'assets/images/gift/gift5.png', 'user': '四季平安', 'avatar': 'assets/images/avatar/uimg3.jpg', 'num': 6},
{'label': '捏捏小脸', 'gift': 'assets/images/gift/gift6.png', 'user': 'Alice', 'avatar': 'assets/images/avatar/uimg4.jpg', 'num': 28},
{'label': '你真好看', 'gift': 'assets/images/gift/gift7.png', 'user': '叶子', 'avatar': 'assets/images/avatar/uimg7.jpg', 'num': 95},
{'label': '亲吻', 'gift': 'assets/images/gift/gift8.png', 'user': 'YOYO', 'avatar': 'assets/images/avatar/uimg8.jpg', 'num': 11},
{'label': '玫瑰', 'gift': 'assets/images/gift/gift12.png', 'user': '宇辉', 'avatar': 'assets/images/avatar/uimg9.jpg', 'num': 3},
{'label': '私人飞机', 'gift': 'assets/images/gift/gift16.png', 'user': 'Hison', 'avatar': 'assets/images/avatar/uimg10.jpg', 'num': 273},
],
), // 直播弹幕+商品讲解
Container(
margin: const EdgeInsets.only(top: 7.0),
height: 200.0,
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Expanded(
child: ListView.builder(
padding: EdgeInsets.zero,
itemCount: liveJson[index]['message']?.length,
itemBuilder: (context, i) => danmuList(liveJson[index]['message'])[i],
),
),
SizedBox(
width: isVisibleGoodsTalk ? 7 : 35,
),
// 商品讲解
Visibility(
visible: isVisibleGoodsTalk,
child: Column(
...
),
),
],
),
), // 底部工具栏
Container(
margin: const EdgeInsets.only(top: 7.0),
child: Row(
...
),
),

直播聊天弹幕通过Flexible配合Text.rich / TextSpan实现消息自适应布局界面。

通过 SlideTransition 组件实现直播进场动画。

return SlideTransition(
position: animationFirst ? animation : animationMix,
child: Container(
alignment: Alignment.centerLeft,
margin: const EdgeInsets.only(top: 7.0),
padding: const EdgeInsets.symmetric(horizontal: 7.0,),
height: 23.0,
width: 250,
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [
Color(0xFF6301FF), Colors.transparent
],
),
borderRadius: BorderRadius.horizontal(left: Radius.circular(10.0)),
),
child: joinList!.isNotEmpty ?
Text('欢迎 ${joinList![0]['name']} 加入直播间', style: const TextStyle(color: Colors.white, fontSize: 14.0,),)
:
Container()
,
),
);
class _AnimationLiveJoinState extends State<AnimationLiveJoin> with TickerProviderStateMixin {
// 动画控制器
late AnimationController controller = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 500), // 第一个动画持续时间
);
late AnimationController controllerMix = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 1000), // 第二个动画持续时间
);
// 动画
late Animation<Offset> animation = Tween(begin: const Offset(2.5, 0), end: const Offset(0, 0)).animate(controller);
late Animation<Offset> animationMix = Tween(begin: const Offset(0, 0), end: const Offset(-2.5, 0)).animate(controllerMix); Timer? timer;
// 是否第一个动画
bool animationFirst = true;
// 是否空闲
bool idle = true;
// 加入直播间数据列表
List? joinList; @override
void initState() {
super.initState(); joinList = widget.joinQueryList!.toList(); runAnimation();
animation.addListener(() {
if(animation.status == AnimationStatus.forward) {
debugPrint('第一个动画进行中');
idle = false;
setState(() {});
}else if(animation.status == AnimationStatus.completed) {
debugPrint('第一个动画结束');
animationFirst = false;
if(controllerMix.isCompleted || controllerMix.isDismissed) {
timer = Timer(const Duration(seconds: 2), () {
controllerMix.forward();
debugPrint('第二个动画开始');
});
}
setState(() {});
}
});
animationMix.addListener(() {
if(animationMix.status == AnimationStatus.forward) {
setState(() {});
}else if(animationMix.status == AnimationStatus.completed) {
animationFirst = true;
controller.reset();
controllerMix.reset();
if(joinList!.isNotEmpty) {
joinList!.removeAt(0);
}
idle = true;
// 执行下一个数据
runAnimation();
setState(() {});
}
});
} void runAnimation() {
if(joinList!.isNotEmpty) {
// 空闲状态才能执行,防止添加数据播放状态混淆
if(idle == true) {
if(controller.isCompleted || controller.isDismissed) {
setState(() {});
timer = Timer(Duration.zero, () {
controller.forward();
});
}
}
}
} @override
void dispose() {
controller.dispose();
controllerMix.dispose();
timer?.cancel();
super.dispose();
} }

综上就是flutter3实战开发仿抖音的一些知识分享,由于涉及的技术点还是蛮多的,就没有非常详细的展开介绍了。希望以上的一些分享能给大家有些帮助~

最后附上两个跨端项目

uniapp+vue3短视频直播商城:https://www.cnblogs.com/xiaoyan2017/p/17938517

tauri+vite4中后台管理系统:https://www.cnblogs.com/xiaoyan2017/p/17552562

bottomNavigationBar

flutter3-dylive仿抖音App实例|Flutter3+Getx实战短视频直播应用的更多相关文章

  1. uni-app仿抖音APP短视频+直播+聊天实例|uniapp全屏滑动小视频+直播

    基于uniapp+uView-ui跨端H5+小程序+APP短视频|直播项目uni-ttLive. uni-ttLive一款全新基于uni-app技术开发的仿制抖音/快手短视频直播项目.支持全屏丝滑般上 ...

  2. 关于个人项目(臻美MV【仿抖音App】)滑动切换视频的分析(前端角度)

    我们知道你天天刷抖音的时候可以上滑切换视频,互不影响.那么我们站在前端的角度能否可以实现这种效果呢?这是我的个人项目:臻美MV 下面我是用Vue写的,现在我把它开源. Vue: 初始界面 <te ...

  3. Vue3.0短视频+直播|vue3+vite2+vant3仿抖音界面|vue3.x小视频实例

    基于vue3.0构建移动端仿抖音/快手短视频+直播实战项目Vue3-DouYin. 5G时代已来,短视频也越来越成为新一代年轻人的娱乐方式,在这个特殊之年,又将再一次成为新年俗! 基于vue3.x+v ...

  4. 10分钟快速上车短视频风口:基于uniapp框架创建自己的仿抖音短视APP

    在今年也就是第48次发布的<中国互联网络发展状况统计报告>有这样一个数据,21年的上半年以来,我国我国网民规模达10.11亿,其中短视频用户达8.88亿.碎片化的生活场景下,短视频成为人们 ...

  5. iOS多种刷新样式、音乐播放器、仿抖音视频、旅游App等源码

    iOS精选源码 企业级开源项目,模仿艺龙旅行App 3D立体相册,可以旋转的立方体 横竖屏切换工具,使用陀螺仪检测手机设备方向,锁屏状... Swift版Refresh(可以自定义多种样式)架构方面有 ...

  6. 基于vue+uniapp直播项目|uni-app仿抖音/陌陌直播室

    一.项目简介 uni-liveShow是一个基于vue+uni-app技术开发的集小视频/IM聊天/直播等功能于一体的微直播项目.界面仿制抖音|火山小视频/陌陌直播,支持编译到多端(H5.小程序.Ap ...

  7. android高仿抖音、点餐界面、天气项目、自定义view指示、爬取美女图片等源码

    Android精选源码 一个爬取美女图片的app Android高仿抖音 android一个可以上拉下滑的Ui效果 android用shape方式实现样式源码 一款Android上的新浪微博第三方轻量 ...

  8. Vite2+Electron仿抖音|vite2.x+electron12+vant3短视频|直播|聊天

    整合vite2+electron12跨平台仿抖音电脑版实战Vite2-ElectronDouYin. 基于vite2.0+electron12+vant3+swiper6+v3popup等技术跨端仿制 ...

  9. 一个类似抖音 APP 拍摄按钮效果的控件

    TouchButton 一个类似抖音 APP 拍摄按钮效果的控件 效果图预览 用法 <net.angrycode.library.TouchButton android:id="@+i ...

  10. Python 爬虫——抖音App视频抓包

    APP抓包 前面我们了解了一些关于 Python 爬虫的知识,不过都是基于 PC 端浏览器网页中的内容进行爬取.现在手机 App 用的越来越多,而且很多也没有网页端,比如抖音就没有网页版,那么上面的视 ...

随机推荐

  1. JS LeetCode 303. 区域和检索 - 数组不可变,一维数组的前缀和

    壹 ❀ 引 本题来自LeetCode303. 区域和检索 - 数组不可变,属于一道简单题,不过题目期望的做法我也是看了题解才懂得,这里大致做个记录,题目描述如下: 给定一个整数数组 nums,求出数组 ...

  2. NC14291 Cut

    题目链接 题目 题目描述 给你一个长度为 \(n\) 的序列,你每次可以将一个序列分割成两个连续的的子序列, 分割的代价为原序列的总和. 现在允许你在初始时将序列重新排列一次. 问分割成 \(n\) ...

  3. kotlin--Object关键字

    1.匿名内部类 Object可以实现,继承一个抽象类的同时,实现多个接口. interface A { fun funA() } interface B { fun funB() } abstract ...

  4. Java设计模式-组合模式Composite

    介绍 组合模式(Composite Pattern),又叫部分整体模式,它创建了对象组的树形结构,将对象组合成树状结构以表示"整体-部分"的层次关系. 组合模式依据树形结构来组合对 ...

  5. Java集合框架学习(十三) Collections类详解

    Collections类介绍 这个类操作或返回集合的专有静态方法. 它包含多态算法,利用wrappers(封装器)返回指定集合类型的新集合,以及其他一些零散功能. 如果该类的方法引用的集合或类对象为n ...

  6. webservice之jax-ws实现方式(服务端)

    1.什么是webservice? webservice是一种远程资源调用技术,它的实现方式主要分为两种, 第一种是jaxws方式,它是面向方法的,它的数据类型是xml是基于soap实现传输: 第二种是 ...

  7. 用Taro写一个微信小程序——渲染 HTML

    在taro小程序里面渲染一段html代码,具体可查看文档https://docs.taro.zone/docs/html 一.渲染 HTML 文档推荐使用 dangerouslySetInnerHTM ...

  8. HTML初始----day01

    1.HTML骨架标签总结 标签名 定义 说明 html标签 页面中最大的标签,称为根标签 <head></head> 文档的头部 注意在head标签中我们必须要设置的标签是ti ...

  9. Advanced .Net Debugging 2:CLR基础

    一.简介 这是2024新年后我的第一篇文章,也是我的<Advanced .Net Debugging>这个系列的第二篇文章.这篇文章告诉我们为了进行有效的程序调试,我们需要掌握哪些知识.言 ...

  10. 【LeetCode二叉树#07】左叶子节点之和(基于栈的迭代法前中后序遍历复习)

    左叶子节点之和 力扣题目链接(opens new window) 计算给定二叉树的所有左叶子之和. 示例: 思路 注意审题,这里是要求 左叶子节点 之和 不是二叉树中的左侧节点之和,因此使用层序遍历是 ...