TVFocusGuide
TVFocusGuideは、直観的なTVアプリを作成するために役立ちます。自動フォーカスのサポートにより、フォーカスの検出/復元や、複数回アクセスしたときのフォーカスの記憶が可能になります。トラップやフォーカスリダイレクトもサポートされ、アプリ内でのフォーカス動作をカスタマイズできます。
このコンポーネントを使用すると、フォーカス可能なコントロールがほかのコントロールと一列に並んでいなくても、フォーカス可能な各コントロールに確実に移動できるようになります。RNTesterには、このコンポーネントの2つの異なる使用方法を示す例が含まれています。
| プロパティ | 値 | 説明 |
|---|---|---|
| destinations | any[]? | FocusGuideViewのフォーカスの移動先として登録するComponentの配列。 |
| autoFocus | boolean? | trueの場合、TVFocusGuideが自動的にフォーカスを管理します。初回のアクセス時には、フォーカス可能な最初の子にフォーカスがリダイレクトされます。また、最後にフォーカスを持っていた子が記憶され、次回のアクセス時にはその子にフォーカスがリダイレクトされます。destinationsプロパティが同時に使用された場合は、このプロパティよりも優先されます。 |
| trapFocus*(Up、Down、Left、Right) | 指定の方向について、フォーカスがコンテナから外れないようにします。 |
次のアニメーションは、TVFocusGuideViewを使用した場合のナビゲーション制御を示しています。

