史上最方便的Android頁面框架XPage使用指南

視頻講解

視頻鏈接 : https://www.bilibili.com/video/BV1eD4y1R73U

簡介

XPage是一個非常方便的fragment頁面框架汁政!天下武功拍霜,唯快不破,XPage最大的特點就是快,提高開發(fā)的效率!

XPage設(shè)計的初衷是希望能做一個通用的Activity作為殼,F(xiàn)ragment作為頁面填充展示,并且能夠像Activity那樣自由的切換和數(shù)據(jù)交互。

特征

  • 支持assets下“corepage.json”靜態(tài)配置Fragment頁面信息肥缔。
  • 支持Application中動態(tài)配置Fragment頁面信息。
  • 支持通過注解@Page的方式動態(tài)自動配置頁面信息摹恰。
  • 支持自定義Fragment頁面信息配置辫继。
  • 支持4種默認Fragment頁面切換動畫。
  • 支持Fragment頁面間參數(shù)傳遞俗慈。
  • 支持Fragment頁面屬性保存姑宽。
  • 支持Fragment頁面的onKeyDown、onFragmentResult等生命周期
  • 支持Fragment和Fragment頁面自由跳轉(zhuǎn)以及數(shù)據(jù)交互闺阱。
  • 支持導(dǎo)航欄通過注解的方式自動添加及設(shè)置炮车。
  • 支持進行內(nèi)存泄露監(jiān)測。
  • 支持自定義TitleBar全局主題屬性酣溃。
  • 支持自定義Fragment頁面容器瘦穆。
  • 支持自定義Activity頁面容器。
  • 支持Fragment之間赊豌、activity和fragment之間的數(shù)據(jù)交互扛或。
  • 兼容kotlin和androidx。

設(shè)計原由

當初做Android開發(fā)時每當我寫一個頁面碘饼,都需要創(chuàng)建一個Activity熙兔,并且還需要在manifest中注冊一堆Activity信息悲伶,這樣既不方便,而且對資源的開銷也比較大住涉。因此當時我就設(shè)想能否創(chuàng)造出一個通用萬能的Activity容器麸锉,可以全權(quán)負責(zé)Fragment的切換展示和數(shù)據(jù)交互,只需要一行代碼即可完成所有的操作舆声,還不需要自己手動去注冊花沉,可以一鍵生成。

設(shè)計思路

剛開始的時候真的很難媳握,沒有什么好的思路碱屁,最初只是簡單封裝了一個Activity,通過傳入一些key值從而獲取并加載對應(yīng)的fragment毙芜,類似ARouter中Fragment發(fā)現(xiàn)那種忽媒。其實這樣做并沒有解決一個容器的問題,而且頁面切換也不是很靈活腋粥,不夠通用,使用起來也不是很方便架曹。

突然有一天我發(fā)現(xiàn)Github上有個開源項目CorePage寫得非常好隘冲,完美地解決了我對一個Activity容器的問題,于是我決定仔細研究其代碼绑雄,并在其基礎(chǔ)上設(shè)計出了XPage的最初版本展辞。

就在XPage正式投入使用的過程中,我發(fā)現(xiàn)還是存在不少問題的:

  • 1.對外API不夠靈活万牺,使用起來不夠方便罗珍;

  • 2.每個Fragment仍需要手動注冊,很麻煩脚粟;

對于API不夠靈活的問題覆旱,我在之后的版本中陸續(xù)通過構(gòu)造者模式設(shè)計以及Android主題屬性等手段解決了。

而對于手動注冊的問題核无,我正是借鑒了ARouter的思路扣唱,通過Android APT技術(shù),從而實現(xiàn)了Fragment信息的自動注冊团南。

解決痛點

  • 只需要一個Activity容器就可以實現(xiàn)多個頁面的交互噪沙。

  • Fragment自由切換和數(shù)據(jù)交互。

  • 無需在manifest中注冊一堆Activity信息吐根,通過@Page注解一鍵自動注冊正歼。


集成指南

添加Gradle依賴

1.在項目根目錄的build.gradle的 repositories 添加jitpack倉庫

allprojects {
     repositories {
        ...
        maven { url "https://jitpack.io" }
    }
}

2.在dependencies添加引用

以下是版本說明,選擇一個即可拷橘。

  • androidx版本:3.0.0及以上
