摺疊屏上應用設計規範,瞭解一下

Android開發者發表於2022-02-14

Android 的覆蓋範圍在遞增,體驗也在變得越來越好,現已有超過 2.5 億臺大屏裝置搭載了 Android 系統,包括平板電腦、可摺疊裝置以及 Chrome OS 裝置。如何適配不同的螢幕尺寸並保障良好的體驗,一直以來都是開發者的一大難題。尤其隨著可摺疊裝置等新興產品的湧現,適配工作也愈發迫切。本文將重點介紹 Material Design 指南中更新的相關內容,並提供一些建議來幫助開發者按照自適應介面的原則來構建應用,從而解決在平板電腦和可摺疊裝置上的適配問題。

本文將重點討論 View 系統中的適配,如需瞭解更多有關如何利用 Compose 構建大螢幕應用的資訊,請參閱文章《為任意螢幕尺寸構建 Android 介面》。

如果您更喜歡通過視訊瞭解本文內容,請點選下方:

https://www.bilibili.com/vide...

△ 摺疊屏上應用設計規範

設計指南

2021 年年初,我們在 Material Design 網站上釋出了 針對大屏裝置的指南文件Android 開發者峰會 期間我們更新了一些內容,以幫助開發者為可摺疊裝置等更多其他型別的裝置做好準備。

深入理解佈局

深入理解佈局指南 介紹了佈局容器的相關概念,它提供了一個整體框架,可幫助開發者思考如何在螢幕上排列導航欄、工具欄和內容等介面元素。

△ 佈局的三個主要區域

△ 佈局的三個主要區域

指南中的 組合部分 帶您瞭解如何充分利用螢幕空間以保障可讀性,並且以尊重使用者心智模型的方式在不同的場景下合理排布重要內容和操作選項。包括適當縮放以展示更多內容,如示例中的副標題和日期,以及較小的組合技術,例如在緊湊型的佈局中對內容進行視覺分組並保持其相關性等。

△ 組合指南中涉及的部分佈局方式

△ 組合指南中涉及的部分佈局方式

以 Fortnightly 示例應用為例,它在平板電腦上的介面佈局十分均衡,這得益於它遵從了指南里對容器的建議。而且可以看到,Fortnightly 使用了視覺分隔線 (Visual Divider) 用於分隔最新新聞,在螢幕的另一邊,則利用留白和排版對不同類別的新聞報導進行分組。

△ Fortnightly 遵循指南對內容進行分隔和分組

△ Fortnightly 遵循指南對內容進行分隔和分組

網格系統

現在,許多應用將螢幕視作一個大畫布或單欄,以水平和垂直的方式按相互關係繪製元素,有些應用也會在一側整體留出邊距。這一做法在小屏上或許行得通,當螢幕尺寸較大時就會出現明顯的問題。網格系統則將您的佈局劃分為一系列欄,從而幫助您在規範網格中設計更具表現力的佈局。在佈局中使用欄式網格 (如下圖),能夠讓大屏裝置的體驗呈現更貼心,更組織有序的印象,使得裝置和內容更自然地融為一體。

△ 欄式網格

△ 欄式網格

您可以通過這些欄將螢幕劃分為不同區域,用於容納相關的資訊和操作,進而改善資訊層次結構。如下圖所示,這裡分了三個區域,這些區域將按照設計者期望使用者閱讀的順序,把使用者的注意力吸引到這些區域對應在螢幕的主要資訊片段或資訊組上。最重要的一點是,欄式網格提供了一種合理的方式來思考當螢幕尺寸變大或變小時如何將內容進行重排,從而幫助您對不同的螢幕尺寸作出一致響應。

△ 使用欄式網格將螢幕劃分為三個主要區域

△ 使用欄式網格將螢幕劃分為三個主要區域

在本例中,三個主要區域通過重排來保持相同的資訊層次結構,但以更加人性化的方式在小螢幕上顯示。

△ 使用欄式網格在不同螢幕尺寸中對內容進行重排

△ 使用欄式網格在不同螢幕尺寸中對內容進行重排

