react native入门及学习总结

原创
04/28 21:45
阅读数 107

环境搭建参考官网

hello world

import React from 'react';
import {
  //样式对象
  StyleSheet,
  //视图组件
  View,
  //文本组件
  Text,
} from 'react-native'

const App= () => {
  //定义视图的JSX对象
  //JSX对象只能有1个根对象
  //创建 UI 时最基础的组件view。类似于网页中的div
  //文本的内容需要放置到Text组件中
  return (
    <>
      <View style={styles.view}>
        <Text>helloworld</Text>
      </View>
    </>
  );
};

//定义样式对象
const styles = StyleSheet.create({
  view: {
    height:200,
    width:200,
    //驼峰命名法,第二个单词首字母大写
    backgroundColor: "rgba(200,255,0,0.5)",
  },
});

//导出组件
export default App;

样式

{/* 没有样式继承的,每一个组件都要单独设置样式 */}
<View style ={[styles.txt,{color:'#333300'}]}>
<Text>88888</Text>
</View>
{/* 直接设置样式对象 */}
<View style={styles.card}></View>
{/* 创建样式对象 */}
<View style={{marginTop:8,marginBottom:8,height:100,backgroundColor:'blue'}}></View>
{/* 合并多个样式对象,同样的设置,右边的优先级越高 */}
<View style={[styles.card, {backgroundColor:'yellow'}]}></View>

登录页面小案例

import React, { Component } from 'react'
import { View, Text, TouchableOpacity, TextInput, StyleSheet } from 'react-native'

class Inputs extends Component {
   state = {
      email: '',
      password: '',
      intro:'',
   }
   handleEmail = (text) => {
      this.setState({ email: text })
   }
   handlePassword = (text) => {
      this.setState({ password: text })
   }

   handleIntro = (text) => {
      this.setState({ intro: text })
   }

   register = (email, pass,intro) => {
      alert('email: ' + email + '\npassword: ' + pass + "\nintro:" + intro)
   }
   render() {
      return (
         <View style = {styles.container}>
            <TextInput 
               style = {styles.input}
              // 下划线的颜色,透明则为 transparent
               underlineColorAndroid = "transparent"
               //占位符
               placeholder = "请输入邮箱"
              //  占位符的字体颜色
               placeholderTextColor = "#ccc"
              //  字母大写模式:'none', 'sentences', 'words', 'characters'
               autoCapitalize = "none"
              //  键盘类型,可选的值有 "default","number-pad","decimal-pad", "numeric","email-address","phone-pad"
               keyboardType = "email-address"
              //  键盘上的返回键类型,可选的值有 "done","go","next","search","send"
               returnKeyType = "next"
              //  文本变更后的回调函数,参数为输入框里的文本
               onChangeText = {this.handleEmail}/>

            <TextInput 
               style = {styles.input}
               underlineColorAndroid = "transparent"
               placeholder = "请输入密码"
               placeholderTextColor = "#ccc"
               autoCapitalize = "none"
               returnKeyType = "next"
              //  是否属于密码框类型
               secureTextEntry = {true}
               onChangeText = {this.handlePassword}/>

            <TextInput 
               style = {[styles.input,{height:100}]}
               underlineColorAndroid = "transparent"
               placeholder = "请输入描述"
               placeholderTextColor = "#ccc"
               autoCapitalize = "none"
              //  多行设置
               multiline = {true}
              //  行数
               numberOfLines = {4}
               //文字的位置靠上
               textAlignVertical="top"
               returnKeyType="done"
               onChangeText = {this.handleIntro}/>
            {/* 封装视图,使其可以正确响应触摸操作 */}
            <TouchableOpacity
               style = {styles.submitButton}
               onPress = {
                  () => this.register(this.state.email, this.state.password,this.state.intro)
               }>
               <Text style = {styles.submitButtonText}>注册</Text>
            </TouchableOpacity>
         </View>
      )
   }
}
export default Inputs

const styles = StyleSheet.create({
   container: {
      paddingTop: 23
   },
   input: {
      margin: 15,
      paddingLeft:8,
      height: 40,
      borderColor: '#eeeeee',
      borderWidth: 1
   },
   submitButton: {
      backgroundColor: '#7a42f4',
      padding: 10,
      alignItems:'center',
      margin: 15,
      height: 40,
   },
   submitButtonText:{
      color: 'white'
   }
})

图片组件

import React, { Component } from 'react';
import { AppRegistry, View, Image,ScrollView } from 'react-native';