dependencies {
  ...
  // XPage
  implementation 'com.github.xuexiangjys.XPage:xpage-lib:3.0.0'
  annotationProcessor 'com.github.xuexiangjys.XPage:xpage-compiler:3.0.0'
  // ButterKnife的sdk
  implementation 'com.jakewharton:butterknife:10.1.0'
  annotationProcessor 'com.jakewharton:butterknife-compiler:10.1.0'
}
  • support版本:2.3.0及以下
dependencies {
  ...
  // XPage
  implementation 'com.github.xuexiangjys.XPage:xpage-lib:2.3.0'
  annotationProcessor 'com.github.xuexiangjys.XPage:xpage-compiler:2.3.0'
  // ButterKnife的sdk
  implementation 'com.jakewharton:butterknife:8.4.0'
  annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0'
}

【注意】如果你使用的是kotlin局义,請使用如下配置:

apply plugin: 'kotlin-kapt'

dependencies {
  ...
  //XPage
  implementation 'com.github.xuexiangjys.XPage:xpage-lib:3.0.0'
  kapt 'com.github.xuexiangjys.XPage:xpage-compiler:3.0.0'
  //ButterKnife的sdk
  implementation 'com.jakewharton:butterknife:10.1.0'
  kapt 'com.jakewharton:butterknife-compiler:10.1.0'
}

3.進行moduleName注冊(非必要)

defaultConfig {
    ...

    javaCompileOptions {
        annotationProcessorOptions {
            arguments = [ moduleName : project.getName() ]
        }
    }
}

【注意】:如果不注冊的話齐疙,默認ModuleName為app


頁面注冊

方法一:Application中動態(tài)注冊【推薦】

1.自動進行頁面注冊【推薦】

使用apt編譯時自動生成的頁面注冊配置類 "moduleName"+PageConfig 的getPages()進行注冊旭咽。

PageConfig.getInstance()
        .setPageConfiguration(new PageConfiguration() { //頁面注冊
            @Override
            public List<PageInfo> registerPages(Context context) {
                //自動注冊頁面,是編譯時自動生成的贞奋,build一下就出來了。如果你還沒使用@Page的話穷绵,暫時是不會生成的轿塔。
                return AppPageConfig.getInstance().getPages(); //自動注冊頁面
            }
        })
        .debug("PageLog")       //開啟調(diào)試
        .setContainActivityClazz(XPageActivity.class) //設(shè)置默認的容器Activity
        .enableWatcher(false)   //設(shè)置是否開啟內(nèi)存泄露監(jiān)測
        .init(this);            //初始化頁面配置

【注意】:如果你的項目中只是增加了依賴,還沒有使用@Page注解XPageFragment頁面的話仲墨,在編譯時是不會自動生成注冊頁面的9寸浴!

2.手動動態(tài)進行頁面注冊

PageConfig.getInstance()
        .setPageConfiguration(new PageConfiguration() { //頁面注冊
            @Override
            public List<PageInfo> registerPages(Context context) {
                List<PageInfo> pageInfos = new ArrayList<>();
                addPageInfoAndSubPages(pageInfos, MainFragment.class);
                pageInfos.add(PageConfig.getPageInfo(DateReceiveFragment.class));
                return pageInfos;        //手動注冊頁面
            }
        })
        .debug("PageLog")       //開啟調(diào)試
        .enableWatcher(false)   //設(shè)置是否開啟內(nèi)存泄露監(jiān)測
        .init(this);            //初始化頁面配置

方法二:assets中靜態(tài)注冊

在assets文件夾中新建“corepage.json“目养,然后進行如下配置:

[
  {
    "name": "測試頁面1",
    "classPath": "com.xuexiang.xpagedemo.fragment.TestFragment1",
    "params": ""
  },
  {
    "name": "測試頁面2",
    "classPath": "com.xuexiang.xpagedemo.fragment.TestFragment2",
    "params": {
      "key1":"這是參數(shù)1的值",
      "key2":"這是參數(shù)2的值"
    }
  }俩由,
]

混淆配置

# fastjson
-dontwarn com.alibaba.fastjson.**
-keep class com.alibaba.fastjson.** { *; }
-keepattributes Signature

# xpage
-keep class com.xuexiang.xpage.annotation.** { *; }

基礎(chǔ)使用

頁面跳轉(zhuǎn)

使用XPage,Activity必須要繼承XPageActivity,Fragment必須要繼承XPageFragment癌蚁,否則將無法調(diào)用頁面跳轉(zhuǎn)的openPage方法幻梯。

