フォーカス管理の概要のビデオ
次のビデオでは、Vega Fire TVデバイスでのフォーカス管理について紹介しています。フォーカス管理戦略
Vegaは、次の3種類のフォーカス管理のパラダイムを提供しています。- デカルトフォーカス管理: これがデフォルト/暗黙のフォーカス動作です。多くのユースケースではこれで十分です。
- フォーカスプロパティとメソッド: アプリ内の特定のコンポーネントでデフォルトのフォーカス動作をオーバーライドする必要がある場合、Vegaでは、フォーカスに関連するさまざまなプロパティとターボモジュールメソッドがサポートされています。これらは、デフォルトの動作が適さない特殊なケースを定義するために使用できます。
- 専用のフォーカスコンポーネント: 場合によっては、デフォルトのデカルト動作とは体系的に異なる動作が必要になることがあります(たとえば、よく使用されるものにTVFocusGuideViewがあります)。このセクションには、Vegaでサポートされているカスタムコンポーネント(
TVFocusGuideViewを含む)の一覧が含まれています。
3 > 2 > 1となります。特化されたコンポーネントはフォーカスプロパティをオーバーライドし、フォーカスプロパティはデフォルトのデカルト動作をオーバーライドします。
Vegaでの基本的なフォーカス処理
フォーカスを示す方法
フォーカスインジケーターは、画面上のどの要素に現在キーボードフォーカスがあるかを示す重要な視覚的表示要素です。これらのインジケーターは通常、背景色や不透明度の調整などのスタイル変更によって実装されますが、アクセシビリティ上の理由から物理的特性の変更を含めることが重要です。 境界線の追加、要素のサイズ変更、UIコンポーネントの追加などの物理的な変更を加えることにより、さまざまな条件下でフォーカス状態の表示が維持されるようになります。物理的な変化は、単なる視覚的な変化(色や不透明度)とは異なり、ハイコントラストモードなどのアクセシビリティ設定を有効にした状態でも検出できます。フォーカス状態での表示がすぐに使用できるコンポーネント
Vegaでは、React Nativeと同様に、ほとんどのコンポーネントについて、プラットフォームレベルでのフォーカス表示が提供されていません。フォーカスすると外観が変わるのは、との2つのコンポーネントだけです。不透明度が0.2に変更され、activeOpacity小道具を使って変更できます。
Vegaでのフォーカスへの応答
onFocusプロパティとonBlurプロパティを使用すると、フォーカスの状態に基づいてコンポーネントの外観を動的に変更できます。これらのプロパティは、それぞれコンポーネントがフォーカスを取得したとき、または失ったときにトリガーされるコールバック関数を受け入れます。
コンポーネントにフォーカスが移動したときに青い境界線を追加する例を以下に示します。
コンポーネントをフォーカス可能にする方法
Viewベースのコンポーネントは、いずれもフォーカスを受け取るように構成できます。コンポーネントをフォーカス可能にするには、focusableまたは accessible (または両方) true をに設定します。accessibleコンポーネントにフォーカスが移らないようにするには、focusableとの両方を設定しますfalse。
focusable``accessibleどちらもデフォルトはベースですがView、必ずしも明示的に設定する必要はありません。false次のコンポーネントは自動的にフォーカス可能になります。
- よく使用されるのは、
TouchableHighlight、TouchableOpacity、TouchableWithoutFeedback、Button、Pressableどのタッチ可能なコンポーネントのグループです。これらには、コンポーネントが「押された」とき(リモコンの選択ボタンやキーボードのEnterキーが押されたとき)のハンドラーを定義するプロパティが用意されているためです。 ScrollView``FlatList基本的なフォーカスマネジメントのサポートも含まれています。インデックスベースのナビゲーションが使用され、フォーカス可能な次のアイテムが画面の外またはScrollViewの境界外にある場合にのみリストがスクロールされます。
フォーカスを命令的に設定する方法
コンポーネントに命令的にフォーカスを設定するには、ターゲットコンポーネントのRefを取得し、.focus()関数を呼び出します。
FocusManagerTurboModuleには、focus(tag)``blur(tag)フォーカス可能なあらゆるコンポーネントで機能する機能も用意されています。詳細については、Focus Manager(英語のみ)を参照してください。
Vegaにおけるコンポーネントのフォーカスの可否
TVアプリで対話型コンポーネントを使用する場合、さまざまなコンポーネントでのフォーカスの動作を理解することが不可欠です。一部のコンポーネントはデフォルトでフォーカスされますが、その他のコンポーネントはfocusableプロパティを使用してフォーカスの可否を指定します。FlatListやTVFocusGuideViewなどのコンテナを使用する場合、これらのコンテナとその子コンポーネントのフォーカス可否設定に応じて、フォーカスの動作が変わります。
Vegaでのフォーカスナビゲーションの動作
このセクションでは、Vegaのフォーカスナビゲーションの動作について、例を挙げながらさまざまなシナリオと予想される動作を紹介します。Vegaでは、Fire OS上のReact Nativeと一貫したエクスペリエンスを提供することを目指しています。デカルトフォーカス管理
デカルトフォーカス管理(または暗黙的フォーカス管理)とは、D-Padキーを押した方向に「最も近い」アイテムにフォーカスを移動する戦略です。シミュレーターでは、矢印キーとEnterキーを使用します。デフォルトの動作をオーバーライドする個別のロジックがアプリに実装されていない限り、Vegaはデカルトフォーカス管理を適用します(動作をオーバーライドするさまざまな方法については、Vegaでのフォーカス動作のカスタマイズに関するセクションを参照してください)。 水平軸と垂直軸に沿って均一な形状のアイテムが配置されているレイアウトでは、フォーカスナビゲーションを直感的に行えます。- 下ボタンを押すと、すぐ下のアイテムにフォーカスが移動します。
- 右ボタンを押すと、隣接するアイテムにフォーカスが水平に移動します。
形状の異なる複数のアイテムが配置されているレイアウトでは、ユーザーがコントロールのD-Padを使用して移動方向を選択すると、その方向とほぼ同じ方向にあるすべての要素がフォーカスの移動先候補になります。その中で重み付け距離が最短の候補(入力方向に沿った距離ほど重み付けが小さくなります)が、次のフォーカス候補と見なされます。
コンポーネントが重なっている場合、フォーカス候補は、ビュー階層内のZオーダーとは無関係に選択されます。たとえば、一部が重なって隠れているコンポーネントがあるとします。上ボタンを押したときに、そのコンポーネントまでの重み付け距離がほかよりも短いと計算されると、離れた位置にある隠れていないコンポーネントではなく、隠れているコンポーネントにフォーカスが設定されます。以下のセクションでは、さまざまなレイアウトでどのようにコンポーネントにフォーカスが移動するか、事例別に詳しく説明します。
さまざまなレイアウトでのデカルトフォーカスナビゲーションアルゴリズムの事例
より複雑なレイアウト、特に形状が不均一なアイテムを扱う場合、フォーカスナビゲーションはコンポーネントのサイズと空間的関係に依存するようになります。Vegaは一貫性を保つためにFire OSと同様のフォーカス検索アルゴリズムを実装していますが、シナリオによっては、アイテムのサイズと場所によってナビゲーション動作が異なる場合があります。 以下に、垂直な経路上に配置されたアイテムにフォーカスが移動しない事例をいくつか紹介します。事例1: 下ボタンを押してもフォーカスが真下のアイテムに移動しないケース
このセットアップは4つのアイテムで構成されており、[上アイテム] にフォーカスがあってユーザーが下ボタンを押すユースケースをシミュレートしています。 このセットアップは4つのアイテムで構成されており、[上アイテム] にフォーカスがあってユーザーが下ボタンを押すユースケースをシミュレートしています。
左側のデモの動作:
- [上アイテム] から下ボタンを押した場合:
- フォーカスが [Small Item] に移動します。
- これは、[Small Item] の下端と [Bottom Item] の上端が水平方向で重なり合っていないためです。
- 同様のコンポーネント配置で、代わりに [Bottom Item] にフォーカスが移動します。
- このように動作が異なるのは、[Small Item] の下端が [Bottom Item] の上端と水平方向で重なり合っているためです。
上側のデモの動作:
- [上アイテム] から下ボタンを押した場合:
- フォーカスが [Small Item] に移動します。
- これは、[Small Item] の左端が [Top Item] の右端と垂直方向で重なり合っているためです。
- 同様のコンポーネント配置の場合: 代わりに [Bottom Item] にフォーカスが移動します。
- これは、[Small Item] の左端が [Top Item] の右端と垂直方向で重なり合っていないためです。
- コンポーネント間のエッジの重なりも考慮されます。
- 次のフォーカス可能な要素までの距離も考慮されます。
- コンポーネントの配置はナビゲーションパスに大きく影響します。
FocusManager.setNextFocus()メソッド。nextFocusUp/nextFocusDown/nextFocusLeft/nextFocusRightプロパティ。FocusManager.focus()プログラムで特定の要素にフォーカスを割り当てることができます。- コンテナ内に移動するときにフォーカスを制御する<TVFocusGuideView>の
destinationプロパティ
事例2: 縦に並んだ異なる幅のアイテム間を移動するときに、全幅の要素にフォーカスが移動しないケース
以下のコンポーネントレイアウトでは、アイテム1にフォーカスがあるときにユーザーが下ボタンを押すと、コンポーネントのレイアウトに応じて、フォーカスが [アイテム1] の下の [全幅アイテム] または [全幅アイテム] に移ります。
シナリオ1: 幅を狭くすると全幅アイテムへのフォーカスはスキップされない
幅を1208から1070に縮小し、高さはそのままにした場合、[アイテム1] から下ボタンを押すと、フォーカスは [全幅アイテム] に移動します。
幅を1208から1270に更新すると、アイテム1から下に移動したときに全幅アイテム2がフォーカスを受け取ります。
シナリオ2: 高さを増やすと全幅アイテムへのフォーカスはスキップされない
[全幅アイテム] の高さを104から105に拡大し、幅はそのままにした場合、[アイテム1] から下ボタンを押すと、フォーカスは [全幅アイテム] に移動します。
高さを104から105に更新すると、アイテム1から下に移動したときに全幅アイテム2がフォーカスを受け取ります。
デフォルトの動作をオーバーライドする実装
デフォルトのフォーカス動作は、すべてのアプリのニーズに合わない場合があります。アプリでは、FocusManager turbo モジュールを使用してsetNextFocus()、コンポーネント間のカスタムフォーカスパスを宣言的に定義できます。
- 下ボタンを押すと、強制的に [アイテム1] から [全幅アイテム] にフォーカスが移動します。
- [アイテム3] から [全幅アイテム] への上方向のナビゲーション
- [全幅アイテム] から [アイテム3] への下方向のナビゲーション
Vegaにおけるフォーカス動作
異なる行のアイテム間のフォーカスの処理
ScrollView``FlatListやなどのリストには、基本的なフォーカス管理サポートが含まれています。インデックスベースのナビゲーションが使用され、フォーカス可能な次のアイテムが画面の外またはScrollViewの境界外にある場合にのみリストがスクロールされます。最後の要素または最初の要素からリストの方向にスクロールすると、フォーカスが次のリストに移動します。たとえば、リストの右端にある最後の要素にフォーカスがある場合、右ボタンを押すとフォーカスが下のリストのタイルに移動します。つまり、フォーカスが要素1.3にある場合、D-padの右ボタンを押すとフォーカスは要素2.3に移動します。
上記の例のデフォルトのフォーカス動作は、ユーザーエクスペリエンスとして望ましくない場合があります。一部のアプリでは、フォーカスを行の末尾で「トラップ」させ、上の例では、フォーカスが要素1.3にある場合、D-Padの右ボタンを押してもフォーカスが移動しないようにする必要があります。FlashList``FlatListとの両方に推奨される実装があります。
フォーカスの復元
プラットフォームの制限 Vegaプラットフォームにはフォーカス復元機能が組み込まれていません。フォーカスの復元は、特定のReact Navigationスタックナビゲーターから行います。 スタックナビゲーターの動作- @amazon-devices/react-navigation__stack
- ❌ フォーカスの自動復元なし
- @amazon-devices/react-navigation__native-stack
- ❌ フォーカスの自動復元なし
フォーカスを復元するための実装オプション
React Navigationで構築されたマルチスクリーンアプリケーションの場合:- 各画面について、最近フォーカスされた要素への参照を保存します。
useFocusEffect()ユーザーが画面に戻ったことを検出するために使用します。useFocusEffect()内部では、FocusManager.setFocus()保存されている参照を呼び出して、正しい要素にフォーカスを戻します。
初期フォーカスの構成
初期フォーカスを管理するには、主に次の2つの方法があります。hasTVPreferredFocusプロップは、コンポーネントの初期マウント時にフォーカスを設定します。このプロパティは初期のレンダリング段階でのみ機能します。useEffectやuseFocusEffect(React Navigationに含まれる)をFocusManager.setFocus()と組み合わせると、特にアプリがReact Navigationで構築された複数ページのアプリ内でフォーカス状態をアクティブに復元する必要がある場合に、より動的なアプローチが可能になります。このアプローチでは、初回のマウント段階以降もフォーカスの動作をより柔軟に細かく制御できます。
非表示のビュー
このセクションでは、ビューが非表示の状態またはマスクされた状態から表示状態に移行したときと、その逆に移行したときのフォーカス管理の動作について説明します。これは、画面間を移動したときやビデオを再生/終了したときに発生する可能性があります。 画面に初めて移動したとき、初期フォーカスは、フォーカス可能なコンポーネントに設定できるhasTVPreferredFocusプロパティの現在の値によって決定されます。
hasTVPreferredFocusプロパティが設定されたコンポーネントがない場合、フォーカスは、ユーザーがいずれかのコンポーネントに移動するまで(D-padキーを押すなど)、画面上のどのコンポーネントにも与えられません。- フォーカスが失われた場合(現在フォーカスのあるコンポーネントをマウント解除した場合など)、フォーカスは、ユーザーがいずれかのコンポーネントに移動するまで、どのコンポーネントにも自動的には与えられません。
- 前の画面に戻ったとき、フォーカスはどのコンポーネントにも自動的には与えられません。
非表示のビューに関する開発者向けガイドライン
ビューがマスクされている場合または非表示になっている場合は常に、表示されていないコンテナに次のプロパティを設定する必要があります。-
pointerEvents: 'none'- あらゆる種類のポインターイベントを無効にして、フォーカス検索ロジックとタッチベースのイベントのヒットテストが実行されないようにします。これにより、ビューがタッチイベントとフォーカスイベントのターゲットになるのを防ぐことができます。 -
accessibilityElementsHidden: true- コンテナは存在していてもユーザーには表示されないため、フォーカス処理に関して、マウントされているコンポーネントをビュー階層から削除します。フォーカス検索ロジックは、このプロパティを使用して非表示のコンテナへのフォーカスを禁止します。
react-navigationライブラリは、画面間を遷移するときにこれらのプロパティの両方を自動的に処理します。詳細については、ナビゲーションに関するドキュメントを参照してください。
また、コンテナに次のプロパティが設定されている場合は常にフォーカス検索ロジックが無効になります。
display: 'none'- コンテナが非表示になるように、内部的にアクセシビリティ状態を設定します。それがフォーカス検索ロジックの計算に利用されます。
フォーカス動作のカスタマイズ
Vegaは、次のフォーカス管理のパラダイムを提供しています。- フォーカスプロパティとメソッド: アプリ内の特定のコンポーネントでデフォルトのフォーカス動作をオーバーライドする必要がある場合、Vegaでは、フォーカスに関連するさまざまなプロパティと
TurboModuleメソッドがサポートされています。これらは、デフォルトの動作が適さない特殊なケースを定義するために使用できます。 - 専用のフォーカスコンポーネント: 場合によっては、デフォルトのデカルト動作とは体系的に異なる動作が必要になることがあります(たとえば、よく使用されるものにTVFocusGuideViewがあります)。このセクションには、Vegaでサポートされているカスタムコンポーネント(
TVFocusGuideViewを含む)の一覧が含まれています。
フォーカスプロパティとメソッド
フォーカスプロパティは、focusableがtrueのコンポーネントに設定できるプロパティで、それらのコンポーネントからのフォーカスの移動方法を変更するために使用します。以下は、各方向のフォーカスプロパティです。nextFocusUpnextFocusDownnextFocusLeftnextFocusRight
nextFocusUp``nextFocusDown``nextFocusLeft、nextFocusRightおよびプロップを設定します。値を同じコンポーネントのノードハンドルに設定すると、指定した方向のボタンの押下動作が実質的にブロックされます。
- 値をundefinedに設定すると、指定した方向のボタンを押したときのデフォルトのデカルト動作が再び有効になります。
- 命令型APIを使用する場合は、
FocusManager``TurboModuleに、フォーカスプロパティと同じ動作をする関数が用意されています。フォーカスがコンポーネントまたはその子から離れないようにするsetFocusRootAPIもあります。詳細については、Focus Manager(英語のみ)を参照してください。
TVFocusGuideViewには、親コンポーネントがアクティブになったときに特定の子コンポーネントにフォーカスを移動したり、コンポーネントからフォーカスが離れないようにしたりするなど、一般的なユースケースが既に含まれています。詳細については、TVFocusGuideViewを参照してください。
FocusManagerターボモジュール
上記のプロパティに加えて、FocusManagerターボモジュールはフォーカス動作のエッジケースを定義するための命令型APIを提供します。サポートされているメソッドのリストについては、Focus Manager(Vega)(英語のみ)を参照してください。
setNextFocus()-clearNextFocus()-setFocusRoot()focus()-blur()getFocused()
専用のフォーカスコンポーネント
TVFocusGuideView
TVFocusGuideViewReact TVFocusGuide NativeのAPIのVega実装を提供します。詳細については、TVFocusGuideViewを参照してください。react-native-tvosこのコンポーネントはからの移植版です。
同期フォーカスイベントのディスパッチ
Vegaには、UIスレッドとJS間でフォーカスイベントを同期的にディスパッチできる新機能が導入されています。UIスレッドは、前のフォーカスイベントまたはフォーカス解除イベントの実行が終了するまで、新しいフォーカス変更を処理せずに待機します。UIスレッドがブロックされている間に受信したほかのフォーカスイベントやフォーカス解除イベントは、キューに入れられます。 同期フォーカスは、キーをすばやく連打したときに発生するフォーカスの問題を解決することを目的としています。- アプリがフォーカスを受け取ったときにアイテムのzIndexを更新すると、zIndexの変更により、アイテムの削除と再挿入が行われます。その結果、アイテムのフォーカスが解除され、フォーカスが失われます。別の状況では、アイテムが<TVFocusGuideView>の内部に配置されている場合、フォーカスが失われる代わりに、
TVFocusGuideによって最初の子にフォーカスが復元されます。
アプリで同期フォーカスを有効にする必要がある場合
次のような場合は、同期フォーカスを使用することを検討してください。- フォーカスが<TVFocusGuideVIew/>内の最初のアイテムにリセットされる問題が発生している。
- 固有のUI要件により、アイテムへのフォーカスの設定または解除に伴うすべての問題を、フォーカスが別のアイテムに移動する前に厳密に適用する必要がある。
- 使用はできるだけ最小限に抑えます。
- 不要な再レンダリングを避けるために、アプリを最適化し、アプリのパフォーマンスに関するベストプラクティスに従う必要があります。
onFocus()とはonBlur()できるだけ軽量に保ち、UIスレッドが必要以上に長くブロックされないようにします。
onFocus()イベントやonBlur()イベントが戻るまで、UIスレッドがブロックされてアイドル状態になることを意味します。その結果、キーをすばやく連打した場合のパフォーマンスとレンダリングにさまざまな影響が生じます。次のセクションでは、いくつかの既知の影響と解決策について詳しく説明します。
既知の影響と解決策
キーをすばやく連打すると長いアニメーションがスキップされる
アイテムへのフォーカスの設定時または解除時にアプリでアニメーションをスケジュールしている場合、アニメーションの再生時間が各キー押下の間隔よりも長いと、アニメーションがスキップされることがあります。アプリでこの動作が発生する場合は、応答性を高めるためにアニメーションの再生時間を短くしてください。不要な再レンダリングによりアプリで遅延が発生する
イベントを同期的にディスパッチするときは、アプリを最適化して不要な再レンダリングを避ける必要があります。最適化の詳細については、アプリのパフォーマンスに関するベストプラクティスを参照してください。 さらに、UIスレッドが必要以上に長くブロックされるのを防ぐために、アプリでは、onFocus()またはonBlur()コールバックに長時間実行されるロジックを含めないようにする必要があります。
フォーカスイベントまたはフォーカス解除イベントのディスパッチ時に進行中のアニメーションでフレーム落ちが発生する
UI スレッドは、JavaScriptonFocus() onBlur() またはコールバックが戻るまでブロックされます。アプリでアニメーションが繰り返し再生されている場合や、イベントのディスパッチと同時に実行されるアニメーションがある場合は、フレーム落ちが発生する可能性があります。
同期フォーカスイベントを有効にする方法
React Native forenableSynchronousFocusEventsVega Vegaはこのプロップを実装しています。アプリでこのプロパティを使用することで、コンポーネントごとにフォーカスイベントのディスパッチを同期するか非同期にするかを選択できます。
プロパティの使用方法:
-
が true
enableSynchronousFocusEventsVegaに設定されている場合。アプリのonFocus()とonBlur()の実装内では、フォーカスをプログラムによって変更するメソッドを呼び出さないようにする必要があります。このようなメソッドには以下が含まれます。requestTVFocus()FocusManager.Focus()および.Blur()ref.focus()およびref.blur()
-
enableSynchronousFocusEventsVega最初のレンダリング後にの値を変更しないでください。 -
enableSynchronousFocusEventsVega共通の親コンテナで値の異なるコンポーネントを混在させることはお勧めしません。 たとえば、コンポーネントのenableSynchronousFocusEventsVegafor の使用法が混在するような次のようなパターンは避けてください。
マルチスクリーンでのフォーカスに関するベストプラクティス
hasTVPreferredFocusは、初回のマウント中にコンポーネントに自動的にフォーカスを描画するための手段です。ただし、これが適切に機能しないエッジケースもあります。たとえば、複数ページのアプリでは、異なるページ間でフォーカスがリセットされる場合があります。hasTVPreferredFocusでページの変更が認識されず、フォーカスが前のページに残ることがあります。
マルチスクリーンのシナリオでは、FocusManagerの方が、より信頼性が高く意図的な方法でアイテムにフォーカスを設定できます。useFocusEffect()from React Navigation FocusManager.setFocus() と組み合わせて使用すると、画面がアクティブになったときにどのコンポーネントにフォーカスが移るかを明示的に制御できます。
react-native、react-native-tvos、react-native-kepler
Vegaプラットフォームは、JavaScriptパッケージであるreact-native-keplerを使用してReact Nativeをサポートしています。これは、react-native-macosなど、ツリー外のほかのプラットフォームと同様のアプローチです。 一般的には、react-native名前空間からAPIとコンポーネントをインポートすることが推奨されます。これにより、アプリ開発者はReact Nativeがサポートするすべてのプラットフォーム間での移植が可能になります。React Nativeのドキュメントで詳しく説明されているように、react-nativeでは、Metroの設定を使用して同じ名前空間を共有することができます。 プラットフォーム固有の機能を扱う場合、このアプローチはより複雑になります。たとえば、FocusManagerのようなプラットフォーム固有のAPIは、react-native-kepler名前空間でしか使用できず、react-nativeからインポートするとエラーが発生します。
Vegaプラットフォームは、マルチモーダルプラットフォームとTVプラットフォームの両方をサポートしているため、さらに複雑さが増します。React Nativeでは、分岐型のアプローチを採用してreact-native-tvosを作成し、TVプラットフォームのサポートやTVFocusGuideViewなどの機能を追加しています。これらはreact-native-tvos名前空間から利用できます。
対照的に、VegaではTV固有のコンポーネント(TVFocusGuideViewなど)をreact-native-kepler名前空間に移植しました。そのため、この単一の名前空間で、マルチモーダルプラットフォームとTVプラットフォームの両方をサポートできます。開発者は@amazon-devices/react-native-kepler名前空間からTV固有のコンポーネントやAPIをインポートできます。
これらの名前空間はのTypeScript定義も提供するためPlatform、Vega SDKタイプではなくコミュニティのreact-nativeタイプを解決するプロジェクトでは、などのプラットフォームブランチのタイプチェックに失敗します。Platform.OS === 'kepler'詳細については、タイプチェック Platform.os を参照してください。