上記のフォーカス処理の改善点の詳細については、こちらの記事(英語のみ)を参照してください。
例
import React from 'react';
import {
Dimensions,
View,
StyleSheet,
TouchableHighlight,
Text
} from 'react-native';
import {
TVFocusGuideView
} from '@amazon-devices/react-native-kepler';
const screenHeight = Dimensions.get('window').height;
const scale = screenHeight / 1080;
const width = 200 * scale;
const height = 120 * scale;
const theme = {
LinkColor: '#0984ffff',
LabelColor: '#ffffffff',
TertiarySystemFillColor: '#3085C3',
BackgroundColor: '#0c0700ff'
};
const Button = React.forwardRef((props: $FlowFixMeProps, ref) => {
const [focused, setFocused] = React.useState(false);
return (
<TouchableHighlight
onPress={() => {
if (props.onPress) {
props.onPress();
}
console.log(`${props.label} in press`);
}}
onFocus={() => {
if (props.onFocus) {
props.onFocus();
}
console.log(`${props.label} in focus`);
setFocused(true);
}}
onBlur={() => {
if (props.onBlur) {
props.onBlur();
}
console.log(`${props.label} in blur`);
setFocused(false);
}}
style={focused ? styles.buttonStyleFocused : styles.buttonStyle}
ref={ref}>
<Text style={[{ color: theme.LinkColor }, styles.buttonText]}>
{props.label}
</Text>
</TouchableHighlight>
);
});
const ThemedView = (props: $FlowFixMeProps) => {
return (
<View style={[styles.buttonStyle, props.style]}>
<Text style={[{ color: theme.LabelColor }, styles.buttonText]}>
{props.label}
</Text>
</View>
);
};
const TVFocusGuideExample = () => {
const [destination, setDestination] = React.useState(null);
const [destinationText, setDestinationText] = React.useState('');
const destinations = destination?.current ? [destination?.current] : [];
const buttonTopRight = React.useRef(null);
const buttonBottomLeft = React.useRef(null);
const rightButtonInFocusViewContainer = React.useRef(null);
const containerDestinations = rightButtonInFocusViewContainer?.current
? [rightButtonInFocusViewContainer?.current]
: [];
const _setDestination = (o: Object, text: string) => {
setDestination(o);
setDestinationText(text);
};
return (
<View
style={[styles.container, { backgroundColor: theme.BackgroundColor }]}>
<View style={styles.rowContainer}>
<Button onPress={() => {}} label="左上" />
<Button
onPress={() => {}}
onFocus={() => _setDestination(buttonBottomLeft, '左下')}
ref={buttonTopRight}
label="右上"
/>
<ThemedView label={`フォーカスガイドは${destinationText}を指します`} />
{/* @ts-expect-error */}
<TVFocusGuideView
style={styles.containerFocusGuide}
destinations={containerDestinations}>
<Button onPress={() => {}} label="ラップされたボタン1" />
<Button onPress={() => {}} label="ラップされたボタン2" />
<Button
ref={rightButtonInFocusViewContainer}
onPress={() => {}}
label="ラップされたボタン3"
/>
</TVFocusGuideView>
</View>
<View style={styles.rowContainer}>
<Button
onPress={() => {}}
onFocus={() => _setDestination(buttonTopRight, '右上')}
ref={buttonBottomLeft}
label="左下"
/>
<TVFocusGuideView
style={[
{ backgroundColor: theme.TertiarySystemFillColor },
styles.focusGuide
]}
destinations={destinations}>
<Text style={[{ color: theme.LabelColor }, styles.buttonText]}>
フォーカスガイド
</Text>
</TVFocusGuideView>
<ThemedView label="" />
<ThemedView
style={{
width: width * 3
}}
label="外部からのナビゲーションの場合、上の青いフォーカスガイドコンテナは
常にボタン3を指します"
/>
</View>
</View>
);
};
const marginSize = 20 * scale;
const styles = StyleSheet.create({
container: {
marginTop: -marginSize,
height: screenHeight
},
rowContainer: {
flexDirection: 'row',
padding: 100 * scale
},
buttonText: {
fontSize: 30 * scale
},
buttonStyle: {
width,
height,
marginLeft: marginSize,
marginRight: marginSize,
marginTop: marginSize,
marginBottom: marginSize
},
buttonStyleFocused: {
opacity: 0.5,
borderColor: 'white',
borderWidth: 4,
width,
height,
marginLeft: marginSize,
marginRight: marginSize,
marginTop: marginSize,
marginBottom: marginSize
},
focusGuide: {
width,
height,
marginLeft: marginSize,
marginRight: marginSize,
marginTop: marginSize,
marginBottom: marginSize
},
containerFocusGuide: {
backgroundColor: 'transparent',
borderColor: 'blue',
borderWidth: 2,
flexDirection: 'row'
}
});
export default TVFocusGuideExample;
リファレンス
プロパティ
autoFocus
trueの場合、TVFocusGuideが自動的にフォーカスを管理します。初回のアクセス時には、フォーカス可能な最初の子にフォーカスがリダイレクトされます。また、最後にフォーカスを持っていた子が記憶され、次回のアクセス時にはその子にフォーカスがリダイレクトされます。destinationsプロパティが使用されている場合は、このプロパティよりも優先されます。
| 型 | 必須 |
|---|---|
| ブール型 | × |
trapFocus*(Up、Down、Left、Right)
指定の方向について、フォーカスがコンテナから外れないようにします。
| 型 | 必須 |
|---|---|
| ブール型 | × |
destinations
FocusGuideViewのフォーカスの移動先として登録するコンポーネントの配列です。
| 型 | 必須 |
|---|---|
| 参照の配列 | × |
メソッド
setDestinations
setDestinations([ref1, ref2, ...]);
FocusGuideViewのフォーカスの移動先として登録するコンポーネントの配列です。
| 型 | 必須 |
|---|---|
| 参照の配列 | × |
既知の問題
- コンポーネントがマウントされていない場合、
setDestinationsコールバックは更新に失敗します。代わりにdestinationsプロパティを使用するか、setDestinationsを呼び出す前にsetTimeoutを使用して待ち時間を追加してください。 - 厳密な型チェックを有効にしてTVFocusGuideを.tsxにインポートすると、「
プロパティ'children'は型に存在しません」というエラーで失敗します。アプリでは、エラーを回避するために、コンポーネントを使用する箇所の前に{/* @ts-expect-error */}または{/* @ts-ignore */}を追加できます。
const TVApp = () => {
return (
<View>
{/* @ts-expect-error */}
<TVFocusGuideView>
<Text>Hello World</Text>
</TVFocusGuideView>
</View>
);
};
その他のリソース
その他のライブラリについては、サポート対象のサードパーティのライブラリとサービスを参照してください。
Last updated: 2025年9月30日

