React Native - Image组件

Image组件

Image组件用于显示多种不同类型图片的React Native组件,包括网络图片、静态资源、临时的本地图片、以及本地磁盘上的图片(如相册)等。

1、加载网络图片

render() {
         return (
             <View style={styles.container}>
                 <Image style={styles.imageStyle} 
                        source={{uri: imageAddress}}/>
             </View>
         );
     }

imageAddress图片地址,你需要手动指定图片的尺寸,如果没有声明,则图片将不会被呈现在界面上。。同时我们强烈建议你使用 https 以满足 iOS App Transport Security 的要求。指定source属性来显示图片,imageStyle图片显示样式

// 正确
<Image source={{uri: 'https://facebook.github.io/react/logo-og.png'}}
       style={{width: 400, height: 400}} />

// 错误
<Image source={{uri: 'https://facebook.github.io/react/logo-og.png'}} />

简单使用

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

 export default class ImageComponent extends Component {
     render() {
         return (
             <View style={styles.container}>
                 <Image style={styles.imageStyle} 
                        source={{uri:'https://facebook.github.io/react/logo-og.png'}}/>
             </View>
         );
     }
 }

// 指定显示样式
 var styles = StyleSheet.create({
     container: {
         flex: 1,
         backgroundColor: 'white',
     },
     imageStyle: {
         top: 50,
         left: 50,
         width: 200,
         height: 200,
     }
 });

实现效果

屏幕快照 2019-11-21 上午9.28.04.png

加载data类型数据图片

有时候你可能拿到的是图片的base64数据,此时可以使用'data:'格式来显示图片。请注意,你需要手动指定图片的尺寸。

建议仅对非常小的图片使用 base64 数据,比如一些小图标。

// 请记得指定宽高!
<Image
  style={{
    width: 51,
    height: 51,
    resizeMode: 'contain',
  }}
  source={{
    uri:
      'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADMAAAAzCAYAAAA6oTAqAAAAEXRFWHRTb2Z0d2FyZQBwbmdjcnVzaEB1SfMAAABQSURBVGje7dSxCQBACARB+2/ab8BEeQNhFi6WSYzYLYudDQYGBgYGBgYGBgYGBgYGBgZmcvDqYGBgmhivGQYGBgYGBgYGBgYGBgYGBgbmQw+P/eMrC5UTVAAAAABJRU5ErkJggg==',
  }}
/>

2、静态图片资源

React Native提供了一个统一的方式来管理iOS 和 Android应用中的图片。要往 App 中添加一个静态图片,只需把图片文件放在代码文件夹中某处,然后像下面这样去引用它,require等同于使用var声明了一个变量:

<Image source={require('./my-icon.png')} />

图片文件的查找会和 JS 模块的查找方式一样。在上面的这个例子里,是哪个组件引用了这个图片,Packager 就会去这个组件所在的文件夹下查找my-icon.png。并且,如果你有my-icon.ios.pngmy-icon.android.pngPackager 就会根据平台而选择不同的文件。

你还可以使用@2x,@3x这样的文件名后缀,来为不同的屏幕精度提供图片。比如下面这样的代码结构:

.
├── button.js
└── img
    ├── check.png
    ├── check@2x.png
    └── check@3x.png

并且button.js里有这样的代码:

<Image source={require('./img/check.png')} />

Packager 会打包所有的图片并且依据屏幕精度提供对应的资源。譬如说,iPhone 7 会使用check@2x.png,而 iPhone 7 plus 或是 Nexus 5 上则会使用check@3x.png。如果没有图片恰好满足屏幕分辨率,则会自动选中最接近的一个图片。

注意:如果你添加图片的时候 packager 正在运行,可能需要重启 packager 以便能正确引入新添加的图片。

这样会带来如下的一些好处:

  • iOS 和 Android 一致的文件系统。
  • 图片和 JS 代码处在相同的文件夹,这样组件就可以包含自己所用的图片而不用单独去设置。
  • 不需要全局命名。你不用再担心图片名字的冲突问题了。
  • 只有实际被用到(即被 require)的图片才会被打包到你的 app。
  • 现在在开发期间,增加和修改图片不需要重新编译了,只要和修改 js 代码一样刷新你的模拟器就可以了。
  • 与访问网络图片相比,Packager 可以得知图片大小了,不需要在代码里再声明一遍尺寸。
  • 现在通过 npm 来分发组件或库可以包含图片了。

注意:为了使新的图片资源机制正常工作,require 中的图片名字必须是一个静态字符串(不能使用变量!因为 require 是在编译时期执行,而非运行时期执行!)。

// 正确
<Image source={require('./my-icon.png')} />;

