1. 始める前に
Android デバイスには、さまざまな形状、サイズ、フォーム ファクタがあります。小画面のデバイスから大画面のデバイスまで、さまざまな種類のデバイスで動作するようにアプリを設計する必要があります。製品レベルの機能を備えたアプリを作成するデベロッパーは Android Wear、Android Auto、Android TV をサポートする場合がありますが、これらのトピックはこのコースの対象外です。アプリが多様な画面に対応していると、さまざまなデバイスを使用する多くのユーザーにアプリを提供できます。
アプリは柔軟なレイアウトを備えていなければなりません。特定のアスペクト比と画面サイズを想定した固定ディメンションでレイアウトを定義するのではなく、レイアウトがさまざまな画面サイズと向きに無理なく対応できるようにする必要があります。アプリの実行中に画面サイズとアスペクト比が変化することがある折りたたみ式デバイスでアプリが動作する場合も、同じ原則が適用されます。折りたたみ式デバイスについては、この Codelab の最後で簡単に説明します。
前提条件
- Android Studio にコードをダウンロードして実行する方法を理解している。
- Android アーキテクチャ コンポーネントの
ViewModel
とLiveData
に精通している。 - Navigation コンポーネントの基本的な知識を持っている。
学習内容
- アプリに
SlidingPaneLayout
を追加する方法
作成するアプリの概要
- Sports アプリを更新して大画面に適合させます。
必要なもの
- Android Studio がインストールされているパソコン
- Sports アプリのスターター コード
この Codelab のスターター コードをダウンロードする
この Codelab では、ここで学んだ機能を使って拡張するためのスターター コードが提供されます。スターター コードには、以前の Codelab で学んだコードだけでなく、今後の Codelab で学ぶ予定の、見慣れないコードが含まれていることもあります。
この Codelab のコードを GitHub から取得して Android Studio で開く手順は次のとおりです。
- Android Studio を起動します。
- [Welcome to Android Studio] ウィンドウで、[Get from VCS] をクリックします。
- [Get from Version Control] ダイアログの [Version control] で、[Git] が選択されていることを確認します。
- 提供されたコードの URL を [URL] ボックスに貼り付けます。
- 必要に応じて、[Directory] を推奨されるデフォルトとは異なるものに変更します。
- [Clone] をクリックします。Android Studio がコードの取得を開始します。
- Android Studio が開くまで待ちます。
- Codelab のスターター コード、アプリコード、または解答コードに適したモジュールを選択します。
- 実行ボタン をクリックし、コードをビルドして実行します。
2. Code-Along 動画を見る(省略可)
コースの講師が Codelab を完了する様子を視聴する場合は、以下の動画を再生してください。
動画を拡大して全画面表示にすることをおすすめします(動画の右下隅のアイコン を使用します)。そうすれば、Android Studio とコードがもっとはっきり見えるようになります。
このステップは省略可能です。動画をスキップして、すぐに Codelab の学習を開始することもできます。
3. スターター アプリの概要
Sports アプリは 2 つの画面で構成されます。最初の画面では、スポーツリストを表示します。ユーザーは特定のスポーツを選択できます。アイテムが選択されたら、2 番目の画面を表示します。2 番目の画面は、選択されたスポーツのニュースを表示する詳細画面です。詳細画面では、実装を簡単にするために、プレースホルダ テキストを表示します。
スターター コードのチュートリアル
ダウンロードしたスターター コードには、あらかじめ設計されたリスト画面と詳細画面のレイアウトが含まれています。このパスウェイでは、アプリを大画面に適合させることに専念します。大画面を活用するために、SlidingPaneLayou
t を使用します。作業の土台とするファイルの一部について簡単に説明します。
fragment_sports_list.xml
- [Design] ビューで
res/layout/fragment_sports_list.xml
を開きます。 - このファイルには、アプリの最初の画面であるスポーツリストのレイアウトが格納されています。
- このレイアウトは、スポーツ ニュースのリストを表示する RecyclerView で構成されています。
sports_list_item.xml
- [Design] ビューで
res/layout/sports_list_item.xml
を開きます。 - このファイルには、RecyclerView の各アイテムのレイアウトが格納されています。
- このレイアウトは、スポーツのサムネイル画像、ニュースのタイトル、簡潔なスポーツ ニュースのプレースホルダ テキストで構成されています。
fragment_sports_news.xml
- [Design] ビューで
res/layout/fragment_sports_news.xml
を開きます。 - このファイルには、アプリの 2 番目の画面のレイアウトが格納されています。この画面は、ユーザーが RecyclerView からスポーツを選択したときに表示されます。
- このレイアウトは、スポーツの画像バナーと、スポーツ ニュースのプレースホルダ テキストで構成されています。
main_activity.xml と content_main.xml
この 2 つのファイルでは、単一のフラグメントを含むメイン アクティビティのレイアウトが定義されています。
navigation/nav_graph.xml
ナビゲーション グラフには 2 つのデスティネーションが含まれています。1 つはスポーツリスト用、もう 1 つはスポーツ ニュース用です。
res/values フォルダ
このフォルダにあるリソース ファイルについては学習済みです。
colors.xml
には、アプリで使用されるテーマカラーが格納されています。strings.xml
には、アプリに必要なすべての文字列が格納されています。themes.xml
には、アプリ用に行われた UI のカスタマイズが格納されています。
MainActivity.kt
このファイルには、デフォルト テンプレートによって生成された、アクティビティのコンテンツ ビューを main_activity.xml
として設定するコードが格納されています。メソッド onSupportNavigateUp()
は、アプリバーからのデフォルトの「上へ」ナビゲーションを処理するためにオーバーライドされています。
model/Sport.kt
これは、スポーツリストの RecyclerView の各行に表示されるデータを保持するデータクラスです。
data/SportsData.kt
このファイルには、getSportsData()
という関数が格納されています。この関数は、ハードコードされたスポーツデータが事前入力されている ArrayList
を返します。
SportsViewModel.kt
これはアプリの共有 ViewModel
です。ViewModel
は、SportsListFragment
(スポーツリストを表示する最初の画面)と NewsDetailsFragment
(詳細なスポーツ ニュースを表示する 2 番目の画面)によって共有されます。
_currentSport
は、ユーザーが選択した現在のスポーツを保存するMutableLiveData,
型のプロパティです。currentSport
プロパティは_currentSport
のバッキング プロパティであり、他のクラスのためのパブリックな読み取り専用バージョンとして公開されます。_sportsData
プロパティは、スポーツデータのリストを格納します。前述のプロパティと同様に、このプロパティにはパブリックな読み取り専用バージョンとしてsportsData
があります。- イニシャライザである
init{}
ブロックは、_currentSport
と_sportsData
を初期化します。_sportsData
については、data/SportsData.kt
から取得されるスポーツのリスト全体が初期化されます。_currentSport
については、リストの最初のアイテムが初期化されます。 - 関数
updateCurrentSport()
はSports
インスタンスを受け取り、渡された値で_currentSport
を更新します。
SportsAdapter.kt
これは RecyclerView
のアダプターです。コンストラクタ内で、クリック リスナーが渡されます。このファイルに含まれるコードの大半は、以前の Codelab でお馴染みのボイラープレート コードです。
SportsListFragment.kt
これは、スポーツリストが表示される最初の画面のフラグメントです。
onCreateView()
関数は、バインディング オブジェクトを使用してfragment_sports_list
レイアウト XML をインフレートします。onViewCreated()
関数はRecyclerView
のアダプターをセットアップします。共有ViewModel
であるSportsViewModel
で、ユーザーが選択したスポーツを現在のスポーツとして更新します。スポーツ ニュースの詳細画面に移動し、submitList(List)
を使用して、表示するスポーツリストをアダプターに送信します。
NewsDetailsFragment.kt
これはアプリの 2 番目の画面であり、スポーツ ニュースのプレースホルダ テキストが表示されます。
onCreateView()
関数は、バインディング オブジェクトを使用してfragment_sports_news
レイアウト XML をインフレートします。onViewCreated()
関数は、データが変更されたときに UI を自動的に更新するために、SportsViewModel
のプロパティであるcurrentSport
にオブザーバーをアタッチします。オブザーバー内では、スポーツのタイトル、画像、ニュースが更新されます。
アプリをビルドして実行する
- アプリをビルドして、エミュレータまたはデバイスで実行します。スポーツリストからいずれかのアイテムを選択すると、アプリは 2 番目の画面に移動してニュースのプレースホルダ テキストを表示します。
4. リスト / 詳細パターン
現在のスターター アプリは、タブレットなどの大型デバイスでは画面スペースを最大限に活用できません。この問題を解決するには、この Codelab で学習するリスト / 詳細パターンを使用してアプリの UI を表示します。
タブレットでアプリを実行する
このタスクでは、タブレット プロファイルを備えたエミュレータを作成します。エミュレータを作成したら、Sports アプリのスターター コードを実行して UI を確認します。
- Android Studio で、[Tools] > [AVD Manager] に移動します。
- [Android Virtual Device Manager] ウィンドウが表示されます。下部に表示される [+ Create New Virtual Device...] をクリックします。
- [Virtual Device Configuration] ウィンドウが表示されます。ここで、エミュレータのハードウェアと OS を構成します。左側のペインで [Tablet] をクリックします。中央のペインで [Pixel C] またはそれと似たハードウェア プロファイルを選択します。
- [Next] をクリックします。
- 最新のシステム イメージを選択します。この Codelab の作成時点における最新バージョンは R(API レベル 30)です。
- [Next] をクリックします。
- ここで仮想デバイスの名前を変更できますが、変更するかどうかは任意です。
- [Finish] をクリックします。
- [Android Virtual Device Manager] ウィンドウに自動的に戻ります。新しく作成した仮想デバイスの横にある起動アイコン をクリックします。
- タブレット プロファイルを備えたエミュレータが起動します。起動するまで時間がかかることがあるので、しばらくお待ちください。
- [Android Virtual Device Manager] ウィンドウを閉じます。
- 新しく作成したエミュレータで Sports アプリを実行します。
ご覧のとおり、大型デバイスではアプリは画面全体を活用していません。大画面では、リストのみを表示するより、リストと詳細を表示する方が効果的です。リスト / 詳細パターンはマスター / 詳細パターンとも呼ばれます。このパターンでは、レイアウトの片側にアイテムのリストが表示され、アイテムをタップするとリストの横に詳細が表示されます。一般的に、このようなビューは、より多くのコンテンツを表示できるスペースがあるタブレットなどの大画面でのみ表示されます。
以下の画像は、リスト / 詳細パターンの例です。
上記のリスト / 詳細パターンでは、アイテムのリストが左側に表示され、選択されたアイテムの詳細が右側に表示されます。
Sports アプリで上記のパターンを使用する場合は、ニュース フラグメントが詳細画面になります。
この Codelab では、SlidingPaneLayout
を使用してリスト / 詳細 UI を実装する方法を学びます。
5. SlidingPaneLayout パターン
リスト / 詳細 UI は、画面サイズに応じて異なる動作をしなければなりません。大きなディスプレイには、リストペインと詳細ペインを並べて配置できる十分なスペースがあります。リストアイテムをクリックすると、その詳細が詳細ペインに表示されます。しかし、小さな画面では、このパターンは窮屈に見えます。両方のペインを一度に表示するよりも、一度に 1 つずつ表示する方が適切です。最初は、リストペインが画面全体に表示されます。アイテムをタップすると、リストペインに代わってそのアイテムの詳細ペインが画面全体に表示されます。
ここでは、SlidingPaneLayout を使用して、現在の画面サイズを基に適切なユーザー エクスペリエンスを選択するロジックを処理する方法を学びます。
この例では、小さな画面で詳細ペインがリストペインの上にスライドして表示されます。
下記の画像は、小さな画面で SlidingPaneLayout
がどのように表示されるかを示しています。リストからアイテムが選択されると、詳細ペインがリストペインの上に重ねられることに注意してください。つまり、常に両方のペインが存在しています。
このように、SlidingPaneLayout
は、大型デバイスでは 2 つのペインを並べて表示し、スマートフォンなどの小型デバイスでは一度に 1 つのペインのみを表示するよう自動的に調整する機能を備えています。
6. ライブラリの依存関係を追加する
build.gradle (Module: Sports.app)
を開きます。- アプリで
SlidingPaneLayout
を使用するため、dependencies
セクションに次の依存関係を含めます。
dependencies {
...
implementation "androidx.slidingpanelayout:slidingpanelayout:1.2.0-beta01"
}
7. スポーツリストのフラグメント XML を構成する
このタスクでは、fragment_sports_list
のルート レイアウトを SlidingPaneLayout
に変換します。すでに学習したとおり、SlidingPaneLayout
は、最上位レベルの UI で使用できる、横に並んだ 2 つのペインのレイアウトを提供します。このレイアウトでは、最初のペインをコンテンツの閲覧用リストとして使用します。このペインは、もう 1 つのペインでコンテンツを表示する主要な詳細ビューに従属します。
Sports アプリでは、最初のペインはスポーツリストを表示する RecyclerView
であり、2 番目のペインはスポーツ ニュースを表示します。
SlidingPaneLayout を追加する
fragment_sports_list.xml
を開きます。ルート レイアウトはFrameLayout
であることに注目してください。FrameLayout
をandroidx.slidingpanelayout.widget.SlidingPaneLayout.
に変更します。
<androidx.slidingpanelayout.widget.SlidingPaneLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".SportsListFragment">
<androidx.recyclerview.widget.RecyclerView...>
</androidx.slidingpanelayout.widget.SlidingPaneLayout>
SlidingPaneLayout
にandroid:id
属性を追加し、その値を@+id/sliding_pane_layout
に設定します。
<androidx.slidingpanelayout.widget.SlidingPaneLayout
...
android:id="@+id/sliding_pane_layout"
...>
SlidingPaneLayout に 2 番目のペインを追加する
このタスクでは、SlidingPaneLayout
に 2 番目の子を追加します。これが右側のコンテンツ ペインとして表示されます。
fragment_sports_list.xml
のRecyclerView
の下に、2 番目の子androidx.fragment.app.FragmentContainerView
を追加します。- 必須の属性
layout_height
およびlayout_width
をFragmentContainerView
に追加します。それらの値をmatch_parent
に設定します。これらの値は後で更新します。
<androidx.fragment.app.FragmentContainerView
android:layout_height="match_parent"
android:layout_width="match_parent"/>
FragmentContainerView
にandroid:id
属性を追加し、その値を@+id/detail_container
に設定します。
android:id="@+id/detail_container"
android:name
属性を使用して、FragmentContainerView
にNewsDetailsFragment
を追加します。
android:name="com.example.android.sports.NewsDetailsFragment"
layout_width 属性を更新する
SlidingPaneLayout
は、ペインの幅を使用して両方のペインを並べて表示するかどうかを決定します。たとえば、リストペインの最小幅が 300dp
と測定され、詳細ペインに 400dp
の幅が必要な場合、SlidingPaneLayout
は、最低 700dp
の幅が使用可能であれば、自動的に 2 つのペインを並べて表示します。
幅の合計が SlidingPaneLayout
の使用可能な幅を超える場合、子ビューは重ねられます。この場合、子ビューは SlidingPaneLayout
の使用可能な幅いっぱいに展開されます。
子ビューの幅を決定するには、デバイス画面の幅に関する基本的な情報が必要です。サイズ変更が可能なアプリ レイアウトの設計、開発、テストに使用できる不変のブレークポイントの一覧を次の表に示します。これらのブレークポイントは、柔軟性とレイアウトのシンプルさのバランスを取りつつ、固有のケースに合わせてアプリを最適化できるように、特別に選択されたものです。
幅 | ブレークポイント | デバイスによる表現 |
コンパクトな幅 | 600 dp 未満 | 縦向きのスマートフォンの 99.96% |
中程度の幅 | 600 dp 以上 | 縦向きのタブレット(広げた状態の大型縦向きインナー ディスプレイ)の 93.73% |
拡大幅 | 840 dp 以上 | 横向きのタブレット(広げた状態の大型横向きインナー ディスプレイ)の 97.22% |
Sports アプリでは、スポーツリストをスマートフォンの単一のペインに表示できます(これは幅が 600dp
未満のデバイスに当たります)。タブレットで両方のペインを表示するには、幅の合計を 840dp
より大きくする必要があります。最初の子(リサイクラー ビュー)には 550dp
の幅、2 番目の子(FragmentContainerView
)には 300dp
の幅を使用できます。
fragment_sports_list.xml
で、RecyclerView
のレイアウト幅を550dp
に変更し、FragmentContainerView
のレイアウト幅を300dp
に変更します。
<androidx.recyclerview.widget.RecyclerView
...
android:layout_width="550dp"
.../>
<androidx.fragment.app.FragmentContainerView
...
android:layout_width="300dp"
.../>
- タブレット プロファイルを備えたエミュレータと、スマートフォン プロファイルを備えたエミュレータでアプリを実行します。
タブレットでは 2 つのペインが表示されます。タブレットの 2 番目のペインの幅は、この後のステップで修正します。
- スマートフォン プロファイルを備えたエミュレータでアプリを実行します。
layout_weight を追加する
このタスクでは、タブレットの UI を修正して、2 番目のペインを残りのスペース全体に広げます。
SlidingPaneLayout
では、ビューが重ならない場合に、子ビューのレイアウト パラメータ layout_weight
を使用して、測定後に残りのスペースを分割する方法を定義できます。このパラメータは幅にのみ適用されます。
fragment_sports_list.xml
で、FragmentContainerView
にlayout_weight
を追加し、その値を1
に設定します。これにより、リストペインが測定された後、2 番目のペインが展開されて残りのスペースを埋めるようになります。
android:layout_weight="1"
- アプリを実行します。
これで、SlidingPaneLayout
が首尾よく追加されました。しかし、これで終わりではありません。「戻る」ナビゲーションを実装し、リストからアイテムが選択されたときに 2 番目のペインを更新する必要があります。この後のタスクでそれらを実装します。
8. 詳細ペインを入れ替える
タブレット プロファイルを備えたエミュレータでアプリを実行します。スポーツリストからリストアイテムを選択します。ご覧のとおり、アプリは詳細ペインに移動します。
このタスクでは、この問題を修正します。現在、デュアルペイン コンテンツは選択されたスポーツで更新され、その後アプリは NewsDetailsFragment
に移動します。
SportsListFragment
ファイルのonViewCreated()
関数で以下の行を見つけます。これが詳細画面に移動するコードです。
// Navigate to the details screen
val action = SportsListFragmentDirections.actionSportsListFragmentToNewsFragment()
this.findNavController().navigate(action)
- 上記の行を次のコードに置き換えます。
binding.slidingPaneLayout.openPane()
SlidingPaneLayout
で openPane()
を呼び出して、2 番目のペインを最初のペインと入れ替えます。タブレットなどで両方のペインが表示されている場合、これにより目に見える効果は生じません。
- タブレットとスマートフォンのエミュレータでアプリを実行します。デュアルペイン コンテンツが適切に更新されることを確認します。
次のタスクでは、カスタムの「戻る」ナビゲーション機能をアプリに追加します。
9. カスタムの「戻る」ナビゲーションを追加する
リストペインと詳細ペインが重ねられる小型デバイスでは、システムの [戻る] ボタンでユーザーを詳細ペインからリストペインに戻す必要があります。そのためには、カスタムの「戻る」ナビゲーションを提供して、OnBackPressedCallback
を SlidingPaneLayout
の現在の状態に接続します。
「戻る」ナビゲーション
「戻る」ナビゲーションとは、ユーザーがこれまでにアクセスした画面の履歴を遡って移動する機能です。すべての Android デバイスは、このタイプのナビゲーションの用に [戻る] ボタンを備えています。ユーザーの Android デバイスによって、このボタンは物理ボタンの場合もあれば、ソフトウェア ボタンの場合もあります。
カスタムの「戻る」ナビゲーション
Android では、ユーザーがアプリ内を移動する際に、デスティネーションの「バックスタック」が保持されます。これにより、通常は [戻る] ボタンを押すと、以前のデスティネーションに適切に移動できます。ただし、可能な限り最高のユーザー エクスペリエンスを提供するために、アプリ独自の「戻る」動作を実装する必要が生じる場合があります。
たとえば、Chrome ブラウザなどの WebView を使用する場合は、デフォルトの [戻る] ボタンの動作をオーバーライドして、ユーザーがアプリの前の画面ではなくウェブの閲覧履歴に戻れるようにすることができます。
同様に、SlidingPaneLayout
に対してカスタムの「戻る」ナビゲーションを提供し、アプリが詳細ペインからリストペインに戻るようにする必要があります。
カスタムの「戻る」ナビゲーションを実装する
Sports アプリにカスタムの「戻る」ナビゲーションを実装するには、次の手順を実施する必要があります。
OnBackPressedCallback
をオーバーライドして、[戻る] ボタンの押下を処理するカスタム コールバックを定義します。- コールバック インスタンスを登録して追加します。
まず、カスタム コールバックを定義します。
SportsListFragment
ファイルで、SportsListFragment
クラス定義の下に新しいクラスを追加します。SportsListOnBackPressedCallback
という名前を付けます。SlidingPaneLayout
のprivate
インスタンスをコンストラクタ パラメータとして渡します。
class SportsListOnBackPressedCallback(
private val slidingPaneLayout: SlidingPaneLayout
)
OnBackPressedCallback
からクラスを拡張します。OnBackPressedCallback
クラスはonBackPressed
コールバックを処理します。コンストラクタ パラメータのエラーはこの後すぐ修正します。
class SportsListOnBackPressedCallback(
private val slidingPaneLayout: SlidingPaneLayout
): OnBackPressedCallback()
OnBackPressedCallback
のコンストラクタは、初期有効状態のブール値を取ります。コールバックが有効になっている場合(つまり、isEnabled()
が true を返す場合)に限り、ディスパッチャはコールバックの handleOnBackPressed()
を呼び出して [戻る] ボタンイベントを処理します。
slidingPaneLayout.
isSlideable
*&& slidingPaneLayout.isOpen
* をコンストラクタ パラメータとしてOnBackPressedCallback
に渡します。ブール値isSlideable
は、2 番目のペインがスライド可能な場合(小さな画面上にあり、単一のペインが表示されている場合)にのみ、true になります。isOpen
の値は、2 番目のペイン(コンテンツ ペイン)が完全に開いている場合にtrue
になります。
class SportsListOnBackPressedCallback(
private val slidingPaneLayout: SlidingPaneLayout
): OnBackPressedCallback(slidingPaneLayout.isSlideable && slidingPaneLayout.isOpen)
このコードにより、小さな画面のデバイスでコンテンツ ペインが開いている場合にのみコールバックが有効になることが保証されます。
- 未実装のメソッドに関するエラーを修正するため、赤い電球アイコン をクリックして [Implement members] を選択します。
- [Implement members] ポップアップで [OK] をクリックして、
handleOnBackPressed
メソッドをオーバーライドします。
クラスは次のようになります。
class SportsListOnBackPressedCallback(
private val slidingPaneLayout: SlidingPaneLayout
): OnBackPressedCallback(slidingPaneLayout.isSlideable && slidingPaneLayout.isOpen) {
/**
* Callback for handling the [OnBackPressedDispatcher.onBackPressed] event.
*/
override fun handleOnBackPressed() {
TODO("Not yet implemented")
}
}
handleOnBackPressed()
関数内の TODO ステートメントを削除し、コンテンツ ペインを閉じてリストペインに戻る次のコードを追加します。
slidingPaneLayout.closePane()
SlidingPaneLayout のイベントをモニタリングする
[戻る] ボタンの押下イベントの処理に加えて、スライディング ペインに関連するイベントをリッスンしてモニタリングする必要があります。コンテンツ ペインがスライドする際、それに応じてコールバックを有効または無効にしなければなりません。そのためには、PanelSlideListener
を使用します。
インターフェース SlidingPaneLayout.PanelSlideListener
には、3 つの抽象メソッド onPanelSlide()
、onPanelOpened()
、onPanelClosed()
が含まれています。これらのメソッドは、それぞれ詳細ペインがスライドしたとき、開かれたとき、閉じられたときに呼び出されます。
SlidingPaneLayout.PanelSlideListener
からSportsListOnBackPressedCallback
クラスを拡張します。- エラーを解決するために、3 つのメソッドを実装します。Android Studio で赤い電球アイコンをクリックして、[Implement members] を選択します。
SportsListOnBackPressedCallback
クラスは次のようになります。
class SportsListOnBackPressedCallback(
private val slidingPaneLayout: SlidingPaneLayout
): OnBackPressedCallback(slidingPaneLayout.isSlideable && slidingPaneLayout.isOpen),
SlidingPaneLayout.PanelSlideListener{
override fun handleOnBackPressed() {
slidingPaneLayout.closePane()
}
override fun onPanelSlide(panel: View, slideOffset: Float) {
TODO("Not yet implemented")
}
override fun onPanelOpened(panel: View) {
TODO("Not yet implemented")
}
override fun onPanelClosed(panel: View) {
TODO("Not yet implemented")
}
}
- TODO ステートメントを削除します。
- 詳細ペインが開かれている(表示されている)ときは、
OnBackPressedCallback
コールバックを有効にします。そのためには、setEnabled()
関数を呼び出してtrue
を渡します。onPanelOpened()
内に次のコードを記述します。
setEnabled(true)
- 上記のコードは、プロパティ アクセス構文を使用して簡略化できます。
override fun onPanelOpened(panel: View) {
isEnabled = true
}
- 同様に、詳細ペインが閉じられたときは、
isEnabled
をfalse
に設定します。
override fun onPanelClosed(panel: View) {
isEnabled = false
}
- コールバックを完成させる最後のステップは、詳細ペインのスライド イベントの通知を受け取るリスナーのリストに
SportsListOnBackPressedCallback
リスナークラスを追加することです。SportsListOnBackPressedCallback
クラスにinit
ブロックを追加します。init
ブロック内で、slidingPaneLayout.addPanelSlideListener()
を呼び出してthis
を渡します。
init {
slidingPaneLayout.addPanelSlideListener(this)
}
完成した SportsListOnBackPressedCallback
クラスは次のようになります。
class SportsListOnBackPressedCallback(
private val slidingPaneLayout: SlidingPaneLayout
): OnBackPressedCallback(slidingPaneLayout.isSlideable && slidingPaneLayout.isOpen),
SlidingPaneLayout.PanelSlideListener{
init {
slidingPaneLayout.addPanelSlideListener(this)
}
override fun handleOnBackPressed() {
slidingPaneLayout.closePane()
}
override fun onPanelSlide(panel: View, slideOffset: Float) {
}
override fun onPanelOpened(panel: View) {
isEnabled = true
}
override fun onPanelClosed(panel: View) {
isEnabled = false
}
}
コールバックを登録する
コールバックの実際の動作を確認するため、ディスパッチャ OnBackPressedDispatcher
を使用してコールバックを登録します。
FragmentActivity
の基本クラスを使用すると、OnBackPressedDispatcher
を使用して [戻る] ボタンの動作を制御できます。OnBackPressedDispatcher
は、[戻る] ボタンイベントを 1 つまたは複数の OnBackPressedCallback
オブジェクトにディスパッチする方法を制御します。
addCallback()
メソッドを使用してコールバックを追加します。このメソッドは LifecycleOwner
を受け取ります。これにより、LifecycleOwner
が Lifecycle.State.STARTED
の場合に限り、OnBackPressedCallback
が追加されるようになります。また、アクティビティまたはフラグメントは、関連する LifecycleOwner
が破棄されたときに、登録済みのコールバックを削除します。これにより、メモリリークが防止されるとともに、より寿命が短いフラグメントや他のライフサイクル オーナー内でコールバックを使用しやすくなります。
また、addCallback()
メソッドは、コールバック クラスを 2 番目のパラメータとしてインスタンスに渡します。コールバックを登録する手順は次のとおりです。
SportsListFragment
ファイルの関数onViewCreated()
内で、バインディング変数の宣言のすぐ下にSlidingPaneLayout
のインスタンスを作成し、binding.slidingPaneLayout
の値を割り当てます。
val slidingPaneLayout = binding.slidingPaneLayout
SportsListFragment
ファイルのonViewCreated()
関数内で、slidingPaneLayout
の宣言のすぐ下に次のコードを追加します。
// Connect the SlidingPaneLayout to the system back button.
requireActivity().onBackPressedDispatcher.addCallback(
viewLifecycleOwner,
SportsListOnBackPressedCallback(slidingPaneLayout)
)
上記のコードは、addCallback()
を使用して viewLifecycleOwner
と、SportsListOnBackPressedCallback
のインスタンスを渡します。このコールバックは、フラグメントのライフサイクル中にのみ、アクティブになります。
- 次に、スマートフォン プロファイルを備えたエミュレータでアプリを実行し、カスタムの [戻る] ボタン機能の動作を確認します。
10. ロックモード
スマートフォンなどの小さな画面でリストペインと詳細ペインが重なっている場合、ユーザーはデフォルトでは両方向にスワイプして、ジェスチャー ナビゲーションを使用していない場合でも 2 つのペインを自由に切り替えることができます。SlidingPaneLayout
のロックモードを設定すると、詳細ペインをロックまたはロック解除できます。
- スマートフォン プロファイルを備えたエミュレータで、詳細ペインを画面外にスワイプしてみてください。
- 詳細ペインをスワイプインすることもできます。ご自分で試してみてください。
- これは、Sports アプリでは望ましくない機能です。
SlidingPaneLayout
をロックして、ユーザーがジェスチャーを使用してスワイプインまたはスワイプアウトできないようにする方が適切です。これを実装するには、slidingPaneLayout
定義の下のonViewCreated()
メソッドでlockMode
をLOCK_MODE_LOCKED
に設定します。
slidingPaneLayout.lockMode = SlidingPaneLayout.LOCK_MODE_LOCKED
その他のロックモードについて詳しくは、ドキュメントをご覧ください。
- アプリをもう一度実行して、詳細ペインがロックされていることを確認します。
これで、アプリに SlidingPaneLayout
が追加されました。
11. 解答コード
この Codelab の解答コードは、以下に示すプロジェクトとモジュールにあります。
- プロジェクト用に提供されている GitHub リポジトリ ページに移動します。
- ブランチ名が Codelab で指定されたブランチ名と一致していることを確認します。たとえば、次のスクリーンショットでは、ブランチ名は main です。
- プロジェクトの GitHub ページで、[Code] ボタンをクリックすると、ポップアップが表示されます。
- ポップアップで、[Download ZIP] をクリックして、プロジェクトをパソコンに保存します。ダウンロードが完了するまで待ちます。
- パソコンに保存したファイルを見つけます([ダウンロード] フォルダなど)。
- ZIP ファイルをダブルクリックして展開します。プロジェクト ファイルが入った新しいフォルダが作成されます。
Android Studio でプロジェクトを開く
- Android Studio を起動します。
- [Welcome to Android Studio] ウィンドウで、[Open] をクリックします。
注: Android Studio がすでに開いている場合は、メニューから [File] > [Open] を選択します。
- ファイル ブラウザで、展開したプロジェクト フォルダがある場所([ダウンロード] フォルダなど)に移動します。
- そのプロジェクト フォルダをダブルクリックします。
- Android Studio でプロジェクトが開かれるまで待ちます。
- 実行ボタン をクリックし、アプリをビルドして実行します。正常にビルドされたことを確認します。