記住網格系統有助於您選擇元件行為,在不同的佈局中,以對裝置尺寸和場景最有意義的方式決定替換還是更改元件。例如,在大屏裝置上,您可使用 Navigation rail (左側邊欄導航條) 代替 底部導航 (Bottom navigation),兩者功能相同,視覺表現方式也類似,但 Navigation rail 能夠更加人性化地排布頁面。手機上的全屏對話方塊 (Full-screen dialog) 在大螢幕上可以採用簡單對話方塊 (Simple dialog) 替代,以保持使用者當前操作的上下文。

△ 在大屏上使用簡單對話方塊 (右) 代替全屏對話方塊 (左)

△ 在大屏上使用簡單對話方塊 (右) 代替全屏對話方塊 (左)

尺寸類別

請記住,替換元件時,首先要滿足使用者的功能性和人性化需求。找到調整介面的正確閾值,這是實現響應式介面的重要步驟。因此我們定義了新斷點值,這有助於將裝置劃分到預設的尺寸類別中,這些尺寸代表了市場上實際裝置的尺寸。它們有助於將應用版面的原始尺寸轉換為離散的標準化組,您可以據此做出更高層次的介面決策。例如,幾乎所有標準手機在豎屏模式下都採用了較小 (Compact) 寬度和中等 (Medium) 高度的組合,由於普遍使用垂直滾動,對大多數應用而言,根據寬度的尺寸類別進行適配就已足夠。

△ 基於寬度的尺寸類別

△ 基於寬度的尺寸類別

△ 基於高度的尺寸類

△ 基於高度的尺寸類

這些 尺寸類 將作為新的 API 出現在 1.1 版 Jetpack Window Manager 庫中。從 Android Studio Bumblebee 開始,我們還以參考裝置 (Reference devices) 的形式,將尺寸類別整合到工具中,在此基礎上實現介面有利於保持一致性,操作也更加簡單。而且開發者不需要去檢查實際物理尺寸或螢幕方向,或其他容易出錯的標識。您在設計和構建不同的尺寸類別時,請想想人們會如何手持和觸控這些類別所代表的裝置。關注裝置的形狀和尺寸,有助於您打造出更加人性化的體驗。例如,在平板電腦或大屏手機上,如果不完全調整握持姿勢,人們可能很難觸及螢幕的頂部區域,因此請將重要操作和內容放在容易觸及的區域中。

規範佈局

規範佈局提供了一系列通用佈局方案,對設計大螢幕應用非常有幫助。第一種是列表/詳情,或列表網格檢視的簡單組合,同時在開始展示內容的螢幕起始側,設定/不設定導航容器。

△ 列表/詳情佈局

△ 列表/詳情佈局

支援皮膚可用於人們需要集中精力的體驗中,例如文件。在螢幕尾側或底部新增一塊皮膚,以便於使用工具或上下文控制元件。

△ 支援皮膚

△ 支援皮膚

資訊流是新聞或社交類應用中的常見模式,模板採用圖塊 (Tile) 的形式來吸引使用者發現更多內容。這種互動與移動手機一樣——開啟一項即表示開啟一個新頁面,但這種體驗更具沉浸感,而且專為大螢幕尺寸而設計。

△ 資訊流

△ 資訊流

主頁橫幅優先將內容排列在螢幕頂部,並在內容周圍和下方設計了支援元素,這對以媒體為中心的應用來說,是非常棒的體驗。

△ 主頁橫幅

△ 主頁橫幅

規範佈局實踐

採用響應式介面不僅僅是為不同螢幕尺寸提供並行結構,應用還要足夠靈活,這樣才能根據各種需要調整尺寸,例如旋轉裝置、多視窗模式以及摺疊和非摺疊姿態。因此在執行期間,應用可從一個尺寸類別過渡到另一個尺寸類別,並再次過渡回去。重要的是,不要將尺寸類別視作完全獨立的桶,應用也需保證連續性 (即不中斷使用者體驗),所以應用狀態或資料不能丟失。

△ 響應式介面可根據螢幕尺寸變化而調整內容佈局

△ 響應式介面可根據螢幕尺寸變化而調整內容佈局

設想一下,當您調整瀏覽器視窗大小時,如果瀏覽器回退了一個頁面,或者重定向到另一個頁面,又或者修改了歷史記錄,這種體驗非常奇怪。因此,每個頁面都應足夠靈活,而且應當能夠在尺寸過渡期間保持狀態不變,這個時候規範佈局就能發揮重要作用。針對每個頁面,您可以思考一下,當螢幕尺寸變大時,可以新增什麼內容。當螢幕尺寸變小時,可以刪除哪些內容。然後再選擇合適的策略。這可能意味著您需要重新審視導航圖,尤其是當您目前的設計以手機為主時更應如此。