export default class App extends Component {
  render() {
    return (
      <ScrollView>
         {/* 普通图片设置 */}
        <Image
          source={require('./assets/1.png')}
        />
        {/* 网络图片设置 */}
        <Image
          style={{margin:10,width: 177, height: 100}}
          source={{uri: 'https://www.twle.cn/static/i/img1.jpg'}}
        />
        {/* 图片显示模式,contain,安装正常的比例缩放到整个可以刚好放进来 */}
        <Image
          style={{margin:10,width: 200, height: 200,resizeMode:'contain',borderWidth:1,borderColor:'#000'}}
          source={require('./assets/img1.jpg')}
        />
        {/* 不会变形,放大图片至刚好覆盖住整个内容 */}
        <Image
          style={{margin:10,width: 200, height: 200,resizeMode:'cover',borderWidth:1,borderColor:'#000'}}
          source={require('./assets/img1.jpg')}
        />
        {/* 直接将图片拉升成设置的大小,会变形 */}
        <Image
          style={{margin:10,width: 200, height: 200,resizeMode:'stretch',borderWidth:1,borderColor:'#000'}}
          source={require('./assets/img1.jpg')}
        />
      </ScrollView>
    );
  }
}

动画组件

import React, { Component } from 'react';
import { ActivityIndicator, View, Text, TouchableOpacity, StyleSheet,Button } from 'react-native';

class App extends Component {
   state = { animating: true }

   closeActivityIndicator = () => {
      this.setState({animating:!this.state.animating })
      
   }

   //生命周期函数
   componentDidMount = () => this.closeActivityIndicator()
   render() {
      const animating = this.state.animating
      return (
         <View style = {styles.container}>
            {/* 活动指示器,切换animating属性进行显示隐藏切换 */}
            <ActivityIndicator
               animating = {animating}
               color = '#bc2b78'
               size = "large"
               style = {styles.activityIndicator}/>
            <Button onPress={this.closeActivityIndicator} title="切换显示loading"></Button>
         </View>
      )
   }
}
export default App

const styles = StyleSheet.create ({
   container: {
      flex: 1,
      marginTop: 70
   },
   activityIndicator: {
      height: 80
   }
})

弹框

import React from 'react'
import { Alert, Text, TouchableOpacity, StyleSheet, View } from 'react-native'

const App = () => {

    const showAlert1 = () =>{
        Alert.alert('发送数据成功')
    }
    const showTip = () => {
      Alert.alert('删除数据成功')
    }
    const showAlert2 = () =>{
      Alert.alert(
          '警告',
          '确认删除?',
          [
              {text: '确认', onPress: () => showTip()},
              {text: '取消', style: 'cancel'}, 
          ],
          {cancelable: false}
      )
   }

    return (
       <View style={{alignItems:"center"}}>
         <TouchableOpacity onPress = {showAlert1} style = {styles.button}>
            <Text>发送</Text>
         </TouchableOpacity>
         <TouchableOpacity onPress = {showAlert2} style = {styles.button}>
            <Text>删除</Text>
        </TouchableOpacity>
        
        </View>
    )
}
export default App

const styles = StyleSheet.create ({
    button: {
        backgroundColor: '#4ba37b',
        width: 100,
        height:50,
        borderRadius: 50,
        justifyContent:'center',
        alignItems: 'center',
        marginTop: 100,
    }
})

数据存储组件

import React, { Component } from 'react'
import { Text, View, Alert,TextInput, StyleSheet,TouchableHighlight } from 'react-native'
import { AsyncStorage } from "react-native"

export default class App extends Component {
   state = {
      'name': '老陈,你胖吗?',
      'inputText':'不是清楚,最近体重称坏了,会多转1圈,才显示十几斤',
   }

   async readName() {
        try {
         //   读取数据
          const value = await AsyncStorage.getItem('name')
          if(value !== null) {
              this.setState({ 'name': value })
          }
          Alert.alert("读取数据成功")
        } catch(e) {
          console.log(e);
          Alert.alert("读取数据失败!")
        }
   }