// 错误
const icon = this.props.active ? 'my-icon-active' : 'my-icon-inactive';
<Image source={require('./' + icon + '.png')} />;

// 正确
const icon = this.props.active
  ? require('./my-icon-active.png')
  : require('./my-icon-inactive.png');
<Image source={icon} />;

请注意:通过这种方式引用的图片资源包含图片的尺寸(宽度,高度)信息,如果你需要动态缩放图片(例如,通过 flex),你可能必须手动在 style 属性设置{ width: null, height: null }

3、网络图片的请求参数

可以在Image组件的source属性中指定一些请求参数,如下面的示例:

<Image
  source={{
    uri: 'https://facebook.github.io/react/logo-og.png',
    method: 'POST', 
    headers: { 
      Pragma: 'no-cache',
    },
    body: 'Your Body goes here',
  }}
  style={{width: 400, height: 400}}
/>

4、使用getSize获取图片的宽和高

getSize()函数

Image组件提供了一个静态函数getSize,用来取得指定URL地址图片的宽和高(单位为像素)。

注意:在调用 getSize 函数取图片的宽、高时,React Native 框架事实上会下载这张图片,并且将该图片保存到缓存中。所以 getSize 函数也可以作为预加载图片资源的一个方法。

原生方式

let imageSource = "https://facebook.github.io/react/logo-og.png";

Image.getSize(imageSource, (width, height) => {
       console.log(`The image dimensions are ${width}x${height}`);
   }, (error) => {
      console.error(`Couldn't get the image size: ${error.message}`);
   });

promise方式

Image.getSize(imageSource).then((width, height) => {
  //取得图片的宽高,并进行相应的处理
  //......
}).catch((error) => {
  //下载图片失败
  console.log(error);
});

5、使用prefetch预获取图片

可以使用Image组件的静态函数 prefetch 来预下载某张网络图片。

let imageSource = "https://facebook.github.io/react/logo-og.png";
        
Image.prefetch(imageSource, (result) => {
      console.log(`The image prefetch are ${result}`);
 }, (error) => {
     console.error(`Couldn't get the image size: ${error.message}`);
 });

6、目前支持的属性

  • resizeMode:表示内部图片的显示模式。是一个枚举类型,具体用法在下面会有介绍。
  • source:图片的引用地址,其值为 {uri:string}。如果是一个本地的静态资源,那么需要使用 require('string') 包裹。
  • defaultSource:表示图片未加载完成时,使用的默认图片地址。(仅iOS支持)
    -onLoadStart:加载开始时触发该事件(仅iOS支持)
  • onProgress:加载过程的进度事件。(仅iOS支持)
  • onLoad:加载成功时触发该事件。(仅iOS支持)
  • onLoadEnd:不管是加载成功还是失败,都会触发该事件。(仅iOS支持)

支持的图片格式

  • React Native 默认支持 JPG、PNG 格式。
  • 在 iOS 平台下,还支持 GIF、WebP 格式。
  • 在 Android 平台下,默认不支持 GIF、WebP 格式。可以通过修改 Android 工程设置让其支持这两种格式:

打开项目目录下的 android/app/build.gradle,视情况添加相关代码:

dependencies {
  compile 'com.facebook.fresco:animated-gif:0.11.0'  //需要GIF动画支持添加本行语句
  compile 'com.facebook.fresco:webpsupport:0.11.0'  //需要WebP格式支持添加本行语句
  compile 'com.facebook.fresco:animated-webp:0.11.0'  //需要WebP动画支持添加本行语句
}

设置图像显示模式

  • resizeMode

当 Image 组件的实际宽、高与图片的实际宽、高不符时,要如何显示图片由样式定义中resizeMode取值来决定。resizeMode 的五个取值分别是:contain、cover、stretch、center 和 repeat。如果没有定义resizeMode,默认值为:cover。其中 repeat 只对 iOS 平台有效。

无论resizeMode 取值如何,图片都会在 Image 组件的显示区域居中显示。即显示的图片中点与显示区域的中点是一个点。

resizeMode 比较特殊,可以按需要,自由放置在如下两个地方:既可以作为 Image 组件样式中的一个键值对发挥作用。也可以作为Image组建的属性来发挥作用。

  • 各种模式

cover 模式(默认值)

该模式要求图片能够填充整个 Image 组件定义的显示区域,可以对图片进行放大或者缩小,可以丢弃放大或缩小后的图片中的部分区域,只求在显示比例不失真的情况下填充整个显示区域。

如果图片的宽、高有一个值小于Image 的实际宽、高,React Native 会对图片进行放大,直到图片的宽与高均不小于Image的实际宽、高。然后将放大的图片居中显示,超出显示区域的部分被直接丢弃。