如需構建響應式介面,我們應該優先考慮介面中長駐元素的位置,例如導航元素。遵循 Material 指南,我們可以根據寬度的尺寸類別提供替代佈局,將導航調整到最方便使用的位置。例如,小螢幕採用底部導航檢視,中等螢幕採用 Navigation rail,大螢幕採用完整導航檢視。請大家注意,這些佈局採用的是寬度限定符 "-w",而非最小寬度限定符 "-sw"。剩餘空間用於排列內容,我們可以在這些空間應用規範佈局。

列表/詳情

對列表/詳情而言,AndroidX 中有個名為 SlidingPaneLayout 的專用控制元件,使用前需為它的兩個子元素指定 layout_width,在執行期間,SlidingPaneLayout 會判斷是否有足夠空間同時展示兩個窗格:

<SlidingPaneLayout …>
      <FragmentCOntainerView
              android : id=”@+id/list_pane”
              android : layout_width=”300dp”
              android : layout_weight=”1”
              …  />

      <FragmentCOntainerView
              android : id=”@+id/detail_pane”
              android : layout_width=”360dp”
              android : layout_weight=”2”

<SlidingPaneLayout …>

△ SlidingPaneLayout 佈局示例

當螢幕空間足夠,則兩個窗格至少都要達到指定的寬度,剩餘空間可通過 layout_weight 分配,如左圖所示;如果空間不足,如右圖所示,則每個窗格都使用父檢視的全寬,詳情窗格將被滑到一邊,或直接覆蓋第一個窗格。

△ SlidingPaneLayout 中空間分配結果

△ SlidingPaneLayout 中空間分配結果

viewModel.selectedItemFlow.collect { item ->
// 更新詳情窗格的內容
detailPane.showItem(item)
// 將詳細資訊窗格滑動到檢視中
// 如果並排放置兩個窗格
// 並不會產生實際效果
slidingPaneLayout.openPane()
}

如上程式碼所示,您可以通過程式碼控制滑動窗格,當使用者從列表中選擇一個專案,我們從 ViewModel 的 Kotlin 流中接收到該專案,然後更新詳情窗格的內容,並通過呼叫 openPane 將其滑入檢視。在 Trackr 應用 中效果如下圖所示:

關於如何使用 SlidingPaneLayout 實現雙窗格佈局的相關內容,請參閱 Android 開發者網站: 建立雙窗格佈局,該頁面還介紹了其他內容,例如整合系統返回按鈕以實現側滑回退窗格等。

資訊流

我們可以通過資訊流沉浸式地展示一個資料集,因此 RecyclerView 是非常適合的選擇,我們可以通過改變 RecyclerView 使用的 LayoutManager 來改變其展現形式。LinearLayoutManager 適合用於較小型寬度,但在中等寬度和展開型寬度場景下,頁面內容則會出現過度拉伸和變形的情況,這時改用 GridLayoutManager,或 StaggeredGridLayoutManager 甚至 FlexBoxLayoutManager,可能會更合適。

△ 通過更換 RecyclerView 的 LayoutManager 來改變其展現形式

△ 通過更換 RecyclerView 的 LayoutManager 來改變其展現形式

主頁橫幅

我們還可以改變單項佈局,使某些項比其他項更高或更寬,以此凸顯其重要性,打造更有趣的視覺效果。在主頁橫幅佈局中,我們強調某個特定元素,重新排布它周圍的其他支援元素。當然我們有很多方法可以實現這一點,但 ConstraintLayout 的靈活性最大,因為它提供了很多種方式來約束子元素的尺寸,以及相對於其他子元素的位置。在如下媒體類示例應用,它的首圖限制在 16:9 的寬高比內,描述窗格佔 60% 寬度,剩餘空間留給其他元素。約束條件可以改變甚至還可以用 MotionLayout 設定動畫,它是一個特殊的 ConstraintLayout

△ 主頁橫幅示例

△ 主頁橫幅示例

