<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      esky

      導航

      測試1-手勢導航和全屏體驗

      手勢導航和全屏體驗

      1. 簡介

      對于 Android 10 或更高版本,支持導航手勢這種新模式。在此模式中,您的應用可使用整個 屏幕,提供更身臨其境的顯示體驗。當用戶從屏幕下邊緣向上滑動時,可轉到 Android 主屏幕。當用戶從左邊緣或右邊緣向內滑動時,可轉到上一屏幕。

      使用這兩種手勢,您的應用即可充分利用屏幕底部的實際空間。但是,如果您的應用在系統手勢區域使用手勢或具有控件,則可能與系統級手勢發生沖突。

      此 Codelab 旨在說明如何使用邊襯區避免手勢沖突。此外,此 Codelab 還將說明如何對需要駐留在手勢區的拖動手柄等控件使用 手勢排除 API。

      您將學習的內容

      • 如何在視圖上使用邊襯區監聽器
      • 如何使用手勢排除 API
      • 在激活手勢時,沉浸模式有何表現

      此 Codelab 旨在確保您的應用可與系統手勢相兼容。對于無關緊要的概念和代碼塊,本文不作詳細介紹,僅提供相關內容以供您進行復制和粘貼。

      您將構建的應用

      Universal Android Music Player (UAMP) 是一款展示用的 Android 音樂播放器應用,采用 Kotlin 編寫而成。您將針對手勢導航功能設置 UAMP。

      • 使用邊襯區從手勢區域移開控件
      • 使用手勢排除 API 停用"返回"手勢,以保留與之沖突的控件
      • 使用您的版本,探索沉浸模式的行為隨應用手勢導航發生的變化

      您需要用到的工具

      • 運行 Android 10 或更高版本的設備或模擬器
      • Android Studio

      2. 應用概覽

      Universal Android Music Player (UAMP) 是一款展示用的 Android 音樂播放器應用,采用 Kotlin 編寫而成。此應用支持多種功能(包括后臺播放、音頻焦點處理、Google 助理集成),并可在多種平臺上使用(如 Wear、TV 和 Auto)。

      圖 1:UAMP 中的流程

      UAMP 會從遠程服務器中加載音樂目錄,用戶可使用此應用瀏覽專輯和歌曲。用戶點按歌曲后,此應用會通過連接的揚聲器或頭戴式耳機進行播放。此應用在設計時,不支持使用系統手勢。因此,在運行 Android 10 或更高版本的設備上運行 UAMP 時,您會在開始時遇到一些問題。

      3. 開始設置

      如要獲取此應用示例,可克隆 GitHub 中的代碼庫,然后切換到初學者分支:

      $  git clone https://github.com/googlecodelabs/android-gestural-navigation/
      

      或者,您也可以 zip 文件形式下載代碼庫,將其解壓縮,并在 Android Studio 中打開。

      完成以下步驟:

      1. 在 Android Studio 中打開并構建應用。
      2. 創建新的虛擬設備,然后選擇 API 級別 29。或者,您也可以連接運行 29 級或更高級別 API 的實際設備。
      3. 運行應用。系統會在出現的列表中,將歌曲分組顯示在 **Recommended(推薦)**和 **Albums(專輯)**選項下。
      4. 點擊 Recommended(推薦),然后從歌曲列表中選擇一首歌曲。
      5. 應用開始播放此歌曲。

      啟用手勢導航

      如果您在運行使用 API 級別 29 的新模擬器實例,默認情況下,系統將不會開啟手勢導航功能。如要啟用手勢導航功能,請選擇 System settings(系統設置)> System(系統)> System Navigation(系統導航)> Gesture Navigation(手勢導航)

      運行啟用手勢導航的應用

      如果您在運行啟用手勢導航的應用,并開始播放歌曲,您可能會發現,播放器控件非常接近"主屏幕"和"返回"手勢區域。

      4. 進入全屏模式

      什么是全屏?

      不管是啟用手勢還是按鈕進行導航,在 Android 10 或更高版本中運行的應用都可以為您帶來全屏體驗。如要提供全屏體驗,您必須將應用移至透明的導航欄和狀態欄后方。

      移到導航欄后方

      您必須先將導航欄背景設置為透明背景,然后您的應用才能在導航欄下面渲染內容。然后,必須將狀態欄設置為透明。這樣,您的應用才能按屏幕的全高進行顯示。

      **注意:**對于運行 Android 10 或更高版本的設備,強烈建議實行全屏體驗。對于運行舊版 Android 的設備,全屏為可選項,但仍建議使用。

      如要更改導航欄和狀態欄的顏色,請執行以下步驟:

      1. **導航欄:**打開 res/values-29/styles.xml,并將 navigationBarColor 設置為 color/transparent
      2. **狀態欄:**同樣,將 statusBarColor 設置為 color/transparent。

      查看 res/values-29/styles.xml 的以下代碼示例:

      <!-- change navigation bar color -->
      <item name="android:navigationBarColor">
          @android:color/transparent
      </item>
      
      <!-- change status bar color -->
      <item name="android:statusBarColor">
          @android:color/transparent
      </item>
      

      系統界面可見度標記

      您還必須設置系統界面可見度標記,才能讓系統將應用置于系統欄下方。您可使用 View 類的 systemUiVisibility API 設置各種標記。請執行以下步驟:

      1. 打開 MainActivity.kt 類,并查找 onCreate() 方法。獲取 fragmentContainer 的實例。
      2. 將以下內容設置為 content.systemUiVisibility

      查看 MainActivity.kt 的以下代碼示例:

        val content: FrameLayout = findViewById(R.id.fragmentContainer)
        content.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
                  View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
                  View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
      

      同時設置這些標記后,即可讓系統以全屏模式顯示您的應用,就像導航欄和狀態欄不存在一樣。請執行以下步驟:

      1. 運行應用,并導航至播放器屏幕,選擇要播放的歌曲。
      2. 驗證系統是否已將播放器控件移到導航欄下方,使其難以訪問:

      1. 導航至"System settings"(系統設置),切換回三鍵導航模式,然后返回應用。
      2. 驗證這些控件是否因應用三鍵導航欄而更難以使用:請注意,系統已將 SeekBar 隱藏到導航欄后方,而且 **Play/Pause(播放/暫停)**基本上已由導航欄所遮蓋。
      3. 探索并試驗一下。完成操作后,導航至"System settings"(系統設置), 切換回手勢導航:

      c7085e9130e5ecb3.gif

      此應用現在會以全屏模式顯示在您的面前,但其中存在應用控件沖突和重疊的易用性問題,而我們必須解決這些問題。

      5. 邊襯區

      通過使用 WindowInsets,應用可得知系統界面出現在內容頂層的什么位置,以及在屏幕的哪些區域內,系統手勢會優先于應用內手勢。邊襯區將由 Jetpack 中的 WindowInsets 類和 WindowInsetsCompat 類表示。我們強烈建議使用 WindowInsetsCompat,以便在所有 API 級別中都保持行為一致。

      系統邊襯區和強制系統邊襯區

      以下邊襯區 API 是最常用的邊襯區類型:

      • **系統窗口邊襯區:**您可通過這些邊襯區,了解系統界面會顯示在應用上方的什么位置。我們將討論如何使用系統邊襯區從系統欄移開控件。
      • **系統手勢邊襯區:**這些邊襯區可返回所有手勢區域。這些區域的所有應用內滑動控件均可意外觸發系統手勢。
      • **強制手勢邊襯區:**這些邊襯區是系統手勢邊襯區的子集,不得覆蓋。您可借此了解到在哪些屏幕區域內,系統手勢的行為會始終優先于應用內手勢。

      使用邊襯區移動應用控件

      您現在已經了解邊襯區 API 的詳細信息,可以按以下步驟所述修復應用控件:

      1. view 對象實例中獲取 playerLayout 實例。
      2. OnApplyWindowInsetsListener 添加到 playerView。
      3. 從手勢區域移開視圖:找到底部的系統邊襯區值,然后按該數量增加視圖的邊距。如要將視圖的邊距相應地更新為 [與應用底部邊距關聯的值],請添加 [與系統邊襯區底部值關聯的值]。

      查看 NowPlayingFragment.kt 的以下代碼示例:

      playerView = view.findViewById(R.id.playerLayout)
      playerView.setOnApplyWindowInsetsListener { view, insets ->
         view.updatePadding(
            bottom = insets.systemWindowInsetBottom + view.paddingBottom
         )
         insets
      }
      
      1. 運行應用,并選擇歌曲。請注意,播放器控件似乎沒有變化。如果在調試中添加斷點并運行應用,您會看到監聽器尚未調用。
      2. 要修復此問題,請切換至 FragmentContainerView,以便其自動處理此問題。打開 activity_main.xml,并將 FrameLayout 更改為 FragmentContainerView

      查看 activity_main.xml 的以下代碼示例:

      <androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:tools="http://schemas.android.com/tools"
          android:id="@+id/fragmentContainer"
          tools:context="com.example.android.uamp.MainActivity"
          android:layout_width="match_parent"
          android:layout_height="match_parent"/>
      
      1. 再次運行應用,并導航至播放器屏幕。系統已將底部播放器控件從底部手勢區域移開。

      應用控件現在可與手勢導航功能一起發揮作用,但這些控件的移動距離超出預期。您必須解決此問題。

      保留當前內邊距和外邊距

      如果在不關閉此應用的情況下切換至其他應用或轉到主屏幕,然后返回此應用,您會發現播放器控件每次都會上移。

      這是因為該活動每次開始時,應用都會觸發 requestApplyInsets()。即使您沒有執行此 調用,系統也會在視圖的生命周期內隨時多次分派 WindowInsets。

      首次將邊襯區底部值數量添加到 activity_main.xml 中聲明的應用底部邊距值時,playerView 上的當前 InsetListener 會正常運行。但是,后續調用會將邊襯區底部值繼續添加到已更新視圖的底部邊距中。

      要解決此問題,請執行以下步驟:

      1. 記錄視圖初始邊距值。創建新的值,并存儲 playerView 視圖初始邊距值,然后再存儲監聽器代碼。

      查看 NowPlayingFragment.kt 的以下代碼示例:

         val initialPadding = playerView.paddingBottom
      
      1. 使用此初始值更新視圖的底部邊距,這樣可避免使用應用的當前底部邊距值。

      查看 NowPlayingFragment.kt 的以下代碼示例:

         playerView.setOnApplyWindowInsetsListener { view, insets ->
                  view.updatePadding(bottom = insets.systemWindowInsetBottom + initialPadding)
                  insets
              }
      
      1. 再次運行應用。在應用之間導航,然后轉到主屏幕。當返回應用時,播放器控件剛好在手勢區域上方的位置。

      重新設計應用控件

      播放器拖動條太靠近底部手勢區域,意味著用戶在完成水平滑動手勢時會意外觸發主屏幕手勢。如果增大邊距,則可解決此問題,但也可能會將播放器移動得過高,超出預期高度。

      盡管可通過使用邊襯區解決手勢沖突問題,但有時在設計時稍作改變,就可以完全避免手勢沖突問題。如要重新設計播放器控件以避免手勢沖突,請執行以下步驟:

      1. 打開 fragment_nowplaying.xml。切換至"Design"(設計)視圖,然后選擇最底部的 SeekBar

      e7f5e258660d92af.png

      1. 切換至"Code"(代碼)視圖。
      2. 如要將 SeekBar 移至 playerLayout 頂部,請將拖動條的 layout_constraintTop_toBottomOf 更改為 parent。
      3. 如要將 playerView 中的其他項目限定至 SeekBar 的底部,請在 media_button、titleposition 中將 layout_constraintTop_toTopOf 從 parent 更改為 @+id/seekBar。

      查看 fragment_nowplaying.xml 的以下代碼示例:

      <androidx.constraintlayout.widget.ConstraintLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:padding="8dp"
         android:layout_gravity="bottom"
         android:background="@drawable/media_overlay_background"
         android:id="@+id/playerLayout">
      
         <ImageButton
             android:id="@+id/media_button"
             android:layout_width="@dimen/exo_media_button_width"
             android:layout_height="@dimen/exo_media_button_height"
             android:background="?attr/selectableItemBackground"
             android:scaleType="centerInside"
             app:layout_constraintLeft_toLeftOf="parent"
             app:layout_constraintTop_toTopOf="@+id/seekBar"
             app:srcCompat="@drawable/ic_play_arrow_black_24dp"
             tools:ignore="ContentDescription" />
      
         <TextView
             android:id="@+id/title"
             android:layout_width="0dp"
             android:layout_height="wrap_content"
             android:layout_marginTop="8dp"
             android:layout_marginStart="@dimen/text_margin"
             android:layout_marginEnd="@dimen/text_margin"
             android:ellipsize="end"
             android:maxLines="1"
             android:textAppearance="@style/TextAppearance.Uamp.Title"
             app:layout_constraintTop_toTopOf="@+id/seekBar"
             app:layout_constraintLeft_toRightOf="@id/media_button"
             app:layout_constraintRight_toLeftOf="@id/position"
             tools:text="Song Title" />
      
         <TextView
             android:id="@+id/subtitle"
             android:layout_width="0dp"
             android:layout_height="wrap_content"
             android:layout_marginStart="@dimen/text_margin"
             android:layout_marginEnd="@dimen/text_margin"
             android:ellipsize="end"
             android:maxLines="1"
             android:textAppearance="@style/TextAppearance.Uamp.Subtitle"
             app:layout_constraintTop_toBottomOf="@+id/title"
             app:layout_constraintLeft_toRightOf="@id/media_button"
             app:layout_constraintRight_toLeftOf="@id/position"
             tools:text="Artist" />
      
         <TextView
             android:id="@+id/position"
             android:layout_width="0dp"
             android:layout_height="wrap_content"
             android:layout_marginTop="8dp"
             android:layout_marginStart="@dimen/text_margin"
             android:layout_marginEnd="@dimen/text_margin"
             android:ellipsize="end"
             android:maxLines="1"
             android:textAppearance="@style/TextAppearance.Uamp.Title"
             app:layout_constraintTop_toTopOf="@+id/seekBar"
             app:layout_constraintRight_toRightOf="parent"
             tools:text="0:00" />
      
         <TextView
             android:id="@+id/duration"
             android:layout_width="0dp"
             android:layout_height="wrap_content"
             android:layout_marginStart="@dimen/text_margin"
             android:layout_marginEnd="@dimen/text_margin"
             android:ellipsize="end"
             android:maxLines="1"
             android:textAppearance="@style/TextAppearance.Uamp.Subtitle"
             app:layout_constraintTop_toBottomOf="@id/position"
             app:layout_constraintRight_toRightOf="parent"
             tools:text="0:00" />
      
         <SeekBar
             android:id="@+id/seekBar"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             app:layout_constraintBottom_toBottomOf="parent"
             app:layout_constraintEnd_toEndOf="parent"
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintTop_toBottomOf="parent" />
      
      </androidx.constraintlayout.widget.ConstraintLayout>
      
      1. 運行應用,并與播放器和拖動條交互。

      這些極小的設計改變可顯著改進應用。

      6. 手勢排除 API

      與主屏幕手勢區域手勢沖突的播放器控件問題已解決。"返回"手勢區域也會與應用控件發生沖突。以下屏幕截圖顯示的是播放器拖動條當前駐留在左側和右側的"返回"手勢區域:

      e6d98e94dcf83dde.png

      SeekBar 可自動處理手勢沖突問題。但您可能需要使用會觸發手勢沖突的其他界面組件。在這些情況下,您可以使用 Gesture Exclusion API 分部分地停用"返回"手勢。

      **注意:**每側手勢排除 API 的限制為 200 dp,而且僅在必要時才可使用。如果在視圖中或在應用的某些部分禁用"返回"手勢,則會導致系統及其他應用出現不一致問題。

      使用手勢排除 API

      要創建手勢排除區域,請使用 rect 對象列表對視圖調用 setSystemGestureExclusionRects()。這些 rect 對象會映射至已排除的矩形區域的坐標。您必須采用視圖的 onLayout()onDraw() 方法完成此調用。為此,請執行以下步驟:

      1. 創建名為 view 的新軟件包。
      2. 要調用此 API,請創建一個名為 MySeekBar 的新類,并擴展 AppCompatSeekBar。

      查看 MySeekBar.kt 的以下代碼示例:

      class MySeekBar @JvmOverloads constructor(
          context: Context,
          attrs: AttributeSet? = null,
          defStyle: Int = android.R.attr.seekBarStyle
      ) : androidx.appcompat.widget.AppCompatSeekBar(context, attrs, defStyle) {
      
      }
      
      1. 創建一個名為 updateGestureExclusion() 的新方法。

      查看 MySeekBar.kt 的以下代碼示例:

      private fun updateGestureExclusion() {
      
      }
      
      1. 添加一項檢查,以便在使用 28 級或更低級別 API 時跳過此調用。

      查看 MySeekBar.kt 的以下代碼示例:

      private fun updateGestureExclusion() {
              // Skip this call if we're not running on Android 10+
              if (Build.VERSION.SDK_INT < 29) return
      }
      
      1. 由于手勢排除 API 限制為 200 dp,所以我們只能排除小塊的拖動條。復制拖動條的邊框,并將每個對象添加到可變列表中。

      查看 MySeekBar.kt 的以下代碼示例:

      private val gestureExclusionRects = mutableListOf<Rect>()
      
      private fun updateGestureExclusion() {
          // Skip this call if we're not running on Android 10+
          if (Build.VERSION.SDK_INT < 29) return
      
          thumb?.also { t ->
              gestureExclusionRects += t.copyBounds()
          }
      }
      
      1. 使用創建的 gestureExclusionRects 列表調用 systemGestureExclusionRects()。

      查看 MySeekBar.kt 的以下代碼示例:

      private val gestureExclusionRects = mutableListOf<Rect>()
      
      private fun updateGestureExclusion() {
          // Skip this call if we're not running on Android 10+
          if (Build.VERSION.SDK_INT < 29) return
      
          thumb?.also { t ->
              gestureExclusionRects += t.copyBounds()
          }
          // Finally pass our updated list of rectangles to the system
          systemGestureExclusionRects = gestureExclusionRects
      }
      
      1. onDraw()onLayout() 中調用 updateGestureExclusion() 方法。覆蓋 onDraw(),并向 updateGestureExclusion 中添加調用。

      查看 MySeekBar.kt 的以下代碼示例:

      override fun onDraw(canvas: Canvas) {
          super.onDraw(canvas)
          updateGestureExclusion()
      }
      
      1. 必須更新 SeekBar 引用。如要開始更新,請打開 fragment_nowplaying.xml
      2. SeekBar 更改為 com.example.android.uamp.view.MySeekBar。

      查看 fragment_nowplaying.xml 的以下代碼示例:

      <com.example.android.uamp.view.MySeekBar
          android:id="@+id/seekBar"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          app:layout_constraintBottom_toBottomOf="parent"
          app:layout_constraintEnd_toEndOf="parent"
          app:layout_constraintStart_toStartOf="parent"
          app:layout_constraintTop_toBottomOf="parent" />
      
      1. 如要在 NowPlayingFragment.kt 中更新 SeekBar 引用,請打開 NowPlayingFragment.kt,并將 positionSeekBar 的類型更改為 MySeekBar。如要使變量類型一致,請將 findViewById 調用的 SeekBar 泛型更改為 MySeekBar。

      查看 NowPlayingFragment.kt 的以下代碼示例:

      val positionSeekBar: MySeekBar = view.findViewById<MySeekBar>(
           R.id.seekBar
      ).apply { progress = 0 }
      
      1. 運行應用,并與 SeekBar 交互。如果手勢沖突問題仍然存在,則可嘗試修改 MySeekBar 的小塊邊框。注意,不要創建超過必需大小的手勢排除區域,這樣會限制其他潛在手勢排除調用,并會導致用戶行為出現不一致的問題。

      7. 恭喜

      恭喜!您已學會如何避免與系統手勢沖突以及解決此問題!

      在擴展全屏并使用邊襯區從手勢區域移開應用控件后,您可確保應用使用全屏模式。此外,您已學會如何在使用應用控件時禁用系統"返回"手勢。

      現在您已了解讓應用使用系統手勢所需的關鍵步驟!

      其他材料

      參考文檔

      posted on 2023-02-26 22:24  hzwsky  閱讀(187)  評論(0)    收藏  舉報

      主站蜘蛛池模板: 国产性三级高清在线观看| 韩国19禁无遮挡啪啪无码网站| 蜜桃臀av在线一区二区| 国产高清自产拍av在线| 一本一道av无码中文字幕﹣百度 | 国产熟女老阿姨毛片看爽爽| 人妻少妇久久久久久97人妻| 国产乱码日产乱码精品精| 国产精品av中文字幕| 国产av无码专区亚洲av软件| 无线乱码一二三区免费看| 精品一区二区三区不卡| 漂亮人妻被中出中文字幕| 免费无码影视在线观看mov| 丁香婷婷色综合激情五月| 国产精品国产三级国快看| 国产精品国产精品偷麻豆| 国产精品自在拍首页视频8| 无码人妻aⅴ一区二区三区蜜桃| 夜夜爽妓女8888888视频| 欧美激烈精交gif动态图| 亚洲一区二区精品偷拍| 免费无码毛片一区二三区| 国产一区二区三区美女| 欧美大bbbb流白水| 97午夜理论电影影院| 午夜精品区| 无码国产精品一区二区av| 91精品国产午夜福利| 四虎国产精品永久入口| 清纯唯美人妻少妇第一页| 亚洲欧美日韩成人一区| 中文字幕国产精品专区| 伊人中文在线最新版天堂| 日韩激情无码av一区二区| 国产日韩精品免费二三氏| 国产av一区二区不卡| av色国产色拍| 中文无码热在线视频| 人妻久久久一区二区三区| 色综合久久网|