【Flutter】動畫介紹隱式動畫

🔥 本文由 程序喵正在路上 原創,CSDN首發!
💖 系列專欄:Flutter學習
🌠 首發時間:2024年5月28日
🦋 歡迎關注🖱點贊👍收藏🌟留言🐾

目錄

  • 動畫基本原理
  • Flutter動畫簡介
  • 隱式動畫
    • curve
    • AnimatedContainer
    • AnimatedPadding
    • AnimatedPositioned
    • AnimatedOpacity
    • AnimatedDefaultTextStyle
    • AnimatedSwitcher
    • 修改 AnimatedSwitcher 的動畫效果
    • 子組件相同的AnimatedSwitcher

動畫基本原理

在任何系統的UI框架中,動畫實現的原理都是相同的,即:在一段時間內,快速地多次改變UI外觀;由于人眼會產生視覺暫留,所以最終看到的就是一個“連續”的動畫,這和電影的原理是一樣的。

我們將UI的一次改變稱為一個動畫幀,對應一次屏幕刷新,而決定動畫流暢度的一個重要指標就是幀率FPS(Frame Per Second),即每秒的動畫幀數。很明顯,幀率越高則動畫就會越流暢!一般情況下,對于人眼來說,動畫幀率超過16 FPS,就基本能看了,超過 32 FPS就會感覺相對平滑,而超過 32FPS,大多數人基本上就感受不到差別了。

由于動畫的每一幀都是要改變UI輸出,所以在一個時間段內連續的改變UI輸出是比較耗資源的,對設備的軟硬件系統要求都較高,所以在UI系統中,動畫的平均幀率是重要的性能指標,而在Flutter中,理想情況下是可以實現 60FPS 的,這和原生應用能達到的幀率是基本是持平的。

Flutter動畫簡介

FLutter 中的動畫主要分為:隱式動畫、顯式動畫、自定義隱式動畫、自定義顯式動畫和 Hero 動畫。

隱式動畫

通過幾行代碼就可以實現隱式動畫,由于隱式動畫背后的實現原理和繁瑣的操作細節都被隱去了,所以叫隱式動畫,FLutter 中提供的 AnimatedContainerAnimatedPaddingAnimatedPositioned
AnimatedOpacityAnimatedDefaultTextStyleAnimatedSwitcher 都屬于隱式動畫。

隱式動畫中可以通過 duration 來配置動畫時長,通過 curve (曲線)來配置動畫過程,這兩個屬性在上述組件中都是存在的。

curve

Flutter 中,Curves 類提供了一系列預定義的曲線,用于控制動畫的速度變化。以下是一些常用的 Curves 組件的值及簡單解釋:

  1. Curves.linear

    • 線性曲線,動畫以恒定的速度進行,沒有加速或減速。
  2. Curves.decelerate

    • 減速曲線,動畫開始時速度較快,然后逐漸減速。
  3. Curves.ease

    • 標準的加速減速曲線,動畫開始和結束時速度較慢,中間時速度較快。
  4. Curves.easeIn

    • 加速曲線,動畫開始時速度較慢,然后逐漸加速。
  5. Curves.easeOut

    • 減速曲線,動畫開始時速度較快,然后逐漸減速。
  6. Curves.easeInOut

    • 加速減速曲線,動畫開始和結束時速度較慢,中間時速度較快,類似于Curves.ease
  7. Curves.fastOutSlowIn

    • 快出慢入曲線,動畫開始時速度較快,然后逐漸減速到結束。
  8. Curves.bounceIn

    • 彈簧效果曲線,動畫開始時速度為0,然后加速進入動畫,到達最大速度后反彈一次。
  9. Curves.elasticIn

    • 彈性效果曲線,動畫開始時速度為0,然后加速進入動畫,到達最大速度后會有一些超過目標值的回彈效果。

這些是常用的一些Curves組件的值,通過選擇合適的曲線,可以控制動畫的速度變化,從而實現不同的動畫效果。

AnimatedContainer

AnimatedContainer 的屬性和 Container 屬性基本是一樣的,不同的地方是,當 AnimatedContainer 屬性改變的時候會觸發動畫。

下面的程序中,我們通過浮動按鈕來改變 AnimatedContainertransform 的值,以觸發動畫。