   setName = () => {
      // 保存数据
      AsyncStorage.setItem('name', this.state.inputText);
      Alert.alert("保存成功!")
   }
   render() {
      return (
         <View style = {styles.container}>
            <TextInput 
              style = {styles.textInput} 
              autoCapitalize = 'none' 
              value={this.state.inputText} />
            <View style={{flexDirection:'row'}}>
                <TouchableHighlight style={[styles.button,{marginRight:8}]} onPress={this.setName}>
                    <Text style={styles.buttonTxt}>保存</Text>
                </TouchableHighlight>
                <TouchableHighlight style={[styles.button,{backgroundColor:'blue'}]} onPress={this.readName.bind(this)}>
                    <Text style={styles.buttonTxt}>读取</Text>
                </TouchableHighlight>
            </View>
            <View style={{marginTop:8}}>
                <Text>当前的值:{this.state.name}</Text>
            </View>
         </View>
      )
   }
}

const styles = StyleSheet.create ({
   container: {
      margin:10
   },
   textInput: {
      margin: 5,
      height: 44,
      width:'100%',
      borderWidth: 1,
      borderColor: '#dddddd'
   },
   button: {
      flex:1,
      height:44,
      justifyContent:'center',
      alignItems:'center',
      width:100,
      backgroundColor: 'red'
   },
   buttonTxt:{
      justifyContent:'center',
      color:'#ffffff'
   }
})

动画组件

import React, { Component } from 'react'
import { View, StyleSheet, Animated, TouchableOpacity } from 'react-native'

class App extends Component {
   UNSAFE_componentWillMount = () => {
      //创建动画属性对象
      this.animatedWidth = new Animated.Value(50)
      this.animatedHeight = new Animated.Value(100)
   }
   animatedBox = () => {
      //点击后,设置动画变化
      Animated.timing(this.animatedWidth, {
         toValue: 200,
         duration: 1000
      }).start()
      Animated.timing(this.animatedHeight, {
         toValue: 500,
         duration: 500
      }).start()
   }
   render() {
      const animatedStyle = {
            width: this.animatedWidth, 
            height: this.animatedHeight }
      return (
         <TouchableOpacity 
            style = {styles.container} 
            onPress = {this.animatedBox}>
            <Animated.View style = {[styles.box, animatedStyle]}/>
         </TouchableOpacity>
      )
   }
}
export default App

const styles = StyleSheet.create({
   container: {
      justifyContent: 'center',
      alignItems: 'center'
   },
   box: {
      backgroundColor: 'blue',
      width: 50,
      height: 100
   }
})

开关switch组件

import React, { Component } from 'react'
import { View, Text, Switch, StyleSheet, Alert } from 'react-native'

export default class App extends Component {
   constructor() {
      super();
      this.label = { false: '关', true: '开' }
      this.state = {
         switch1Value: true,
      }
   }

   toggleSwitch = (value) => {
      this.setState({ switch1Value: value })
   }

   render() {
      return (
         <View style={styles.container}>
            <Switch
               //某一项被选中时执行此回调。
               onValueChange={this.toggleSwitch}
               value={this.state.switch1Value}
            />
            <View><Text>Switch 当前的状态是:{this.label[this.state.switch1Value]}</Text></View>
         </View>
      )
   }
}

const styles = StyleSheet.create({
   container: {
      flex: 1,
      alignItems: 'center',
      marginTop: 100
   }
})

选项框组件

import React, { Component } from 'react';
import { View, Text, Picker, StyleSheet } from 'react-native'

class App extends Component {

   users = [
      {label: '请选择性别',value:''},
      {label: '男',value:'male'},
      {label: '女',value:'female'},
      {label: '其它',value:'other'}
   ]
   state = {user: ''}
   updateUser = (user) => {
      this.setState({ user: user })
   }
   render() {
      return (
         <View style={styles.container}>
            <Text style = {styles.label}>请选择性别</Text>
            {/* 设置选择器 */}
            <Picker 
               selectedValue = {this.state.user} 
               onValueChange = {this.updateUser}>
            {  
               // 设置选项
               this.users.map((o,index) =>
                  <Picker.Item label={o.label} value = {o.value}/>
               )
            }
            </Picker>
            <Text style = {styles.label}>你的选择是</Text>
            <Text style = {styles.text}>{this.state.user}</Text>
         </View>
      )
   }
}
export default App

const styles = StyleSheet.create({

   container: {
      margin:50,
   },
   label: {
      fontSize: 14,
      color:'#333333'
   },
   text: {
      fontSize: 30,
      alignSelf: 'center',
      color: 'red'
   }
})

网络请求

import React, { Component } from 'react';
import {
   StyleSheet,
   Text,
   Image,
   View
} from 'react-native';

var REQUEST_URL = "https://raw.githubusercontent.com/facebook/react-native/0.51-stable/docs/MoviesExample.json";