如果图片的宽、高均大于 Image 的实际宽、高, React Native会对图片进行等比缩小,直到缩小后图片的宽、高有一个值等于Image的实际宽、高。然后将缩小后的图片居中显示,超出区域的部分被直接丢弃。

contain 模式

该模式要求显示整张图片,可以对它进行等比放大或者缩小,但不能丢弃改变后图片的某部分。这个模式下图片得到完整的呈现,比例不会变。但图片可能无法填充 Image 的所有区域,会在侧边或者上下留下空白,由Image组件的底色填充。

如果图片的实际宽、高都小于Image 的实际宽、高, React Native会对图片进行等比放大,直到宽、高中有一个值等于Image的实际宽、高。然后居中显示图片。

如果图片的实际宽、高有一个值或者都大于 Image的实际宽、高, React Native会对图片进行等比缩小,直到缩小后图片的宽、高有一个不小于 Image 的实际宽、高,然后在Image中展现图片。

stretch 模式

该模式要求图片填充整个Image定义的显示区域,因此会对图片进行任意的缩放,不考虑保持图片原来的宽、高比。这种模式显示出来的图片有可能会出现明显的失真。

center 模式

该模式要求图片图片位于显示区域的中心。这种模式下图片可能也无法填充 Image 的所有区域,会在侧边或者上下留下空白,由 Image 组件的底色填充。

如果图片的实际宽、高都小于 Image 的实际宽、高, React Native 不会对图片进行任何缩放,只是把它居中呈现在父 View 中。

如果图片的实际宽、高有一个值或者都大于Image 的实际宽、高, React Native会对图片进行等比缩小,直到缩小后图片的宽、高有一个不小于 Image 的实际宽、高,然后在Image中展现图片。

repeat 模式(iOS 独有)

该模式的图片处理思路是用一张或者多张图片来填充整个 Image 定义的显示区域。

7、使用混合 App 的图片资源

如果你在编写一个混合App(一部分 UI 使用 React Native,而另一部分使用平台原生代码),也可以使用已经打包到 App 中的图片资源(以拖拽的方式放置在Xcodeasset类目中,或是放置在 Androiddrawable 目录里)。注意此时只使用文件名,不带路径也不带后缀:

<Image source={{uri: 'app_icon'}} style={{width: 40, height: 40}} />

对于放置在 Android 的 assets 目录中的图片,还可以使用asset:/ 前缀来引用:

<Image source={{uri: 'asset:/app_icon.png'}} style={{width: 40, height: 40}} />

注意:这些做法并没有任何安全检查。你需要自己确保图片在应用中确实存在,而且还需要指定尺寸。

8、缓存控制(仅 iOS)

React Native支持对网络图片的缓存,访问过一次的图片,在一定时间内会缓存到手机中,当需要再次显示的时候,React Native将会从手机存储中加载这个缓存,而不是从网络获取

Andriod平台,图片是会缓存到本地的,对于iOS需要设置cache属性实现缓存效果

  • default: 使用原生平台默认策略。
  • reload: URL 的数据将从原始地址加载。不使用现有的缓存数据。
  • force-cache: 现有的缓存数据将用于满足请求,忽略其期限或到期日。如果缓存中没有对应请求的数据,则从原始地址加载。
  • only-if-cached: 现有的缓存数据将用于满足请求,忽略其期限或到期日。如果缓存中没有对应请求的数据,则不尝试从原始地址加载,并且认为请求是失败的。
<Image
  source={{
    uri: 'https://facebook.github.io/react/logo-og.png',
    cache: 'only-if-cached',
  }}
  style={{width: 400, height: 400}}
/>

9、背景图片与嵌套写法