import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({super.key});Widget build(BuildContext context) {return const MaterialApp(home: MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key});State<MyHomePage> createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage> {bool flag = true;Widget build(BuildContext context) {return Scaffold(floatingActionButton: FloatingActionButton(child: const Icon(Icons.refresh),onPressed: () {setState(() {flag = !flag;});},),appBar: AppBar(title: const Text('AnimatedContainer'),),body: Center(child: AnimatedContainer(duration: const Duration(seconds: 1, milliseconds: 100),width: 200,height: 200,transform: flag? Matrix4.translationValues(0, 0, 0): Matrix4.translationValues(-100, 0, 0),color: Colors.blue,),),);}
}

AnimatedPadding

import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({super.key});Widget build(BuildContext context) {return const MaterialApp(home: MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key});State<MyHomePage> createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage> {bool flag = true;Widget build(BuildContext context) {return Scaffold(floatingActionButton: FloatingActionButton(child: const Icon(Icons.refresh),onPressed: () {setState(() {flag = !flag;});},),appBar: AppBar(title: const Text('AnimatedPadding'),),body: AnimatedPadding(curve: Curves.linear,padding: EdgeInsets.fromLTRB(10, flag ? 10 : 200, 0, 0),duration: const Duration(seconds: 2),child: Container(width: 100,height: 100,color: Colors.blue,),),);}
}

AnimatedPositioned

import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({super.key});Widget build(BuildContext context) {return const MaterialApp(home: MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key});State<MyHomePage> createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage> {bool flag = true;Widget build(BuildContext context) {return Scaffold(floatingActionButton: FloatingActionButton(child: const Icon(Icons.refresh),onPressed: () {setState(() {flag = !flag;});},),appBar: AppBar(title: const Text('AnimatedPositioned'),),body: Stack(children: [ListView(children: const [ListTile(title: Text("我是一個列表"),),ListTile(title: Text("我是一個列表"),),ListTile(title: Text("我是一個列表"),),ListTile(title: Text("我是一個列表"),),ListTile(title: Text("我是一個列表"),),ListTile(title: Text("我是一個列表"),),],),AnimatedPositioned(curve: Curves.linear,right: flag ? 10 : 300,top: flag ? 10 : 560,duration: const Duration(seconds: 1, milliseconds: 500),child: Container(width: 60,height: 60,color: Colors.blue,)),],),);}
}

AnimatedOpacity

import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({super.key});Widget build(BuildContext context) {return const MaterialApp(home: MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key});State<MyHomePage> createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage> {bool flag = true;Widget build(BuildContext context) {return Scaffold(floatingActionButton: FloatingActionButton(child: const Icon(Icons.refresh),onPressed: () {setState(() {flag = !flag;});},),appBar: AppBar(title: const Text('AnimatedOpacity'),),body: Center(child: AnimatedOpacity(opacity: flag ? 0.2 : 1,duration: const Duration(seconds: 1),curve: Curves.easeIn,child: Container(width: 200,height: 200,color: Colors.red,),),),);}
}

AnimatedDefaultTextStyle

import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({super.key});Widget build(BuildContext context) {return const MaterialApp(home: MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key});State<MyHomePage> createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage> {bool flag = true;Widget build(BuildContext context) {return Scaffold(floatingActionButton: FloatingActionButton(child: const Icon(Icons.refresh),onPressed: () {setState(() {flag = !flag;});},),appBar: AppBar(title: const Text('AnimatedDefaultTextStyle'),),body: Center(child: Container(alignment: Alignment.center,width: 300,height: 300,color: Colors.blue,child: AnimatedDefaultTextStyle(duration: const Duration(seconds: 1),style: TextStyle(fontSize: flag ? 20 : 50, color: Colors.white),child: const Text("你好Flutter"),),),),);}
}

AnimatedSwitcher

Flutter 中的 AnimatedSwitcher 是一個用于在切換子元素時執行動畫效果的預置組件。它允許你在切換子元素時使用自定義的過渡效果,從而實現平滑的切換體驗。

AnimatedSwitcher 通常用于在切換應用程序界面的不同部分或內容時提供動畫效果。例如,當用戶點擊按鈕切換頁面或者切換視圖時,可以使用 AnimatedSwitcher 來實現頁面間的過渡動畫。

在使用 AnimatedSwitcher 時,你需要提供新舊子元素,并指定一個過渡效果。當新的子元素被提供時,AnimatedSwitcher 將會在舊的子元素和新的子元素之間執行過渡動畫。

要使用 AnimatedSwitcher,通常需要指定以下幾個關鍵屬性:

  • child:當前的子元素,即將要顯示的子元素。
  • duration:動畫的持續時間,用于控制過渡動畫的速度。
  • transitionBuilder:一個回調函數,用于定義子元素切換時的過渡效果。該回調函數接受當前子元素和動畫對象,并返回一個 Widget,用于實現過渡效果。