頁面的簡單打開和關(guān)閉

  • 使用openPage即可打開頁面,入?yún)⒖蔀樘D(zhuǎn)類的類名努释,也可以是類的標識(@Page標注的name字段)
// 使用類名打開
openPage(TestFragment.class);
// 使用標識打開
openPage("TestFragment");
  • 使用popToBack即可關(guān)閉頁面碘梢。
// 關(guān)閉當前頁,返回上一頁
popToBack();
// 關(guān)閉當前頁并跳轉(zhuǎn)至某個頁面
popToBack("popBackName", null);

頁面打開等待結(jié)果返回

  • 1.使用openPageForResult即可伐蒂,類似Activity里的startActivityForResult煞躬。
openPageForResult(TestFragment.class, null, REQUEST_CODE);
  • 2.使用setFragmentResult來設(shè)置頁面關(guān)閉的返回值,類似Activity里的setResult方法逸邦。
// 設(shè)置返回的數(shù)據(jù)恩沛,類似Activity里的setResult
Intent intent = new Intent();
intent.putExtra(KEY_BACK_DATA, "==【返回的數(shù)據(jù)】==");
setFragmentResult(500, intent);
// 返回操作
popToBack();
  • 3.重寫Fragment的onFragmentResult方法來接收返回的數(shù)據(jù),類似Activity里的onActivityResult方法缕减。
@Override
public void onFragmentResult(int requestCode, int resultCode, Intent data) {
    super.onFragmentResult(requestCode, resultCode, data);
    if (data != null) {
        Bundle extras = data.getExtras();
        XToastUtils.toast("requestCode:" + requestCode + " resultCode:" + resultCode + " data:" + extras.getString(TestFragment.KEY_BACK_DATA));
    }
}

數(shù)據(jù)傳遞

  • 使用openPage打開頁面時雷客,可傳入Bundle作為參數(shù)。
// 設(shè)置需要傳遞的參數(shù)
Bundle params = new Bundle();
params.putBoolean(DateReceiveFragment.KEY_IS_NEED_BACK, false);
int id = (int) (Math.random() * 100);
params.putString(DateReceiveFragment.KEY_EVENT_NAME, "事件" + id);
params.putString(DateReceiveFragment.KEY_EVENT_DATA, "事件" + id + "攜帶的數(shù)據(jù)");
// 把參數(shù)傳入
openPage(DateReceiveFragment.class, params);
  • 數(shù)據(jù)接收

數(shù)據(jù)接收和普通Fragment接收數(shù)據(jù)一樣烛卧,使用getArguments獲取傳入的數(shù)據(jù)佛纫。

Bundle arguments = getArguments();
String eventName = arguments.getString(DateReceiveFragment.KEY_EVENT_NAME);
String eventData = arguments.getString(DateReceiveFragment.KEY_EVENT_DATA);

頁面轉(zhuǎn)場動畫

頁面轉(zhuǎn)場動畫可以動態(tài)設(shè)置,也可以靜態(tài)設(shè)置总放。

靜態(tài)設(shè)置

在我們使用@Page進行頁面注冊的時候呈宇,我們可以靜態(tài)設(shè)置轉(zhuǎn)場動畫、默認參數(shù)局雄、拓展字段等甥啄。

Page注解的屬性表:

屬性名 類型 默認值 備注
name String 注解類的類名 頁面的名稱、唯一標識符
params String[] {""} 靜態(tài)設(shè)置默認參數(shù)
anim CoreAnim CoreAnim.slide 頁面轉(zhuǎn)場動畫
extra int -1 拓展字段

動態(tài)設(shè)置

使用openPage打開頁面時炬搭,可傳入CoreAnim枚舉設(shè)置頁面轉(zhuǎn)場動畫蜈漓。

switch(position) {
    case 0:
        openPage(TestFragment.PAGE_NAME, null, CoreAnim.none);//沒有動畫
        break;
    case 1:
        openPage(TestFragment.PAGE_NAME, null, CoreAnim.present);//由下到上動畫
        break;
    case 2:
        openPage(TestFragment.PAGE_NAME, null, CoreAnim.slide);//從左到右動畫
        break;
    case 3:
        openPage(TestFragment.PAGE_NAME, null, CoreAnim.fade);//漸變
        break;
    case 4:
        openPage(TestFragment.PAGE_NAME, null, CoreAnim.zoom);//放大
        break;
    default:
        break;
}

設(shè)置自定義轉(zhuǎn)場動畫