开发者们常面对的一种需求就是类似web中的背景图(background-image文档。要实现这一用例,只需使用<ImageBackground>组件(其 props 与<Image>完全相同),然后把需要背景图的子组件嵌入其中即可。

// 注意你必须指定宽高样式。
return (
  <ImageBackground source={...} style={{width: '100%', height: '100%'}}>
    <Text>Inside</Text>
  </ImageBackground>
);

简单例子

1、加载图片

import React, { Component } from 'react';
import { View, Image, Text, StyleSheet, Platform, ActivityIndicator } from 'react-native';
 
export default class ImageExample extends Component {
    constructor() {
        super();
 
        // 宽、高、是否正在加载、URL等属性
        this.state = { width: 0, height: 0, loading: true }
        this.uri = 'https://tutorialscapital.com/wp-content/uploads/2017/09/background.jpg';
    }
 
    // 组件出现之后获取图片大小
    componentDidMount() {
        Image.getSize( this.uri, ( width, height ) => {
            this.setState({ width: width, height: height, loading: false });
        }, ( error ) => {
            this.setState({ loading: false });
            console.log( error );
        });
    }
 
    render(){
        return(
            <View style = { styles.container }>
                <View style = { styles.imageContainer }>
                    {
                        ( this.state.loading )
                        ?
                            <ActivityIndicator size = "large" />
                        :
                            ( <Image source = {{ uri: this.uri }} style = { styles.image } /> )
                    }
                    <View style = { styles.showDimensionsView }>
                        <Text style = { styles.text }>{ this.state.width }(w) X { this.state.height }(h)</Text>
                    </View>                    
                </View>
            </View>
        );
    }    
}
 
// 定义显示样式
const styles = StyleSheet.create({
    container: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
        paddingTop: ( Platform.OS === 'ios' ? 20 : 0 )
    },
 
    imageContainer: {
        position: 'relative',
        width: 200, 
        height: 250,
        justifyContent: 'center',
        alignItems: 'center'
    },
 
    image: {
        resizeMode: 'cover',
        width: '100%',
        height: '100%'
    },
 
    showDimensionsView: {
        position: 'absolute',
        left: 0,
        right: 0,
        bottom: 0,
        backgroundColor: 'rgba(0,0,0,0.6)',
        alignItems: 'center',
        paddingTop: 15,
        paddingBottom: 15
    },

    text: {
        fontSize: 20,
        color: 'black',
        color: 'rgba(255,255,255,0.8)'
    }
});

刚开始会显示图片加载指示器,当图片获取大小成功之后会设置图片属性,重新渲染显示图片

实现效果

屏幕快照 2019-11-27 下午2.58.04.png

remote-image-tutorial

2、简单的图片浏览器

这里实现一个简单的图片浏览器,通过点击下方的“上一张”和“下一张”按钮可以进行图片的切换

import React, {Component} from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  Image,
  TouchableOpacity
} from 'react-native';
 
// 网络图片数组
var imgs = [
  'https://img3.doubanio.com/view/movie_poster_cover/mpst/public/p2263582212.jpg',
  'https://img3.doubanio.com/view/movie_poster_cover/mpst/public/p2265761240.jpg',
  'https://img3.doubanio.com/view/movie_poster_cover/mpst/public/p2266110047.jpg'
];
 
// 定义图片浏览组件
class MyImage extends Component {
    //构造函数
    constructor(props) {
        super(props);
        this.state = {
            imgs: props.imgs,
            count: 0
        };
    }
 
    //下一张按钮点击事件
    goNext() {
        var count = this.state.count;
        count++;
        if (count < imgs.length) {
            this.setState({count: count});
      }
    }
 
    //上一张按钮点击事件
    goPreview() {
      var count = this.state.count;
      count --;
      if(count >= 0){
        this.setState({count: count});
      }
    }
 
    render() {
      return (
        <View style={[styles.flex]}>
          <View style={styles.image}>
            <Image style={styles.img}
              source={{uri: this.state.imgs[this.state.count]}}
              resizeMode="contain" />
          </View>
          <View style={styles.btns}>
            <TouchableOpacity onPress={this.goPreview.bind(this)}>
              <View style={styles.btn}>
                 <Text>上一张</Text>
              </View>
            </TouchableOpacity>
            <TouchableOpacity onPress={this.goNext.bind(this)}>
              <View style={styles.btn}>
                 <Text>下一张</Text>
              </View>
            </TouchableOpacity>
          </View>
        </View>
      );
    }
}
 
//默认应用的容器组件
export default class MyNextImage extends Component {
    // 容器组件,显示MyImage组件
    render() {
        return (
          <View style={[styles.flex, {marginTop:40}]}>
            <MyImage imgs={imgs}></MyImage>  
          </View>
        );
    }
}
 
// 样式定义
const styles = StyleSheet.create({
  flex:{
    flex: 1,
    alignItems:'center'
  },
  image:{
    borderWidth:1,
    width:300,
    height:200,
    borderRadius:5,
    borderColor:'#ccc'
  },
  img:{
    height:198,
    width:300,
  },
  btns:{
    flexDirection: 'row',
    justifyContent: 'center',
    marginTop:20
  },
  btn:{
    width:60,
    height:30,
    borderColor: '#0089FF',
    borderWidth: 1,
    justifyContent: 'center',
    alignItems:'center',
    borderRadius:3,
    marginRight:20,
  },
});

实现效果

屏幕快照 2019-11-21 上午10.32.17.png

参考

静态图片资源
Image介绍
hangge

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容