在这一节中,我们将实现音频的上一首和下一首功能。点击按钮播放上一首或下一首时,我们需要获取上一首和下一首的音频信息。
1. 保存上一首、下一首音频的信息
首先,在跳转到频道详情页面时,将上一首和下一首的音频 ID 保存在页面的 model 中。
const mapStateToProps = ({ album }: RootState) => ({
list: album.list,
});
onItemPress = (item: IAlbum, index: number) => {
const { navigation, dispatch, list } = this.props;
const previousItem: IAlbum = list[index - 1];
const nextItem: IAlbum = list[index + 1];
dispatch({
type: 'player/setState',
payload: {
previousId: previousItem ? previousItem.id : '',
nextId: nextItem ? nextItem.id : '',
},
});
};
为了避免点击一次后无法获取下一首的音频信息,我们需要将整个音频列表保存在 model 中:
playList: list.map(item => ({ id: item.id, title: item.title })),
在 model 中定义类型:
export interface PlayerModelState {
previousId: string;
nextId: string;
playList: { id: string; title: string }[];
}
const initialState: PlayerModelState = {
previousId: '',
nextId: '',
playList: [],
};
2. 添加 next 和 previous 函数
在 effects 中声明 next 和 previous 函数:
effects: {
next: Effect;
previous: Effect;
};
为了避免同时播放多首音频,我们需要在切换音频时先停止之前的音频。我们在 sound.ts 中添加 stop 函数:
const stop = () => {
return new Promise(resolve => {
if (sound) {
sound.stop(() => {
resolve();
});
} else {
resolve();
}
});
};
export { stop };
3. 实现上一首功能
在 effects 中定义上一首的逻辑,暂停当前音频并切换到上一首:
*previous(payload, { put, call, select }) {
yield call(stop);
const { id, sounds }: PlayerModelState = yield select(({ player }: RootState) => player);
const index = sounds.findIndex(item => item.id === id);
let currentIndex = index - 1;
const currentItem = playList[currentIndex];
const previousItem = playList[currentIndex - 1];
yield put({
type: 'setState',
payload: {
playState: 'paused',
id: currentItem.id,
title: currentItem.title,
previousId: previousItem ? previousItem.id : '',
nextId: id,
},
});
yield put({
type: 'fetchShow',
payload: { id: currentItem.id },
});
}
4. 实现下一首功能
下一首的实现与上一首类似,只需调整音频索引:
*next(payload, { put, call, select }) {
yield call(stop);
const { id, sounds }: PlayerModelState = yield select(({ player }: RootState) => player);
const index = sounds.findIndex(item => item.id === id);
let currentIndex = index + 1;
const currentItem = playList[currentIndex];
const nextItem = playList[currentIndex + 1];
yield put({
type: 'setState',
payload: {
playState: 'paused',
id: currentItem.id,
title: currentItem.title,
previousId: id,
nextId: id,
},
});
yield put({
type: 'fetchShow',
payload: { id: currentItem.id },
});
}
5. 添加上一首和下一首按钮
在 PlayControl 组件中,添加上一首和下一首按钮,并根据 previousId 和 nextId 的状态禁用按钮:
const { previousId, nextId } = this.props;
<View style={styles.control}>
<Touchable
onPress={this.previous}
style={styles.button}
disabled={!previousId}>
<Icon name="icon-shangyishou" size={30} color="#fff" />
</Touchable>
<Touchable
onPress={this.next}
style={styles.button}
disabled={!nextId}>
<Icon name="icon-xiayishou" size={30} color="#fff" />
</Touchable>
</View>
const styles = StyleSheet.create({
control: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginVertical: 15,
marginHorizontal: 90,
},
button: {
marginHorizontal: 10,
},
});
在点击按钮时触发 previous 和 next 函数:
previous = () => {
const { dispatch } = this.props;
dispatch({
type: 'player/previous',
});
};
next = () => {
const { dispatch } = this.props;
dispatch({
type: 'player/next',
});
};
6. 更新标题栏
在页面加载时,我们将当前播放音频的标题显示在标题栏中:
const mapStateToProps = ({ player, loading }: RootState) => ({
title: player.title,
});
componentDidMount() {
const { navigation, title } = this.props;
navigation.setOptions({
headerTitle: title,
});
}
componentDidUpdate(prevProps: IProps) {
if (prevProps.title !== this.props.title) {
this.props.navigation.setOptions({
headerTitle: this.props.title,
});
}
}
7. 自定义 Touchable 组件
为了控制按钮的禁用样式,我们自定义了 Touchable 组件:
const Touchable: React.FC<TouchableOpacityProps> = React.memo(props => {
const { style, disabled, ...rest } = props;
const disabledStyle = disabled && styles.disabled;
return (
<TouchableOpacity
disabled={disabled}
style={[style, disabledStyle]}
activeOpacity={0.8}
{...rest}
/>
);
});
const styles = StyleSheet.create({
disabled: {
opacity: 0.5,
},
});
总结
本节实现了上一首和下一首功能,并通过按钮进行控制。通过调整音频索引和暂停当前音频来切换音频。同时,还更新了标题栏显示当前播放的音频标题。下一节我们将实现图片动画和渐变色效果。