//自定義動畫
openPage(TestFragment.PAGE_NAME, null, new int[]{
        // OpenEnterAnimation, 頁面打開進場動畫
        R.anim.custom_open_enter,
        // OpenExitAnimation, 頁面打開退場動畫
        R.anim.custom_open_exit,

        // CloseEnterAnimation, 頁面關(guān)閉進場動畫
        R.anim.custom_close_enter,
        // CloseExitAnimation, 頁面關(guān)閉退場動畫
        R.anim.custom_close_exit
});

進階使用

使用PageOption進行頁面操作【推薦】

使用PageOption.to進行頁面選項構(gòu)建穆桂。

  • setAnim: 設(shè)置頁面轉(zhuǎn)場動畫
  • setRequestCode: 設(shè)置頁面打開的請求碼,用于返回結(jié)果
  • setAddToBackStack: 設(shè)置是否加入堆棧
  • setNewActivity: 設(shè)置是否使用新的Activity打開
  • setContainActivityClazz:設(shè)置新打開Activity的容器
  • putBoolean融虽、putString享完、putAll等:設(shè)置傳遞的參數(shù)
  • open:打開頁面進行跳轉(zhuǎn)
PageOption.to(TestFragment.class) //跳轉(zhuǎn)的fragment
    .setAnim(CoreAnim.zoom) //頁面轉(zhuǎn)場動畫
    .setRequestCode(100) //請求碼,用于返回結(jié)果
    .setAddToBackStack(true) //是否加入堆棧
    .setNewActivity(true, ContainActivity.class) //是否使用新的Activity打開
    .putBoolean(DateReceiveFragment.KEY_IS_NEED_BACK, true) //傳遞的參數(shù)
    .open(this); //打開頁面進行跳轉(zhuǎn)

自定義TitleBar樣式

可以設(shè)置XPageTitleBarStyle主題樣式來自定義標題欄的默認樣式有额。

<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/xpage_default_actionbar_color</item>
    <item name="colorPrimaryDark">@color/xpage_default_actionbar_color</item>
    <item name="colorAccent">@color/xpage_default_actionbar_color</item>

    <!--標題欄的背景圖片般又,優(yōu)先使用背景圖片,沒有背景圖片才使用背景顏色巍佑,可選-->
    <item name="xpage_actionbar_background">@null</item>
    <!--標題欄的背景顏色-->
    <item name="xpage_actionbar_color">@color/xpage_default_actionbar_color</item>
    <!--是否支持沉浸式標題欄, 默認false-->
    <item name="xpage_actionbar_immersive">false</item>
    <!--標題欄返回箭頭, 默認R.drawable.xpage_ic_navigation_back_white-->
    <item name="xpage_actionbar_navigation_back">@drawable/xpage_ic_navigation_back_white</item>
    <!--標題欄的高度茴迁,默認52dp-->
    <item name="xpage_actionbar_height">60dp</item>
    <!--標題欄標題文字的大小,默認18sp-->
    <item name="xpage_actionbar_title_text_size">21sp</item>
    <!--標題欄副標題文字的大小萤衰,默認12sp-->
    <item name="xpage_actionbar_sub_text_size">14sp</item>
    <!--標題欄動作文字的大小堕义,默認15sp-->
    <item name="xpage_actionbar_action_text_size">18sp</item>
    <!--標題欄動作圖片的padding,默認5dp-->
    <item name="xpage_actionbar_action_padding">6dp</item>
    <!--標題欄兩側(cè)文字的padding脆栋,默認14dp-->
    <item name="xpage_actionbar_side_text_padding">16dp</item>
    
    <item name="XPageTitleBarStyle">@style/XPageTitleBar.Custom</item>
</style>

<style name="XPageTitleBar.Custom">
    <item name="tb_immersive">false</item>
    <item name="tb_centerGravity">center</item>
</style>

利用XPage來寫程序的Tab主頁

詳細可參見BottomNavigationViewFragment

就像正常使用ViewPager加載Fragment那樣倦卖。但是這里需要注意的兩點是:

  • 由于使用ViewPager進行加載,而非XPage,因此Fragment的initTitleBar方法需要被覆蓋筹吐。
@Override
protected TitleBar initTitleBar() {
    //不使用@Page標注的一定要注意覆蓋這個方法
    return null;
}
  • 由于為了新開頁面不影響Tab主頁當前容器的狀態(tài)糖耸,需要在打開新頁面的使用設(shè)置使用新容器。
