パート2: 最終コード
パート2: 最終コード
オープンベータ版ドキュメント:本テクニカルドキュメントは、リリース前のオープンベータ版の一部としてAmazonから提供されるものです。ここで説明されている機能は、Amazonがフィードバックを受け取り、機能の開発を繰り返す過程で変更される可能性があります。最新の機能の情報については、リリースノートを参照してください。
次のステップ
基本的なビデオアプリを作成したら、Vegaの学習を続ける方法をいくつかご紹介します。
-
フォーラムに参加する: 他のVega開発者とつながり、経験を共有し、開発者向けコミュニティ(英語のみ)でプロジェクトの支援を受けましょう。
-
他のサンプルアプリもチェックしてください。 サンプルアプリのコレクションをご覧ください。
最終コード
ラボの最終コードは次のとおりです。
フォルダ構造
src/
├── components/
| ├── index.ts
│ ├── Button.tsx
│ ├── Header.tsx
│ └── VideoCard.tsx
├── screens
| ├── index.ts
│ ├── LandingScreen.tsx
| ├── VideoDetailScreen.tsx
│ └── VideoPlaybackScreen.tsx
└── App.tsx
コンポーネント
index.ts
import Header from './Header';
import VideoCard from './VideoCard';
import Button from './Button';
export {
Header,
VideoCard,
Button,
}
Header.tsx
import * as React from 'react';
import {StyleSheet, Text, View} from 'react-native';
const Header = () => {
return (
<View style={styles.headerContainer}>
<Text style={styles.headerText}>Vegaへようこそ!</Text>
</View>
);
};
const styles = StyleSheet.create({
headerContainer: {
padding: 10,
alignItems: 'center',
backgroundColor: '#232F3E',
width: '100%',
},
headerText: {
fontSize: 50,
fontWeight: '700',
color: '#FF9900',
},
});
export default Header;
VideoCard.tsx
import React, {useState} from 'react';
import {StyleSheet, Text, TouchableOpacity, Image, View} from 'react-native';
interface IProps {
title: string;
imgURL: string;
description: string;
pressFunction: Function;
}
const VideoCard = ({title, imgURL, description, pressFunction}: IProps) => {
const [focused, setFocused] = useState(false);
return (
<TouchableOpacity
style={[
styles.videoCardContainer,
focused ? styles.focused : styles.unfocused,
]}
onPress={() => pressFunction()}
onFocus={() => setFocused(true)}
onBlur={() => setFocused(false)}>
<Image style={styles.videoImage} source={{uri: imgURL}} />
<View style={styles.videoTextContainer}>
<Text style={styles.videoTitle}>{title}</Text>
<Text style={styles.videoDescription}>{description}</Text>
</View>
</TouchableOpacity>
);
};
const styles = StyleSheet.create({
videoCardContainer: {
height: 400,
width: 550,
margin: 10,
borderRadius: 5,
},
unfocused: {
borderWidth: 1,
borderColor: 'white',
},
focused: {
borderWidth: 5,
borderColor: 'yellow',
},
videoTextContainer: {
height: '25%',
display: 'flex',
justifyContent: 'space-around',
padding: 10,
},
videoImage: {
height: '75%',
},
videoTitle: {
fontSize: 20,
fontWeight: 'bold',
color: 'white',
},
videoDescription: {
color: 'white',
},
});
export default VideoCard;
Button.tsx
画面
index.ts
import LandingScreen from './LandingScreen';
import VideoPlaybackScreen from './VideoPlaybackScreen';
import VideoDetailScreen from './VideoDetailScreen';
export {
LandingScreen,
VideoPlaybackScreen,
VideoDetailScreen
}
LandingScreen.tsx
import React, {useState, useEffect} from 'react';
import {FlatList, StyleSheet, View, Text} from 'react-native';
import {Header, VideoCard} from '../components';
import {TVFocusGuideView} from '@amazon-devices/react-native-kepler';
interface IVideo {
id: string;
title: string;
description: string;
duration: number;
thumbURL: string;
imgURL: string;
videoURL: string;
categories: Array<string>;
channel_id: number;
}
const LandingScreen = ({navigation}: any) => {
const [islandVideos, setIslandVideos] = useState<IVideo[]>([]);
const [underwaterVideos, setUnderwaterVideos] = useState<IVideo[]>([]);
const url = 'https://d2ob7xfxpe6plv.cloudfront.net/TestData.json';
const getAllVideos = () => {
fetch(url)
.then((response) => response.json())
.then((data) => {
// 各カテゴリの動画をフィルタリングします
const islands = data.testData.filter(
(video: IVideo) =>
video.categories && video.categories.includes('コスタリカの島々'),
);
const underwater = data.testData.filter(
(video: IVideo) =>
video.categories &&
video.categories.includes('コスタリカの水中'),
);
setIslandVideos(islands);
setUnderwaterVideos(underwater);
})
.catch((error) => {
console.log(error);
});
};
useEffect(() => {
getAllVideos();
}, []);
return (
<>
<Header />
<TVFocusGuideView autoFocus={true}>
<Text style={styles.categoryTitle}>コスタリカの島々</Text>
<FlatList
style={styles.flatList}
horizontal
data={islandVideos}
renderItem={({item}) => (
<View style={styles.itemContainer}>
<VideoCard
title={item.title}
description={
item.description.split(' ').slice(0, 20).join(' ') + '...'
}
imgURL={item.imgURL}
pressFunction={() =>
navigation.navigate('VideoDetailScreen', {video: item})
}
/>
</View>
)}
/>
</TVFocusGuideView>
<TVFocusGuideView autoFocus={true}>
<Text style={styles.categoryTitle}>コスタリカの水中</Text>
<FlatList
style={styles.flatList}
horizontal
data={underwaterVideos}
renderItem={({item}) => (
<View style={styles.itemContainer}>
<VideoCard
title={item.title}
description={
item.description.split(' ').slice(0, 20).join(' ') + '...'
}
imgURL={item.imgURL}
pressFunction={() =>
navigation.navigate('VideoDetailScreen', {video: item})
}
/>
</View>
)}
/>
</TVFocusGuideView>
</>
);
};
const styles = StyleSheet.create({
flatList: {
padding: 10,
},
itemContainer: {
margin: 10,
},
categoryTitle: {
fontSize: 24,
fontWeight: 'bold',
color: 'white',
marginLeft: 30,
},
});
export default LandingScreen;
VideoDetailScreen.tsx
import React from 'react';
import {ImageBackground, Text, View, StyleSheet} from 'react-native';
import {Button} from '../components';
const VideoDetailScreen = ({navigation, route}: any) => {
const video = route.params.video;
return (
<ImageBackground
source={{uri: video.imgURL}}
imageStyle={styles.image}
style={styles.screenContainer}>
<Text style={styles.videoTitle}>{video.title}</Text>
<Text style={styles.videoDescription}>{video.description}</Text>
<View style={styles.buttonContainer}>
<Button
buttonText="今すぐ観る"
pressFunction={() =>
navigation.navigate('VideoPlaybackScreen', {
videoURL: video.videoURL,
})
}
/>
<Button buttonText="戻る" pressFunction={() => navigation.goBack()} />
</View>
</ImageBackground>
);
};
const styles = StyleSheet.create({
image: {
opacity: 0.2,
},
screenContainer: {
display: 'flex',
height: '100%',
justifyContent: 'center',
},
videoTitle: {
fontSize: 50,
color: 'white',
fontWeight: '700',
margin: 30,
},
videoDescription: {
color: 'white',
fontSize: 30,
margin: 30,
},
buttonContainer: {
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
marginLeft: 30,
width: '18%',
},
});
export default VideoDetailScreen;
VideoPlaybackScreen.tsx
import React from 'react';
import {View, StyleSheet, Text} from 'react-native';
import {Button} from '../components';
const VideoPlaybackScreen = ({navigation, route}: any) => {
return (
<View style={styles.playerContainer}>
<Text style={styles.playerPlaceholder}>{route.params.videoURL}</Text>
<View style={styles.buttonContainer}>
<Button buttonText="戻る" pressFunction={() => navigation.goBack()} />
</View>
</View>
);
};
const styles = StyleSheet.create({
playerContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
buttonContainer: {
width: 100,
position: 'absolute',
top: 10,
left: 10,
},
playerPlaceholder: {
color: 'white',
fontSize: 30,
},
});
export default VideoPlaybackScreen;
App.tsx
import * as React from 'react';
import { NavigationContainer, DefaultTheme } from '@amazon-devices/react-navigation__native';
import { createStackNavigator } from '@amazon-devices/react-navigation__stack';
import { LandingScreen, VideoDetailScreen, VideoPlaybackScreen } from './screens';
export const App = () => {
const Stack = createStackNavigator();
const AppTheme = {
...DefaultTheme,
colors: {
...DefaultTheme.colors,
background: "#232F3E",
text: "white",
},
};
return (
<NavigationContainer theme={AppTheme}>
<Stack.Navigator screenOptions={{ headerShown: false }}>
<Stack.Screen name='LandingScreen' component={LandingScreen}/>
<Stack.Screen name='VideoDetailScreen' component={VideoDetailScreen}/>
<Stack.Screen name='VideoPlaybackScreen' component={VideoPlaybackScreen}/>
</Stack.Navigator>
</NavigationContainer>
);
}