通過這些屬性,你可以輕松地在 Flutter 應用程序中添加動畫效果,從而提升用戶體驗并增加應用的交互性。AnimatedSwitcherFlutter 框架中 Material 組件庫的一部分,因此可以與其他 Material 風格的組件無縫集成。

import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({super.key});Widget build(BuildContext context) {return const MaterialApp(home: MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key});State<MyHomePage> createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage> {bool flag = true;Widget build(BuildContext context) {return Scaffold(floatingActionButton: FloatingActionButton(child: const Icon(Icons.refresh),onPressed: () {setState(() {flag = !flag;});},),appBar: AppBar(title: const Text('AnimatedSwitcher'),),body: Center(child: Container(alignment: Alignment.center,width: 300,height: 220,color: Colors.orange,child: AnimatedSwitcher(//當子元素改變的時候會觸發動畫duration: const Duration(seconds: 1),child: flag? const CircularProgressIndicator(): Image.network("https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/5.jpg",fit: BoxFit.cover,))),),);}
}

Flutter 中的 CircularProgressIndicator 是一個用于顯示循環進度的預置組件。它通常用于在應用程序加載數據、執行長時間任務或執行任何需要顯示進度的操作時顯示一個圓形的進度條。

CircularProgressIndicator 可以以不同的方式進行定制,包括更改顏色、大小和動畫。它是 Flutter 框架中 Material 組件庫的一部分,因此在使用 MaterialAppScaffoldMaterial 風格的組件時很常見。

你可以通過簡單的代碼來創建一個 CircularProgressIndicator,然后將其放置在應用程序的合適位置。在加載數據或執行任務時,CircularProgressIndicator 將顯示一個圓形進度條,直到任務完成為止。

修改 AnimatedSwitcher 的動畫效果

通過 AnimatedSwitcher 中的 transitionBuilder 參數可以修改其動畫效果。

import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({super.key});Widget build(BuildContext context) {return const MaterialApp(home: MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key});State<MyHomePage> createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage> {bool flag = true;Widget build(BuildContext context) {return Scaffold(floatingActionButton: FloatingActionButton(child: const Icon(Icons.refresh),onPressed: () {setState(() {flag = !flag;});},),appBar: AppBar(title: const Text('AnimatedSwitcher'),),body: Center(child: Container(alignment: Alignment.center,width: 300,height: 220,color: Colors.orange,child: AnimatedSwitcher(transitionBuilder: ((child, animation) {//可以改變動畫效果return ScaleTransition(scale: animation,child: FadeTransition(opacity: animation,child: child,),);}),duration: const Duration(seconds: 1),//當子元素改變的時候會觸發動畫child: flag? const CircularProgressIndicator(): Image.network("https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/5.jpg",fit: BoxFit.cover,))),),);}
}

在上面的代碼中,transitionBuilder 參數是用于定義 AnimatedSwitcher 在切換子元素時所使用的過渡效果的回調函數。這個回調函數接受兩個參數:

  1. child:當前子元素(即將要切換的元素)。
  2. animation:表示當前切換的動畫。在這里,animation 是一個 Animation<double> 類型的對象,它描述了從0到1的動畫值。

transitionBuilder 回調函數應該返回一個 Widget,這個 Widget 將作為子元素在切換時的動畫效果。在上面的代碼中,回調函數使用了兩種過渡效果:

  1. ScaleTransition:它通過對子元素進行縮放來實現動畫效果,縮放的比例由動畫值 animation 控制,從而實現子元素的逐漸放大或縮小的效果。
  2. FadeTransition:它通過改變子元素的不透明度來實現淡入淡出的效果,不透明度的變化由動畫值 animation 控制,從而實現子元素的逐漸顯現或消失的效果。

通過結合使用 ScaleTransitionFadeTransition,可以實現更加豐富的過渡效果,使切換子元素時更加平滑和動態。

子組件相同的AnimatedSwitcher

我們知道,AnimatedSwitcher 只有在其子元素發生改變時才會觸發動畫,那么當子元素不變時,比如子元素為一個 Text 組件,我們只修改里面的內容,不改變組件類型,在這種情況下又該如何觸發動畫呢?

很簡單,我們只要設置一下子元素的 key 值為 UniqueKey(),就可以讓 AnimatedSwitcher 認為子元素已經改變。

import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({super.key});Widget build(BuildContext context) {return const MaterialApp(home: MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key});State<MyHomePage> createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage> {bool flag = true;Widget build(BuildContext context) {return Scaffold(floatingActionButton: FloatingActionButton(child: const Icon(Icons.refresh),onPressed: () {setState(() {flag = !flag;});},),appBar: AppBar(title: const Text('AnimatedSwitcher'),),body: Center(child: Container(alignment: Alignment.center,width: 300,height: 220,color: Colors.blue,child: AnimatedSwitcher(transitionBuilder: ((child, animation) {//可以改變動畫效果return ScaleTransition(scale: animation,child: FadeTransition(opacity: animation,child: child,),);}),duration: const Duration(seconds: 1),//當子元素改變的時候會觸發動畫child: Text(key: UniqueKey(),flag ? "凡所過往" : "皆為序章",style: Theme.of(context).textTheme.headlineLarge,)),)),);}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/17648.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/17648.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/17648.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

SpringMvc-restful設計風格

Restful 1、入門1.1 簡介1.2 實例 1、入門 1.1 簡介 RESTFul是什么 RESTFul是WEB服務接口的一種設計風格。 RESTFul定義了一組約束條件和規范&#xff0c;可以讓WEB服務接口更加簡潔、易于理解、易于擴展、安全可靠。 1.2 實例 web.xml <?xml version"1.0"…

5、xss-labs之level6

一、level6-----大小寫繞過 1、測試分析 測試了之前用過的payload&#xff0c;發現都不行&#xff0c;并且level4使用的Java偽協議也不行&#xff0c;可以得出<>、script、onclick都被過濾 2、構造payload 因為href被過濾&#xff0c;可以試一下大寫HREF 初試payload…

沒人愿意和我們最好的工程師一起工作

幾年前&#xff0c;有一位技術非常好的工程師&#xff08;我們叫他“喬恩”&#xff09;為我工作。 他的代碼寫得很好&#xff0c;代碼審查&#xff08;PRs&#xff09;也完成得很快。從技術角度來看&#xff0c;他是個出色的工程師。 但是我們從其他工程師那里得到了一些關于…

Python實現解碼二進制數據以匹配給定的C++結構體

要在Python中實現解碼二進制數據以匹配給定的C結構體Ytest&#xff0c;你需要了解每個字段在結構體中的偏移量&#xff08;由于結構體內存對齊&#xff0c;這些偏移量可能與字段的順序和大小不完全對應&#xff09;。不過&#xff0c;在沒有指定內存對齊的情況下&#xff0c;我…

使用nvm管理node多版本(安裝、卸載nvm,配置環境變量,更換npm淘寶鏡像)淘寶的鏡像域名更換

最近 使用nvm 管理 node 的時候發現nvm install node版本號 總是失敗。 nvm install 20.12.2Error retrieving "http://npm.taobao.org/mirrors/node/latest/SHASUMS256.txt": HTTP Status 404查看原因&#xff0c;因為淘寶的鏡像域名更換&#xff0c;由于 npm.taob…

2020職稱繼續教育--發揮好“顯著優勢”,堅持和完善生態文明制度體系,促進人與自然和諧共生

單選題&#xff08;共7題&#xff0c;每題5分&#xff09; 1、我國生態脆弱區廣布&#xff0c;適宜生存的空間不足&#xff08;&#xff09;。 B、三分之一 2、按照傳統文化的說法&#xff0c;制度體系與治理體系&#xff0c;是“體”和“用”的關系&#xff0c;強調&#xff…

BI系統:數據驅動的決策利器,引領企業走向智能化

在當今這個數據驅動的時代&#xff0c;商業智能&#xff08;BI&#xff09;系統已成為企業不可或缺的工具。BI系統不僅提高了數據處理和分析的效率&#xff0c;更重要的是&#xff0c;它能夠幫助企業做出更加明智和精準的決策。在數聚多年的從業經驗來看&#xff0c;BI系統的重…

基于直接二元搜索的片上偏振分束器設計 (Nature Photonics, 9, 6, (2015))案例復現

時間—2024.6.08 騰訊會議 智能算法驅動的光子學設計與應用

Dream

好像很多人夢寐以求的都是別人已經擁有的&#xff0c;多少人奮斗一生的目標&#xff0c;卻只是別人的起點&#xff0c;人生而自由&#xff0c;只是不在枷鎖之中&#xff0c;生活中沒有人不遺憾&#xff0c;只是沒有人喊疼&#xff0c;時間不會重來&#xff0c;已經過去了就讓它…

內存泄露問題? 怎么解決

內存泄漏在Android應用開發中是一個常見問題&#xff0c;它發生在對象不再被使用時&#xff0c;但仍然被引用&#xff0c;導致垃圾回收器無法釋放其占用的內存。這會逐漸消耗應用可用內存&#xff0c;最終可能導致應用運行緩慢、崩潰或被系統終止。以下是一些常見的內存泄漏場景…

vue3 使用vant

使用前提&#xff1a; vite創建的vue3項目 vanthttps://vant-ui.github.io/vant/#/zh-CN/home npm i vant 引入樣式&#xff1a; main.js import vant/lib/index.css vant封裝 import { showLoadingToast,closeToast,showDialog,showConfirmDialog } from vant;export func…

Typora圖床配置優化(PicGo-Core(command line) 插件 + gitee)

Typora圖床配置優化&#xff08;PicGo-Core(command line) 插件 gitee&#xff09; 前言 在日常使用Typora編寫markdown筆記時&#xff0c;經常需要插入圖片來幫助理解和整理邏輯。然而&#xff0c;由于圖片保存在本地&#xff0c;上傳到網上時經常出現圖片不見或錯誤警告的…

育菁桌面式數控機床助力教育裝備

桌面式數控機床是一種小型化的數控機床&#xff0c;它通常具有緊湊的設計和較小的體積&#xff0c;可以放置在桌面上進行操作。 這種車床結合了數控技術&#xff0c;通過計算機編程來控制機床的運動和加工過程&#xff0c;以實現高精度、高效率的工件加工。 桌面式數控車床是一…

如何部署一套高可用性的醫院信息管理系統?基于華為云、SpringBoot、Vue及Jenkins、Gitlab的CI/CD流程

目錄 一、項目背景 二、項目架構 三、項目部署流程 1、前端部署 2、后端部署 3、監控與運維 四、項目過程 一、項目背景 隨著醫療信息化程度的不斷加深&#xff0c;醫院信息管理系統的穩定性和可用性成為了醫療機構日常運營的關鍵。在這個數字化時代&am…

選擇快充時代下的理想充電器與電壓誘騙芯片PW6606

隨著科技的不斷進步&#xff0c;我們的電子設備對于充電速度和效率的要求越來越高。在快充技術迅猛發展的今天&#xff0c;了解不同類型的充電器及其對應的快充協議&#xff0c;以及如何選擇適合的電壓誘騙芯片&#xff0c;對于提升充電體驗和保障設備安全顯得尤為重要。 一、快…

生信網絡學院|05月31日《SOLIDWORKS Manage 產品周期管理》

課程主題&#xff1a;SOLIDWORKS Manage 產品周期管理 課程時間&#xff1a;2024年05月31日 14:00-14:30 主講人&#xff1a;付艦 生信科技 PLM實施顧問 1、SOLIDWORKS Manage介紹 2、周期流程管理 3、產品項目管理 4、項目會議管理 5、項目問題管理 安裝騰訊會議客戶端…

Android 13 VSYNC重學習

Android 13 VSYNC重學習 引言 學無止境&#xff0c;一個字干就完事&#xff01; 源碼參考基于Android 13 aosp&#xff01; 一. Android VSync模塊開胃菜 在開始正式的分析之前&#xff0c;我們先簡單對Android的Vsync模塊簡單介紹下,如下圖所示&#xff0c;其中: HW_VSync是…

【Java面試】一、Redis篇(上)

文章目錄 0、準備1、緩存穿透&#xff1a;不存在的key2、緩存擊穿&#xff1a;熱點key過期3、緩存雪崩&#xff1a;大批key同時過期4、雙寫一致性4.1 要求高一致性4.2 允許一定的一致延遲 5、面試 0、準備 Redis相關概覽&#xff1a; 以簡歷上所列的項目為切入點&#xff0c;展…

Steamdeck使用Windows系統游玩雪地奔馳時閃退問題解決方法

我非常喜歡雪地奔馳這款游戲&#xff0c;買sd的一部分也是為了它。可在我打開這個游戲時&#xff0c;游戲發生閃退問題。查閱了網絡各個途徑&#xff0c;基本沒有解決方法。因此我自己分析終于解決該問題。以下是我解決問題的思路&#xff0c;僅供記錄參考&#xff1a; 游戲在崩…

2024提升數字思維能力加快企業數字化轉型(74頁PPT)

方案介紹&#xff1a; 本報告的價值在于為企業提供了一套系統的提升數字思維能力、加快數字化轉型的理論框架和實踐指南。通過本報告的學習和應用&#xff0c;企業可以更加清晰地認識到數字化轉型的重要性和緊迫性&#xff0c;明確自身在數字化轉型中的優勢和不足&#xff0c;并…