對於支援皮膚而言,從 LinearLayoutConstraintLayout 的任何佈局控制元件,都可以當作容器來定位皮膚。如下圖所示,我們考慮一件事,當過渡到小螢幕尺寸時,皮膚上的內容應該放在哪裡。我們有許多可選方案,比如使用螢幕尾側的側邊抽屜式導航欄,或者使用上滑式底部動作條,或者使用選項選單,甚至可以將內容完全隱藏起來。

適配可摺疊裝置

可摺疊裝置不僅配備了更大的螢幕,它們還可以根據裝置的摺疊方式和使用者的使用方式調整裝置的方向/姿勢。

目前有三種常見的裝置形態: 摺疊、未摺疊和桌面模式 (懸停)。另外,我們稍後也將看到其他理論上存在的狀態,例如書本模式。

△ 摺疊裝置的三種常見姿態

△ 摺疊裝置的三種常見姿態

與其他大螢幕裝置一樣,我們需要多想想使用者會怎樣握持未摺疊裝置?如平板電腦,部分螢幕區域難以用大拇指觸及,使用者也很難騰出整隻手來自由操控螢幕。使用者輕易就能觸及螢幕的底部角落,但可能無法觸及螢幕最頂端,尤其是在豎屏模式下。這意味著如果您使用 Navigation rail 這類元件,將導航按鈕居中或固定在螢幕底部,這會更便於使用者的操作。

△ 大屏裝置中的使用者操作熱區

△ 大屏裝置中的使用者操作熱區

同時,我們還需要考慮鉸鏈位置對互動的影響。鉸鏈會帶來明顯的觸覺差異,甚至兩個螢幕會存在物理分離。因此,請您避免將按鈕和其他重要操作項直接放在鉸鏈區域。大多數裝置上的鉸鏈區域寬度約為 48 dp,在桌面模式下也請避免將介面元素放在鉸鏈區域,因為在這種裝置模式下,使用者幾乎無法使用該區域的任何功能。

△ 鉸鏈區域

△ 鉸鏈區域

當裝置從摺疊模式轉換到非摺疊模式時,有兩種主要的技術方案可用於設計佈局。第一種是擴大螢幕,該方案採用了一種簡單的響應式佈局,在該佈局下應用會擴充套件內容並填充到螢幕上。通常情況下,我們會根據前面提到的 Material 指南 來擴充套件欄式網格。

第二種是增加另一個頁面,根據您構建的應用不同,可以採用與列表/詳情或者以另一個皮膚補充主皮膚功能相同的方案。

△ 情境 1: 擴大螢幕 (圖左) 情境 2: 增加頁面 (圖右)

△ 情境 1: 擴大螢幕 (圖左) 情境 2: 增加頁面 (圖右)

在這兩種情況下,根據 material.io 的指南,您需要建立一個平均分佈在鉸鏈區域兩側的八欄網格,當新增 Navigation rail 等導航容器時,螢幕起始側會被壓縮以容納導航容器。

△ 平均分佈在鉸鏈兩側的八欄網格 (藍背景)

△ 平均分佈在鉸鏈兩側的八欄網格 (藍背景)

適配示例

現在我們來看如何在執行期間利用好摺疊狀態。Jetpack Window Manager 庫提供了相應的 API,可以檢測應用視窗是否存在摺疊。任何 Activity 都可以獲得一個 WindowInfoRepository 例項。然後,在 Started 和 Stopped 這兩種生命週期狀態之間,我們可以安全地從視窗布局資訊流中收集資訊。每當流發射一個值時,我們都可以檢查 displayFeature,然後有針對性地尋找 FoldingFeature。

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val windowInfoRepo = windowInfoRepository()

        // 在 STARTED 和 STOPPED 這兩種生命週期狀態之間安全地從 windowInfoRepo 中收集資料
        lifecycleScope.launch(Dispatchers.Main) {
            lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
                windowInfoRepo.windowLayoutInfo.collect { info ->
                    for (feature in info.displayFeatures) {
                        val fold = feature as? FoldingFeature ?: continue
                        // 使用 FoldingFeature
                    }
                }
            }
        }
    }

△ 識別摺疊姿態

掌握了摺疊姿態的相關資訊後,我們可以通過一些方法來檢視裝置是否處於前面提及的某種姿態。在書本模式下,裝置的狀態為 HALF_OPENED,且其方向為 VERTICAL;在桌面模式下的狀態為 HALF_OPENED,且其方向為 HORIZONTAL