PageOption.to(TestFragment.class)
        //新建一個容器丘薛,以不影響當前容器
        .setNewActivity(true)
        .open(this);

復(fù)雜Activity界面容器的自定義

詳細可參見ComplexActivity

1.自定義頁面容器的布局,在布局中一定要包含idfragment_container邦危。

<FrameLayout
    android:id="@id/fragment_container"
    android:layout_width="match_parent"
    android:layout_height="400dp">
</FrameLayout>

2.在XPageActivity中設(shè)置頁面容器的布局ID

@Override
protected int getLayoutId() {
    return R.layout.activity_complex;
}

3.使用changePage方法切換Fragment洋侨。

changePage(TestFragment.PAGE_NAME, null, CoreAnim.none);

【注意】在切換Fragment的時候,fragment并不會走onResume和onPause生命周期倦蚪,建議使用onHiddenChanged代替希坚。

4.使用getPage方法獲取指定的Fragment,就可以獲取該fragment頁面中的數(shù)據(jù)陵且。

TabAFragment tabAFragment = getPage(TabAFragment.class);
if (tabAFragment != null) {
    ToastUtils.toast(tabAFragment.getData());
} else {
    ToastUtils.toast("頁面還未加載裁僧!");
}

常見問題

1.問:我使用的是自動注冊,為什么我剛接入的時候慕购,一直報錯找不到AppPageConfig?

答:首先需要明確的是聊疲,AppPageConfig是需要編譯之后才會出現(xiàn)的,如果你沒有編譯的話沪悲,是肯定沒有的获洲。如果你編譯了還是找不到,你可以根據(jù)如下步驟依次進行排查:

  • 排查當前項目中是否有Fragment被@Page注解了殿如,如果沒有的話贡珊,即使編譯了也是不會生成AppPageConfig文件的最爬。
  • 排查是否進行了moduleName注冊,因為自動生成的注冊類是根據(jù)"moduleName"+PageConfig的規(guī)則進行自動生成的门岔,如果沒有配置moduleName的話爱致,默認才是app,這樣自動生成的注冊類才是AppPageConfig寒随。如果你配置了moduleName糠悯,而且模塊的名稱也不是app,那么自動生成的注冊類肯定不是AppPageConfig牢裳。
  • 查看編譯時是否有其他報錯逢防,如果在編譯的過程中就報錯了,那么作為apt這種編譯時自動生成的注冊類也是無法生成的蒲讯。
  • 如果以上都沒能解決你的問題忘朝,那么考慮八成是你哪里集成出錯了,所以需要你回頭重新閱讀集成指南判帮,不能放過每一個細節(jié)局嘁。如果還是不行,考慮直接使用簡化版的Android空殼模版工程 先熟悉一下集成和使用晦墙。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末悦昵,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子晌畅,更是在濱河造成了極大的恐慌但指,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件抗楔,死亡現(xiàn)場離奇詭異棋凳,居然都是意外死亡,警方通過查閱死者的電腦和手機连躏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進店門剩岳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人入热,你說我怎么就攤上這事拍棕。” “怎么了勺良?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵绰播,是天一觀的道長。 經(jīng)常有香客問我郑气,道長幅垮,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮忙芒,結(jié)果婚禮上示弓,老公的妹妹穿的比我還像新娘。我一直安慰自己呵萨,他們只是感情好奏属,可當我...
    茶點故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著潮峦,像睡著了一般囱皿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上忱嘹,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天嘱腥,我揣著相機與錄音,去河邊找鬼拘悦。 笑死齿兔,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的础米。 我是一名探鬼主播分苇,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼屁桑!你這毒婦竟也來了医寿?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤蘑斧,失蹤者是張志新(化名)和其女友劉穎靖秩,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體竖瘾,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡盆偿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了准浴。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,688評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡捎稚,死狀恐怖乐横,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情今野,我是刑警寧澤葡公,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站条霜,受9級特大地震影響催什,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜宰睡,卻給世界環(huán)境...
    茶點故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一蒲凶、第九天 我趴在偏房一處隱蔽的房頂上張望气筋。 院中可真熱鬧,春花似錦旋圆、人聲如沸宠默。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽搀矫。三九已至,卻和暖如春刻肄,著一層夾襖步出監(jiān)牢的瞬間瓤球,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工敏弃, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留卦羡,地道東北人。 一個月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓权她,卻偏偏與公主長得像虹茶,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子隅要,可洞房花燭夜當晚...
    茶點故事閱讀 44,573評論 2 353