export default class App extends Component {
   constructor(props) {
      super(props);
      this.state = {
         movies: null,
      };
      //绑定事件,用箭头函数可以省略
      this.fetchData = this.fetchData.bind(this);
   }

   //componentDidMount() 会在组件挂载后(插入 DOM 树中)立即调用
   componentDidMount() {
      this.fetchData();
   }

   //发起请求
   fetchData() {
      fetch(REQUEST_URL)
         .then((response) => response.json())
         .then((responseData) => {
            this.setState({
               movies: responseData.movies,
            });
         });
   }

   render() {
      if (!this.state.movies) {
         return this.renderLoadingView();
      }
      var movie = this.state.movies[0];
      return this.renderMovie(movie);
   }

   //loding页面
   renderLoadingView() {
      return (
         <View style={styles.container}>
            <Text>loading...</Text>
         </View>
      );
   }

   //获取数据渲染页面
   renderMovie(movie) {
      return (
         <View style={styles.container}>
            <Image
               style={styles.thumbnail}
               source={{ uri: movie.posters.thumbnail }}
            />
            <View style={styles.rightContainer}>
               <Text style={styles.title}>{movie.title}</Text>
               <Text style={styles.year}>{movie.year}</Text>
            </View>
         </View>
      );
   }

}

const styles = StyleSheet.create({
   container: {
      flex: 1,
      flexDirection: 'row',
      justifyContent: 'center',
      alignItems: 'center',
      backgroundColor: '#F5FCFF',
   },
   thumbnail: {
      width: 100,
      height: 80
   },
   rightContainer: {
      flex: 1
   },
   title: {
      fontSize: 20,
      marginBottom: 8,
      textAlign: 'center'
   },
   year: {
      textAlign: 'center'
   }
});

其他、安卓APP打包方式参考官网

苹果APP打包需要使用mac端xcode

 

调用android原生模块

照着官网的实例折腾了好久,竟然有坑!!!

创建项目

npx react-native init Test

使用android studio打开Test/android/build.gradle文件,然后点击sync project with gradle files

问了大神,大神建议新建的RN项目,先用android studio打开加载依赖,开始我也确信,但是测试发现,不用as打开也可以,所以这个步骤应该可以省略,做个笔记,或许以后有用。具体是什么原理还不清楚,听听也不会错,哈哈!

Toast 模块

本向导会用Toast作为例子。假设我们希望可以从 Javascript 发起一个 Toast 消息(一种会在屏幕下方弹出、保持一段时间的消息通知)。

我们首先来创建一个原生模块。一个原生模块是一个继承了ReactContextBaseJavaModule的 Java 类,它可以实现一些 JavaScript 所需的功能。我们这里的目标是可以在 JavaScript 里写ToastExample.show('Awesome', ToastExample.SHORT);,来调起一个短暂的 Toast 通知。

创建一个新的 Java 类并命名为ToastModule.java,放置到android/app/src/main/java/com/your-app-name/目录下,其具体代码如下:

这里要重载三个方法

// ToastModule.java

package com.test;

import android.widget.Toast;

import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;

import java.util.Map;
import java.util.HashMap;

public class ToastModule extends ReactContextBaseJavaModule {
    private static ReactApplicationContext reactContext;
    //定义常量用于控制提示框时间的长短
    private static final String DURATION_SHORT_KEY = "SHORT";
    private static final String DURATION_LONG_KEY = "LONG";

    public ToastModule(ReactApplicationContext reactContext) {
        super(reactContext);
       this.reactContext = reactContext;
    }
    //ReactContextBaseJavaModule要求派生类实现getName方法。这个函数用于返回一个字符串名字,
    // 这个名字在 JavaScript 端标记这个模块。这里我们把这个模块叫做ToastExample,
    // 这样就可以在 JavaScript 中通过NativeModules.ToastExample访问到这个模块。
    // 译注:RN 已经内置了一个名为 ToastAndroid 的模块,所以在练习时请勿使用 ToastAndroid 的名字,否则运行时会报错名字冲突!
    @Override
    public String getName() {
        return "ToastExample";
    }
    //一个可选的方法getContants返回了需要导出给 JavaScript 使用的常量。
    // 它并不一定需要实现,但在定义一些可以被 JavaScript 同步访问到的预定义的值时非常有用。
    @Override
    public Map<String, Object> getConstants() {
        final Map<String, Object> constants = new HashMap<>();
        constants.put(DURATION_SHORT_KEY, Toast.LENGTH_SHORT);
        constants.put(DURATION_LONG_KEY, Toast.LENGTH_LONG);
        return constants;
    }
    //要导出一个方法给 JavaScript 使用,Java 方法需要使用注解@ReactMethod。
    // 方法的返回类型必须为void。React Native 的跨语言访问是异步进行的,
    // 所以想要给 JavaScript 返回一个值的唯一办法是使用回调函数或者发送事件
    @ReactMethod
    public void show(String message, int duration) {
        Toast.makeText(getReactApplicationContext(), message, duration).show();
    }

}