// 書本模式是半開啟的垂直摺疊模式
fun FoldingFeature.isBookMode() =
    state == FoldingFeature.State.HALF_OPENED &&
        orientation == FoldingFeature.Orientation.VERTICAL

// 桌面模式是半開啟的水平摺疊模式
fun FoldingFeature.isTableTopMode() =
    state == FoldingFeature.State.HALF_OPENED &&
        orientation == FoldingFeature.Orientation.HORIZONTAL

△ 書本模式於桌面模式的判定條件

FoldingFeature 中還包含視窗中的摺疊位置,當摺疊導致內容檢視被割裂時,我們應該及時更新佈局引數。您可以做些調整,比如將支援皮膚置於一側,或者在摺疊的上半部分展示主頁橫幅。首先,我們需要知道內容檢視在視窗中的位置,通過 getLocationInWindow 可以獲取位置資訊。我們將使用這些座標以及寬度和高度建立一個 Rect 物件,這樣我們便得到了視窗座標空間中的檢視邊界。

FoldingFeature 給出了在視窗的座標空間中的摺疊邊界,因此我們可以直接檢查這兩個區域是否相交,如果相交,我們可以將 featureRect 的邊界轉換為檢視的座標空間並將其返回。順便說一下,如果您使用 SlidingPaneLayout 來實現列表/詳情佈局,您會自動獲得對書本模式的支援。只要兩個窗格都能容納進去,SlidingPaneLayout 會將窗格置於摺疊姿態的另一側。

fun getFoldBoundsInView(
       foldingFeature: FoldingFeature,
       view: View
): Rect? {
       // 獲取檢視在視窗座標空間中的邊界
       val viewLocation = IntArray(2)
       view.getLocationInWindow(viewLocation)

       val (viewX, viewY) = viewLocation
       val viewRect = Rect(
           left = viewX, top = viewY
           right = viewX + view.width, bottom = view + view.height
       )
    …

       //顯示功能的邊界已經在視窗的座標空間中
       // 檢查 view 的邊界和顯示功能的邊界是否相交
       val featureRect = Rect(foldingFeature.bounds)
       val intersects = featureRect. intersect (viewRect)

       if (featureRect.isEmpty || ! intersects)
           return null
       }

       // 將功能的邊界座標轉換為 view 的座標空間
       featureRect.offset(-viewX, -viewY)
       return featureRect
}

△ 獲取摺疊的位置資訊

測試

如果您的應用存在與摺疊狀態相關的特殊行為,您需要為此編寫單元測試。Jetpack Window Manager 裡面有一條測試規則,支援在插樁測試期間模擬 FoldingFeature。由於測試需用到檢視,我們新增了 WindowLayoutInfoPublisherRule,以及 ActivityScenarioRule,兩者一起組成了一個測試規則鏈。在該測試方法中,我們通過 activityRule 獲取 Activity,然後建立視窗特性來模擬桌面模式,構建 WindowLayoutInfo 物件並使用 publisherRule 釋出該物件。之後,我們可以使用 EspressoJUnit 斷言來檢查 Activity 在桌面模式下能否正常執行。

private val publisherRule = WindowLayoutInfoPublisherRule()
private val activityRule = ActivityScenarioRule (MyActivity: :class.java)

@get :Rule
val testRule = RuleChain.outerRule (publisherRule) .around(activityRule)

@Test
fun testDeviceOpen_TableTop(): Unit = testScope.runBlockingTest {
    activityRule.scenario.onActivity { activity ->
        val feature = FoldingFeature (activity, HALF_OPENED, HORIZONTAL)
        val testWindowInfo = WindowLayoutInfo.Builder( )
                .setDisplayFeatures (listOf (feature))
                .build()

        publisherRule.overrideWindowLayoutInfo(testWindowInfo)
    }
        // 編寫基於桌面模式的斷言
}

△ 測試摺疊狀態

介面測試存在一定難度,因為有些測試須在特定裝置上進行。為此,Android Studio 正在增加對 Gradle 託管的虛擬裝置的支援。您可以使用 7.1 及以上版本的 Android Gradle 外掛來體驗該功能。

