# React07
# 回顾
ReactNative 的组件使用
View 组件 根组件 容器组件 没有一些相关样式 color fontSize
Image 组件 图片
Text 文本组件
- RN 中文本必须放在 Text 组件中
- 可以修饰文字相关的样式 color fontSize
- numberOfLines 显示几行 其它隐藏不显示 ...代替
按钮组件
- button
- touchableOpacity 透明容器 有点击反馈效果
- 背景色 填充 圆角
- 文字使用文本组件 文字大小 颜色 间距
状态栏和背景图
可穿透的状态栏 透明的
背景图 ImageBackground 需要动态获取屏幕的宽高 设置背景的图的宽高
- 背景图组件标签作为根标签使用,把其它内容都包裹起来
开关和输入框
Switch、TextInput
活动提示器 loading 小图标
登录页面
# RN 学习
# 登录页面完善
// rnc
import React, { Component } from "react";
import {
Text,
View,
StatusBar,
ImageBackground,
Image,
TextInput,
TouchableOpacity,
StyleSheet,
Dimensions,
} from "react-native";
// 获取宽高
const { width, height } = Dimensions.get("window");
// rpx转物理像素 使用 例如: 35rpx rpx(35)
const rpx = (x) => (width / 750) * x;
export default class App extends Component {
state = { rememberPwd: false };
// 判断是否记住密码的状态 返回对应的图片地址
img_remember() {
return this.state.rememberPwd
? require("./assets/check.png")
: require("./assets/uncheck.png");
}
render() {
return (
<ImageBackground
source={require("./assets/bg1.jpg")}
style={{ width, height }}
blurRadius={6}
>
{/* 沉浸式状态栏 */}
<StatusBar backgroundColor="transparent" translucent></StatusBar>
{/* 撑一个高度为状态栏高度的容器 把状态栏的高度部分撑出来 防止状态栏被内容覆盖 */}
<View style={{ height: StatusBar.currentHeight }}></View>
{/* 把页面其它内容包裹到容器 */}
<View style={ss.content}>
{/* 用户名 */}
<View style={ss.area_input}>
<Image
style={ss.area_input_icon}
source={require("./assets/user.png")}
></Image>
<TextInput
style={ss.area_input_text}
placeholder="用户名"
placeholderTextColor="white"
></TextInput>
</View>
{/* 密码 */}
<View style={[ss.area_input, { marginTop: rpx(30) }]}>
<Image
style={ss.area_input_icon}
source={require("./assets/pwd.png")}
></Image>
<TextInput
secureTextEntry
style={ss.area_input_text}
placeholder="密码"
placeholderTextColor="white"
></TextInput>
</View>
{/* 账户操作区域 */}
<View
style={{
marginTop: rpx(30),
flexDirection: "row",
justifyContent: "space-between",
}}
>
<View style={{ flexDirection: "row" }}>
<TouchableOpacity
style={{ padding: rpx(5) }}
onPress={() =>
this.setState({ rememberPwd: !this.state.rememberPwd })
}
>
<Image
source={this.img_remember()}
style={{ width: rpx(35), height: rpx(35) }}
></Image>
</TouchableOpacity>
<Text style={{ fontSize: rpx(35), color: "white" }}>
记住密码
</Text>
</View>
<TouchableOpacity activeOpacity={0.7}>
<Text style={{ fontSize: rpx(35), color: "white" }}>
忘记密码
</Text>
</TouchableOpacity>
</View>
{/* 登录按钮 */}
<TouchableOpacity
activeOpacity={0.7}
style={{
backgroundColor: "#55c596",
paddingVertical: rpx(10),
alignItems: "center",
borderRadius: rpx(10),
marginTop: rpx(50),
}}
>
<Text
style={{
color: "white",
fontSize: rpx(40),
letterSpacing: rpx(15),
}}
>
登录
</Text>
</TouchableOpacity>
{/* 新用户注册 */}
<TouchableOpacity
style={{
paddingVertical: rpx(10),
alignItems: "center",
borderRadius: rpx(10),
marginTop: rpx(50),
}}
activeOpacity={0.7}
>
<Text style={{ color: "white", fontSize: rpx(35) }}>
新用户注册
</Text>
</TouchableOpacity>
</View>
{/* 其它登录方式 */}
<View
style={{
position: "absolute",
bottom: rpx(150),
alignSelf: "center",
}}
>
{/* 文字 */}
<View style={{ flexDirection: "row", alignItems: "center" }}>
<View style={ss.line}></View>
<View>
<Text
style={{
fontSize: rpx(28),
color: "white",
marginHorizontal: rpx(20),
}}
>
其它方式登录
</Text>
</View>
<View style={ss.line}></View>
</View>
{/* 图标 */}
<View
style={{
marginTop: rpx(30),
flexDirection: "row",
justifyContent: "space-around",
alignItems: "center",
}}
>
<TouchableOpacity activeOpacity={0.7} style={ss.btn_third}>
<Image
source={require("./assets/qq.png")}
style={ss.btn_third_icon}
></Image>
</TouchableOpacity>
<TouchableOpacity activeOpacity={0.7} style={ss.btn_third}>
<Image
source={require("./assets/wx.png")}
style={ss.btn_third_icon}
></Image>
</TouchableOpacity>
<TouchableOpacity activeOpacity={0.7} style={ss.btn_third}>
<Image
source={require("./assets/phone.png")}
style={ss.btn_third_icon}
></Image>
</TouchableOpacity>
</View>
</View>
</ImageBackground>
);
}
}
const ss = StyleSheet.create({
content: {
paddingTop: rpx(200),
paddingHorizontal: rpx(50),
},
area_input: {
backgroundColor: "rgba(0,0,0,0.4)",
padding: rpx(10),
borderRadius: rpx(10),
flexDirection: "row",
alignItems: "center",
},
area_input_icon: {
width: rpx(50),
height: rpx(50),
marginHorizontal: rpx(20),
},
area_input_text: {
fontSize: rpx(40),
color: "white",
flex: 1,
},
line: {
backgroundColor: "#55c596",
height: 5,
width: rpx(200),
},
btn_third_icon: {
width: rpx(80),
height: rpx(80),
},
btn_third: {
backgroundColor: "#55c596",
borderRadius: rpx(60),
padding: rpx(10),
},
});
# 网络请求
// rnc
/***
* View: 不可以滚动
*
* ScrollView: 可以滚动
*
* 将接口数据展示到页面上 实现的基本步骤:
* 发送请求=>接收返回数据=>存储本地=>展示渲染
*
*/
import React, { Component } from "react";
import { Text, View, Dimensions, Image, ScrollView } from "react-native";
// rpx计算
const rpx = (x) => (Dimensions.get("window").width / 750) * x;
export default class App extends Component {
// state 组件状态
state = { result: [] };
// 组件挂在时触发
componentDidMount() {
const url = "https://api.apiopen.top/getWangYiNews";
// 发送请求调用接口
fetch(url)
.then((res) => res.json())
.then((res) => {
console.log(res);
/***
* RN中 控制台打印的信息 需要在命令行控制台查看
* 使用起来不方法,RN提供了debug调试模式
* 注意:调试模式目前还不是太稳定,需要时候打开它,不使用的时候关闭。否则,会出现一些奇怪的问题。异常的报错,一些功能异常失效。
* 调试模式打开方式:
* 1.cmd里 敲d 打开开发者菜单
* 2.模拟器里选择菜单 debug
* 3.浏览器弹出debug ui工具
* 打开console就可以看到打印信息了
*
* 关闭和打开过程类似 先cmd里敲d 模拟器选择stopping dugging
*
* */
this.setState({ result: res.result });
});
}
showNews() {
return this.state.result.map((item, index) => {
return (
<View
key={index}
style={{ flexDirection: "row", borderBottomWidth: 1 }}
>
<Image
source={{ uri: item.image }}
style={{ width: rpx(250), height: rpx(150) }}
></Image>
<View style={{ flex: 1, justifyContent: "space-between" }}>
<Text style={{ fontSize: rpx(25) }}>{item.title}</Text>
<Text style={{ fontSize: rpx(25) }}>{item.passtime}</Text>
</View>
</View>
);
});
}
render() {
return (
// 用ScrollView替换原来的View 使页面可以滚动
<ScrollView>
{/* 调用方法 实现页面的展示 */}
{this.showNews()}
</ScrollView>
);
}
}
# FlatList
高能性能列表组件,提供了多个属性使用。方便开发者快速开一些功能。
# 核心属性
FlatList 三个核心属性:
- data 需要显示的数组数据
- renderItem 数组每个元素对应的 UI 界面标签和样式
- keyExtractor 每一个 UI 标签的唯一标识
// rnc
import React, { Component } from "react";
// 导入组件FlatList
import { Text, View, FlatList } from "react-native";
export default class App extends Component {
skills = [
"Vue",
"React",
"Angular",
"HTML",
"CSS",
"JavaScript",
"测试1",
"测试2",
"测试1",
"测试2",
"测试1",
"测试2",
"测试1",
"测试2",
"测试1",
"测试2",
"测试1",
"测试2",
"测试1",
"测试2",
"测试1",
"测试2",
"测试1",
"测试2",
"测试1",
"测试2",
"测试1",
"测试2",
"测试1",
"测试2",
"测试1",
"测试2",
"测试1",
"测试2",
"测试1",
"测试2",
];
render() {
return (
// FlatList 核心属性
// data 遍历的数据
// renderItem 处理每个元素的标签和样式 调用方法为了和属性的名称进行区分 习惯加_
// keyExtractor 唯一值key 渲染时提供效率
<FlatList
data={this.skills}
renderItem={this._renderItem}
keyExtractor={(item, index) => index}
></FlatList>
);
}
// 传入的参数是每一个数组元素的对象形式
// {index,item} 把传入的数据进行了解构 只获取item和index
_renderItem = ({ index, item }) => (
<Text style={{ fontSize: 35 }}>
{index + 1}、{item}
</Text>
);
// one是数组中每个数据项 传入的是对象形式 后续解构使用
_renderItem1 = (one) => {
console.log(one);
const { index, item } = one;
return (
<Text style={{ fontSize: 35 }}>
{index}。{item}
</Text>
);
};
}
# 可选属性
ItemSeparatorComponent 行与行之间的分隔组件
ListHeaderComponent 显示到 FlatList 组件头部
ListFooterComponent 显示到 FlatList 组件尾部
// rnc
import React, { Component } from "react";
import { FlatList, Text, View } from "react-native";
/***
* 通过FlatList 遍历显示数组对象数据
* 核心属性:
* data 数据
* renderItem 每一个元素标签样式
* keyExtractor key 唯一值
* 可选属性 不是必需的 使用可以实现一些效果
* ItemSeparatorComponent 行与行之间的分隔组件
* ListHeaderComponent 显示到FlatList组件头部
* ListFooterComponent 显示到FlatList组件尾部
*
*
*
*/
export default class App extends Component {
heros = [
{ name: "后羿", job: "射手", price: 3888 },
{ name: "孙尚香", job: "射手", price: 6888 },
{ name: "廉颇", job: "游走", price: 13888 },
{ name: "韩信", job: "打野", price: 13888 },
{ name: "扁鹊", job: "法师", price: 8888 },
];
render() {
return (
<FlatList
data={this.heros}
renderItem={this._renderItem}
keyExtractor={(item, index) => index}
ItemSeparatorComponent={this._ItemSeparatorComponent}
ListHeaderComponent={this._ListHeaderComponent}
ListFooterComponent={this._ListFooterComponent}
></FlatList>
);
}
// 尾部组件
_ListFooterComponent() {
return (
<Text style={{ fontSize: 35, color: "green" }}>本场红方英雄为5个</Text>
);
}
// 头部组件
_ListHeaderComponent = () => (
<Text style={{ fontSize: 35, color: "red" }}>本局红方阵容:</Text>
);
// 分隔组件
_ItemSeparatorComponent = () => (
<View style={{ height: 3, backgroundColor: "gray" }}></View>
);
_renderItem = ({ item }) => {
return (
<View>
<Text style={{ fontSize: 30 }}>名称:{item.name}</Text>
<Text style={{ fontSize: 30 }}>职业:{item.job}</Text>
<Text style={{ fontSize: 30 }}>价格:{item.price}</Text>
{/* <View style={{height: 3, backgroundColor: 'gray'}}></View> */}
</View>
);
};
}
# 加载更多、下拉刷新、回到顶部
// rnc
import React, { Component } from "react";
import {
Text,
View,
FlatList,
Dimensions,
Image,
ActivityIndicator,
TouchableOpacity,
} from "react-native";
const { width, height } = Dimensions.get("window");
const rpx = (x) => (width / 750) * x;
export default class App extends Component {
// data 接口返回数据 refreshing 下拉刷新状态 showBtn 回到顶部显示状态
state = { data: null, refreshing: false, showBtn: false };
url(page = 1) {
return "https://api.jiasiyuan.cn/news?page=" + page;
}
componentDidMount() {
fetch(this.url())
.then((res) => res.json())
.then((res) => {
console.log(res);
// 详细格式化的数据 可以通过调试模式
this.setState({ data: res });
});
}
render() {
// 判断远程数据未返回时,不渲染
if (this.state.data === null) return <View></View>;
return (
<View>
<FlatList
data={this.state.data.result}
renderItem={this._renderItem}
keyExtractor={(item, index) => index}
ItemSeparatorComponent={this._ItemSeparatorComponent}
ListHeaderComponent={this._ListHeaderComponent}
ListFooterComponent={this._ListFooterComponent}
// 触底事件
onEndReached={this._onEndReached}
// 触底的阈值 未显示高度的百分比 默认是50% 触发太早了,习惯上修改为10%
onEndReachedThreshold={0.1}
// 下拉刷新事件
onRefresh={this._onRefresh}
// 刷新动画动态 bool类型 true为刷新 false为不刷新
refreshing={this.state.refreshing}
// 监控滚动
onScroll={this._onScroll}
// 子元素组件方法的调用 需要先绑定ref
// el 当前FlatList组件标签
// f1 代表绑定的变量
ref={(el) => (this.f1 = el)}
></FlatList>
{/* 回到顶部按钮 */}
{/* 根据判断showBtn 确定是否显示按钮 */}
{this.state.showBtn ? (
<TouchableOpacity
onPress={() => this.f1.scrollToIndex({ index: 0 })}
activeOpacity={0.7}
style={{
position: "absolute",
bottom: rpx(100),
right: rpx(50),
backgroundColor: "red",
padding: rpx(10),
borderRadius: rpx(10),
}}
>
<Text style={{ fontSize: rpx(30), color: "white" }}>回到顶部</Text>
</TouchableOpacity>
) : null}
</View>
);
}
// 监控滚动事件处理方法
_onScroll = (e) => {
// 获取竖向滚动距离 和顶部的距离
// RN的事件处理方式 经过特殊处理的 如果需要打印其中的值 需要在之前调用persist()
// e.persist();
// console.log(e.nativeEvent.contentOffset.y);
// 距离1500以上时 showBtn为true 显示按钮
this.setState({ showBtn: e.nativeEvent.contentOffset.y > 1500 });
};
// 下拉刷新事件处理方法
_onRefresh = () => {
console.log("下拉刷新触发了");
// 下拉之后loading加载 当下拉数据返回之后,再修改为false关闭
this.setState({ refreshing: true });
fetch(this.url())
.then((res) => res.json())
.then((res) => {
console.log(res);
this.setState({ data: res, refreshing: false });
});
};
// 触底事件处理方法
_onEndReached = () => {
// console.log('触底了加载数据吧');
// 加载翻页数据 当前页+1
// page当前页数 pageCount最大页数
const { page, pageCount } = this.state.data;
// 判断当前页数等于最大页数 后续没有数据 不进行请求了
if (page == pageCount) {
return;
}
fetch(this.url(page + 1))
.then((res) => res.json())
.then((res) => {
console.log(res);
// 拼接数据 拼数组
res.result = [...this.state.data.result, ...res.result];
this.setState({ data: res });
});
};
// 头部组件
_ListHeaderComponent = () => (
<View style={{ backgroundColor: "red", alignItems: "center" }}>
<Text style={{ color: "white", fontSize: rpx(40), fontWeight: "800" }}>
网易新闻
</Text>
</View>
);
// 底部组件 加载组件
_ListFooterComponent = () => {
// 获取当前页数和最大页数
const { page, pageCount } = this.state.data;
// 判断当前页就是最大页 后续没有数据 返回没有数据的文本信息
if (page == pageCount) {
return (
<View
style={{
alignItems: "center",
padding: rpx(10),
backgroundColor: "#cccccc",
}}
>
<Text style={{ fontSize: rpx(30) }}>
生活就像卫生纸,扯着扯着就没了~
</Text>
</View>
);
}
return (
<View
style={{
alignItems: "center",
padding: rpx(10),
backgroundColor: "#cccccc",
}}
>
<ActivityIndicator color="red" size="large"></ActivityIndicator>
<Text style={{ fontSize: rpx(30) }}>加载中...</Text>
</View>
);
};
// 分隔组件
_ItemSeparatorComponent = () => (
<View style={{ height: 1, backgroundColor: "#757575" }}></View>
);
// 每个元素的渲染
_renderItem = ({ item }) => (
<View style={{ flexDirection: "row" }}>
<Image
source={{ uri: item.image }}
style={{
width: rpx(250),
height: rpx(150),
marginRight: rpx(10),
}}
></Image>
<View style={{ flex: 1, justifyContent: "space-between" }}>
<Text style={{ fontSize: rpx(30) }}>{item.title}</Text>
<Text style={{ fontSize: rpx(30) }}>{item.passtime}</Text>
</View>
</View>
);
}