这个java文件官方实例有一个大坑,就是被坑在这里几天的。

//官方错误写法?暂时没搞懂
public ToastModule(ReactApplicationContext reactContext) {
    super(reactContext);
    reactContext = context;
  }

//正确写法
public ToastModule(ReactApplicationContext reactContext) {
        super(reactContext);
       this.reactContext = reactContext;
    }

注册模块

在 Java 这边要做的最后一件事就是注册这个模块。我们需要在应用的 Package 类的createNativeModules方法中添加这个模块。如果模块没有被注册,它也无法在 JavaScript 中被访问到。

创建一个新的 Java 类并命名为CustomToastPackage.java,放置到android/app/src/main/java/com/your-app-name/目录下,其具体代码如下:

// CustomToastPackage.java

package com.test;

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class CustomToastPackage implements ReactPackage {

    @Override
    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
        return Collections.emptyList();
    }

    @Override
    public List<NativeModule> createNativeModules(
            ReactApplicationContext reactContext) {
        List<NativeModule> modules = new ArrayList<>();

        modules.add(new ToastModule(reactContext));

        return modules;
    }

}

这个 package 需要在MainApplication.java文件的getPackages方法中提供。这个文件位于你的 react-native 应用文件夹的 android 目录中。具体路径是: android/app/src/main/java/com/your-app-name/MainApplication.java. (这个文件是项目创建的时候就有的,只需要加入2句代码即可)

//第一句代码
import com.your-app-name.CustomToastPackage; // <-- 引入你自己的包

protected List<ReactPackage> getPackages() {
  @SuppressWarnings("UnnecessaryLocalVariable")
  List<ReactPackage> packages = new PackageList(this).getPackages();
  // Packages that cannot be autolinked yet can be added manually here, for example:
  // packages.add(new MyReactNativePackage());
//第二句代码
  packages.add(new CustomToastPackage()); // <-- 添加这一行,类名替换成你的Package类的名字 name.
  return packages;
}

创建 ToastExample.js 文件

为了让你的功能从 JavaScript 端访问起来更为方便,通常我们都会把原生模块封装成一个 JavaScript 模块。这不是必须的,但省下了每次都从NativeModules中获取对应模块的步骤。这个 JS 文件也可以用于添加一些其他 JavaScript 端实现的功能。

// ToastExample.js
/**
 * This exposes the native ToastExample module as a JS module. This has a
 * function 'show' which takes the following parameters:
 *
 * 1. String message: A string with the text to toast
 * 2. int duration: The duration of the toast. May be ToastExample.SHORT or
 *    ToastExample.LONG
 */
import { NativeModules } from "react-native";
// 下一句中的ToastExample即对应上文
// public String getName()中返回的字符串
export default NativeModules.ToastExample;

在app调用,只有两句代码

import React, { Component } from 'react';
import {Text,Button} from 'react-native';

//第一句导入
import ToastExample from "./ToastExample";
//第二句调用
ToastExample.show("终于成功了,我C!", ToastExample.SHORT)

export default class App extends Component {
  render() {
    return (
     <Text >666</Text>
    );
  }
}

 

使用问题总结

1、使用aandroid studio模拟器需要先启动模拟器再启动项目,我的电脑无法直接使用 run android   命令运行模拟器

2、真机调试需要安装手脚架,否则运行  react-native run-android  会报!

 npm install -g yarn react-native-cli

3、运行模拟器/真机报如下错误,

error Failed to install the app. Make sure you have the Android development environment set up: https://reactnative.dev/docs/environment-setup. Run CLI with --verbose flag for more details.
Error: Command failed: gradlew.bat app:installDebug -PreactNativeDevServerPort=8081

答案: 有几种情况,比如有些小米手机用的miui,它的开发者菜单里有个选项叫做“miui优化”,据说关掉这个优化后就不会有这个报错;又可能有的手机可能会弹出一个框问你是否确定安装,但是你没看到没点也会导致这种情况

 

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
OSCHINA
登录后可查看更多优质内容
返回顶部
顶部