在應用級的 build.gradle 檔案中的 testOptions 模組下,指定虛擬裝置配置檔案,就像您平時在 Android Studio 管理和執行虛擬裝置那樣。例如,這裡使用的是 Pixel C 平板電腦映象,接下來 Gradle 會建立能夠在指定裝置上執行測試的目標,甚至還能根據需要下載裝置映象。

android {
    testoptions {
        devices {
            pixelCapi30 (ManagedVirtualDevice) {
                device = "Pixel C" // 平板電腦裝置
                apilevel = 30
                systemImageSource = "aosp" // 如需 GooglePlay 服務,使用 “google”
                abi = "x86”
            }
        }
    }
}
#Gradle target = {device name} + {build variant} + "AndroidTest"
./gradlew pixelCapi30debugAndroidTest

△ 虛擬裝置配置

為便於區分哪些測試是針對哪些裝置的,我們將建立自定義註解 LargeScreenTest,並用該註解來標記測試函式。執行前面的 Gradle 命令時,我們會為 AndroidTestRunner 新增一項引數,確保只執行具有此註釋的測試。若您不使用註釋,也可以使用 TestRunner 的其他過濾選項,比如執行特定類中的測試。將這些特性加以組合,我們可以為測試設定一致執行配置。

annotation class LargeScreenTest

@RunWith(AndroidJUnit4: :class)
class MyActivityTest {

    @Test @LargeScreenTest
    fun largeScreenDeviceTest() {
        // 在平板電腦裝置上測試介面
    }
}

# 只執行帶有指定註解的測試
. /gradlew pixelCapi30debugAndroidTest \-Pandroid.testInstrumentationRunnerArguments.annotation=com.mypkg.LargeScreenTest

△ 使用自定義註解為指定裝置編寫測試

更多資訊

除了讓螢幕上的內容看起來更大之外,大螢幕還帶來了一些其他機會,幫助您的應用大放異彩。在 多視窗模式 下,您的應用可以與其他應用並排使用,除了響應式調整之外,還可以考慮如何讓應用在這種模式下發揮更大作用,比如支援拖拽等。這種小功能可以提高使用者的工作效率,使用者便更樂意使用您的應用。

△ 多視窗模式效果

△ 多視窗模式效果

除了通過觸控進行互動外,大螢幕裝置還支援其他互動形式。裝置的螢幕尺寸越大,使用者就越有可能使用鍵盤、手寫筆、滑鼠、遊戲手柄或其他外接裝置。如果您想提高應用在這些情況下的易用性,可以計劃支援其中一些輸入方式,如需瞭解更多詳情,請參閱文章《是時候為各式裝置適配完善的輸入支援了》。

在如此多樣化的硬體生態系統中,您可能很難擁有各種形狀和尺寸的裝置,如今 Android SDK 為可摺疊裝置提供了模擬器影像,這些模擬器允許您隨時將摺疊狀態更改為鉸鏈的角度。即將推出的 Android Studio Chipmunk 也會配備可調整尺寸的模擬器,允許您自由改變應用視窗的尺寸,每個開發者都可以在幾乎任何型別的裝置中試用他們的應用。

△ Android Studio Chipmunk 中的可調整尺寸的模擬器

△ Android Studio Chipmunk 中的可調整尺寸的模擬器

我們也一直在 Android Studio 中開發新工具,希望為大家開發大螢幕應用提供支援。新的 Layout Validation 工具可以在覆蓋了各種尺寸類別的參考裝置上預覽佈局,並提示問題區域 (例如文字使用了長行),以及為不同斷點推薦不同介面元件。

△ Android Studio 中的 Layout Validation

△ Android Studio 中的 Layout Validation

最後,我們在 Android 開發者網站上列出了針對 大螢幕的應用質量指南,指南中的前面部分介紹的是基本相容性預期,比如應用是否同時支援橫屏和豎屏模式,後面幾部分重點介紹支援各種螢幕型別和狀態,並使用特定螢幕型別或狀態打造不同的體驗。

我們希望大家都能夠利用今天分享的內容,並參考新的質量指南,構建出在各種螢幕尺寸下都能讓使用者心動的應用。

歡迎您 點選這裡 向我們提交反饋,或分享您喜歡的內容、發現的問題。您的反饋對我們非常重要,感謝您的支援!

相關文章