快乐的开始开发首页之前,先处理一下底部标签tab的图片
了解一下 react-native-vector-icons 矢量图标库
添加矢量图库插件
# 添加依赖库
yarn add react-native-svg
# 安装 字体组件工具
yarn add -D react-native-iconfont-cli
# 涉及 原生pod依赖,执行一下pod
./pod.sh
# 创建一个iconfont.js
npx iconfont-init
修改配置iconfont.js
修改配置 iconfont.js 的 图库的url。以及是否使用typeScript,和 保存路径。
{
"symbol_url": "http://at.alicdn.com/t/font_2710555_n7b6g9ja71.js",
"use_typescript": true,
"save_dir": "./src/assets/iconfont",
"trim_icon_prefix": "",
"default_icon_size": 18,
"local_svgs": ""
}
执行 npx iconfont-rn 生成 图片组件
npx iconfont-rn
使用图片组件
给底部tab都添加上tabBarIcon,设置Icon
<Tab.Screen
name="Home"
component={Home}
options={{
tabBarLabel: '首页',
tabBarIcon: ({ color, size }) => (
<IconIconHome color={color} size={size} />
),
}}
/>
还有我听,发现,账户,同样的设置,就不贴重复代码了
image.png
首页开发
首页顶部存在 tab,这时需要用到顶部标签导航器
顶部标签导航器
添加依赖
yarn add @react-navigation/material-top-tabs react-native-tab-view react-native-pager-view
# 涉及原生pod引用,需要pod install
./pod.sh
创建src/navigator/HomeTabs.tsx
文件
import React from "react";
import { createMaterialTopTabNavigator } from "@react-navigation/material-top-tabs";
import Home from "@/pages/Home";
//声明变量,接收函数返回值
const Tab = createMaterialTopTabNavigator();
class HomeTabs extends React.Component {
render() {
return (
// {/* 'tabBarOptions' is deprecated. Migrate the options to 'screenOptions' instead. */}
<Tab.Navigator
screenOptions={{
lazy: true,
tabBarScrollEnabled: true,
tabBarItemStyle: {
width: 80,
},
//tab底部横条样式
tabBarIndicatorStyle: {
height: 4,
width: 20,
marginLeft: 30,
borderRadius: 2,
backgroundColor: '#f86442',
},
tabBarActiveTintColor: '#f86442',
tabBarInactiveTintColor: '#333333',
}}
>
<Tab.Screen name="Home" component={Home} options={{ tabBarLabel: '推荐' }} />
<Tab.Screen name="Home1" component={Home} />
<Tab.Screen name="Home2" component={Home} />
</Tab.Navigator>
);
}
}
export default HomeTabs;
将 src/navigator/BottomTabs
中首页视图替换成HomeTabs
<Tab.Screen
name="HomeTabs"
component={HomeTabs}
options={{
tabBarLabel: '首页',
tabBarIcon: ({ color, size }) => (
<IconIconHome color={color} size={size} />
),
}}
/>
首页轮播图
- 引入插件 (第三方库)
yarn add react-native-snap-carousel
# 引入这个库的 申明文件
yarn add @types/react-native-snap-carousel -D
- 封装一个Banner轮播广告控件,自动轮播,定位点
创建文件src/pages/Home/Banner.tsx
控件
import { hp, viewPortWidth, wp } from "@/utils/index";
import React from "react";
import { Image, Platform, StyleSheet, View } from "react-native";
import SnapCarousel, { AdditionalParallaxProps, Pagination, ParallaxImage } from "react-native-snap-carousel";
// 假数据
const data = [
"https://image2.baidu.com/uimg/cms/img/162729136945485175.png",
"https://image2.baidu.com/uimg/cms/img/162743538431638633.png",
"https://image2.baidu.com/uimg/cms/img/162737580088177971.png",
"https://image2.baidu.com/uimg/cms/img/162703143520178503.png",
];
const sliderWidth = viewPortWidth;
const sideWidth = wp(90);
const sideHeight = hp(15);
const itemWidth = sideWidth + wp(2) * 2;
class Banner extends React.Component {
state = {
activeSlide: 0,
}
onSnapToItem = (index: number) => {
this.setState({
activeSlide: index,
});
}
renderItem = ({ item }: { item: string }, parallaxProps?: AdditionalParallaxProps) => {
return (
<ParallaxImage
source={{ uri: item }}
style={styles.image}
containerStyle={styles.imageContainer}
parallaxFactor={0.4}
showSpinner
spinnerColor='rgba(0, 0, 0, 0.25)'
{...parallaxProps}
/>
);
}
//定位点
get pagination() {
const { activeSlide } = this.state;
return (
<View style={styles.paginationWraper}>
<Pagination
containerStyle={styles.paginationContainer}
dotsLength={data.length}
dotContainerStyle={styles.dotContainer}
dotStyle={styles.dot}
activeDotIndex={activeSlide}
inactiveDotScale={0.8}
inactiveDotOpacity={0.4}
/>
</View>
);
}
render() {
return (
<View>
<SnapCarousel
data={data}
renderItem={this.renderItem}
sliderWidth={sliderWidth}
itemWidth={itemWidth}
onSnapToItem={this.onSnapToItem}
hasParallaxImages
loop
autoplay
/>
{this.pagination}
</View>
);
}
}
// 定义样式
const styles = StyleSheet.create({
imageContainer: {
width: itemWidth,
height: sideHeight,
marginBottom: Platform.select({ ios: 0, android: 1 }),
backgroundColor: 'white',
borderRadius: 12,
},
image: {
...StyleSheet.absoluteFillObject,
resizeMode: 'contain',
},
paginationWraper: {
justifyContent: 'center',
alignItems: 'center',
},
paginationContainer: {
position: 'absolute',
top: -20,
backgroundColor: 'rgba(0, 0, 0, 0.35)',
paddingHorizontal: 3,
paddingVertical: 4,
borderRadius: 8
},
dotContainer: {
marginHorizontal: 6,
},
dot: {
width: 6,
height: 6,
borderRadius: 3,
backgroundColor: 'rgba(255, 255, 255, 0.92)',
}
})
export default Banner;
网络请求---- 后台数据
对于一个APP,假数据是肯定不行的,需要从后台请求网络数据,展示到页面上
如果是用于自己学习的,没有服务端数据, 可以 研究一下 Yapi 接口管理平台
- 导入依赖包
yarn add axios
- 封装请求
src/confing/http.ts
import axios from "axios";
import Config from "react-native-config";
// 配置ip:端口
axios.defaults.baseURL = Config.API_BASE_URL;
// 配置请求拦截器
axios.interceptors.request.use(function (config) {
console.log("http.ts:_______请求配置:" + config);
return config;
}, function (error) {
return Promise.reject(error);
})
// 添加响应拦截器
axios.interceptors.response.use(function (response) {
console.log("http.ts:_______响应数据:" + response);
return response.data;
}, function (error) {
return Promise.reject(error);
})
-
src/index.tsx
导入 http.ts -
src/models/home.ts
调用接口
//引入插件
import axios from "axios";
- 修改
/src/models/home.ts
根据接口数据结构,定义数据模型
// body 子节点数据模型
export interface HomeBodyItem{
item: Object,
itemType: string,
sourceModuleType: string,
}
export interface HeaerItemBannerModel
{
cover: string,
link: string,
realLink: string, //路由连接
name: string,
}
// Banner 广告数据节点模型
export interface HeaerItemBanner
{
data: HeaerItemBannerModel[],
responseId: number,
}
// header 子节点的 item 数据模型
export interface HeaerItemModel {
moduleType: string,
title: string,
list: [],//
}
// header 子节点数据模型
export interface HomeHeaderItem {
item: HeaerItemModel,
itemType: string,
}
//数据根节点
export interface HomeState {
header: HomeHeaderItem[],
body: HomeBodyItem[],
}
调用接口封装
import { Effect, Model } from "dva-core-ts";
import { Reducer } from "redux";
import axios from "axios";
const HOME_URL = "/mock/22/home";
interface HomeModel extends Model {
namespace: 'home'; //不可改变,唯一的
state: HomeState; //home的状态,所有数据都会保存到state中
reducers: {
setState: Reducer<HomeState>;
}; //处理同步工作的action
effects: {
fetchHome: Effect;
}; //处理异步工作的,如异步请求
}
const initState = {
header: [],
body: [],
}
const homdeModel: HomeModel = {
namespace: 'home',
state: initState,
reducers: {
setState(state = initState, { payload, type }) {
return {
...state,
...payload,
};
},
},
effects: {
//第一个参数 action,第二个参数,主要是用来完成异步操作
//等3秒执行 add
*fetchHome(_, { call, put }) {
const { header, body} = yield call(axios.get, HOME_URL);
// console.log("___________首页数据header:", header);
// console.log("___________首页数据body:", body);
yield put({
type: 'setState',
payload: {
header: header,
body: body,
}
});
},
},
}
export default homdeModel;
- 首页
/src/page/Home/Home.tsx
调用首页接口,并给Banner控件赋值
//拿到 models 中 home的 num 值
const mapStateToProps = ({ home, loading }: RootState) => ({
header: home.header,
body: home.body,
loading: loading.effects['home/fetchHome'],
});
// ...
/**
* 首页类
*/
class Home extends React.Component<IProps> {
componentDidMount() {
const {dispatch} = this.props;
dispatch({
type: "home/fetchHome",
});
}
onPress = () => {
const { navigation } = this.props;
navigation.navigate("Detail", { id: 100 });
}
render() {
const { header, body, loading } = this.props;
var banner: HeaerItemBannerModel[] = [];
var icons: HeaerItemBannerModel[] = [];
header.forEach(element => {
//banner
if (element.item.moduleType == 'focus' && element.item.list.length > 0) {
//banner广告
element.item.list.forEach((ele: HeaerItemBanner) => {
banner = ele.data;
});
}
//icon广告
else if (element.item.moduleType == 'square' && element.item.list.length > 0) {
//广告Icon
icons = element.item.list;
}
});
return (
<View>
<Text>Home</Text>
<Button title="跳转到详情页" onPress={this.onPress}></Button>
<Banner banner={banner}/>
</View>
);
}
}
- 修改
Banner.tsx
数据源,展示banner广告数据
interface IProps {
banner: HeaerItemBannerModel[],
}
class Banner extends React.Component<IProps> {
...
renderItem = ({ item }: { item: HeaerItemBannerModel }, parallaxProps?: AdditionalParallaxProps) => {
return (
<ParallaxImage
source={{ uri: item.cover }}
...
/>
);
}
//定位点
get pagination() {
const { banner } = this.props;
...
return (
<View style={styles.paginationWraper}>
<Pagination
...
dotsLength={banner.length}
...
/>
</View>
);
}
render() {
const { banner } = this.props;
if (banner != null && banner.length > 0) {
return <View>
<SnapCarousel
data={banner}
... />
{this.pagination}
</View>;
}
else {
return null;
}
}
}
效果图
image.png
ps: 可能存在图片加载不出来,请修改iOS工程的配置文件info.plist
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
重新运行./yarnios.sh
ps: 待完善,一步一个👣👣👣,up~
网友评论