Reac Native-UI组件的封装

概述

在上一次的学习中,认识React Native的一些基本内容,如:JS文件的构成要素,基础UI组件,基础通用API等待,并制定了大致的学习方向,包括UI组件的封装,生命周期的认识等等。这次学习就以完善上次的movielist程序为主,对基础组件进行封装。

movielist程序中,采用了listview、image、text等UI组件,通过联网获取数据展示在界面上。通常在Android的原生程序中,我们往往要考虑到数据为空怎么办,图片加载不到怎么办……基于此,我们首先对listview的loading状态进行封装。

LoadingListView的封装

在上次的movielist中有提到可以通过setState强制调起render生命周期重新绘制界面,因此我们的loading模式就可以采用相同原理来封装我们movielist程序:

constructor() {
    super();
    this.state = {
      loaded: false,
      dataSource: new ListView.DataSource({rowHasChanged: (row1, row2) => row1 !== row2 })
    };
}

render() {
    if (this.state.loaded) {
      return this.renderList();
    }else {
      return this.renderLoading();
    }
}

我们通过this.state.loaded作为标志来判断加载对应的ui组件,当state的loaded属性发生变化时,就会加载到指定的组件,补全代码:

componentDidMount() {
    fetch(request_url)
      .then((response) => response.json())
      .then((responseData) => {
        this.setState({
          dataSource: this.state.dataSource.cloneWithRows(responseData.movies),
          loaded: true
        });
      })
      .done()
}

renderLoading() {
    return (
        <View style={styles.container} >
            <Text style={styles.loading}>Loading...</Text>
        </View>
    );
}

ErrorImage的封装

完成了ListView的loading状态之后,继续Image的相应处理。Imageyou三种状态:place,success,error。在官网中,对Image提供了onLoad,onLoadStart,onLoadEnd的加载状态三个属性。对于ios开发者来说,多了defaultSource,onError,onProgress这几个重要方法。然而android平台的不支持,使得我们无法直接调用这几个属性来配置相应的状态。那么我们就针对onError的情况做处理并进行UI的封装;

首先理清思路,要对onError的状态处理,第一步,要获取加载失败的状态;第二步,要在error的状态下加载error的图片。因此:

step 1: error状态的获取

Android的Image中,没有提供onError,所以我们必须通过现有的属性获取到onError的状态。经过官方文档的属性说明,我们发现了一个可以帮助我们判断是否加载成功的方法:onLoad:

Invoked when load completes successfully.

这个官网对onLoad的说明,即当加载成功时候会调用这个方法。但是我们获取的是失败的状态,仅通过onLoad方法有没有调用的这个想法是无法实现的。官网同时提供了另外一个属性:onLoadEnd

Invoked when load either succeeds or fails.

也就是说在加载结束的时候会被调用。也就是说如果图片加载成功,会依次经过onLoadonLoadEnd,颇有些类似于Activity的生命周期一样,于是我们联想到标志位的使用。
即初始化一个标记isError,如果这个标记没有在onLoad被状态的时候修改,那我们则在onLoadEnd的时候就会得到一个结果:图片未加载成功。此时我们的Android版的onError状态也就出来了。

step 2: error图片的加载

很遗憾的是,React Native并没有提供类似于Picasso.load().error()的方式,所以我们发现我们没有办法拿到当前加载的Image去重新设置他的source并重新加载。因此只能另辟蹊径,采用强制刷新ImageView的方式,强制加载errorSource。这种思路跟上述的movielist有些类似,根据不同的状态,返回不同的view。而不同的状态是通过setState的方式强制刷新进行新的判断。于是我们的大致思路变成了

1. 设置全局标记,检测Image的error状态;
2. 当获取到error状态的时候,通过setState方法强制刷新
3. 初始化或刷新时根据不同的状态加载不同的组件或者配置不同source

step 3: 自定义ErrorImage的编写

在上次的学习中有提到Reading这个demo,在/app/component这个目录级别下我们看到了有一个组件叫做ImageButton,因此我们可以参考该组件进行封装:

首先我们需要初始化这个组件,包括创建组件并引入相应的API,设置组件可被引用:

improt React ,{
    Componet,
    StyleSheet,
    View,
    Image
} from 'react-native';

var request_url = {uri : "http://d3biamo577v4eu.cloudfront.net/static/images/redesign/poster_default_thumb"};

class ErrorImage extends React.Component{

    render() {
        return (
            <Image source={{uri:request_url}}/>
        );
    }

}

export default ErrorImage;

接下来根据step 1中所描述的,建立检测机制:

var isLoaded = false;

class ErrorImage extends React.Component{

    render() {
        return (
            <Image 
                source={{uri:request_url}}
                onLoad={this.LoadingSuccess}
                onLoadEnd={this.loadingFinish}/>
        );
    }

    loadingSuccess() {
        isLoaded = true;
    }

    loadingFinish() {
        if(!isLoaded){
            // 加载失败需要刷新view 
        }
    }
}

紧接着,我们需要重新刷新view来展示error的状态,根据step 2,建立刷新机制:

class ErrorImage extends React.Component{

    construct(props){
        super(props);
        this.state = {finishError : false};
    }

    loadingFinish() {
        if(!isLoaded){
            this.setState({finishError : true});
        }
    }
}

刷新机制建立好后必然要跟的是我们的error状态处理:

var errorSoruce = require('error.png');

class ErrorImage extends React.Component{
    render() {
        return (
            <Image 
                source={this.state.finishError ? errirSource : request_url}
                onLoad={this.LoadingSuccess}
                onLoadEnd={this.loadingFinish}/>
        );
    }
}

在代码

source={this.state.finishError ? errorSource : request_url}

中,我们可以看到有一个三元表达式,相信熟悉Java的同学都知道是什么意思。在建立刷新机制的时候,我们通过设置state.finishError状态来强制刷新,因此我们也可以通过state.finishError的状态来改变Image的source,从来达到刷新View的效果。

最后,我们在代码中涉及到的request_url,errorSource都是属于我们自己设定好的内容,交给外部引用的时候自然需要不同的source,因此我们需要像自定义属性那样设定我们ErrorImage的一些配置,通过属性PropTypes。虽然在官网中也只有几句简简单单的解释,但是有了ImageButton的demo在,也并不妨碍我们的理解。初始化我们的自定义属性:

import {PropTypes} from 'react'

const PropTypes= {

}

根据所需,我们需要建立至少包括source,errorSource,onLoad,onLoadEnd,style等可能被外部或内部引用改变的东西,并指明他们的类型,同时配置相关的属性:

import {PropTypes} from 'react'

const PropTypes= {
     disabled: PropTypes.bool,
     source: PropTypes.oneOfType([
        PropTypes.shape({
            uri: PropTypes.string,
        }),
        PropTypes.number,
    ]),
    errorSource: PropTypes.number,
    onLoad: PropTypes.func,
    onLoadEnd: PropTypes.func,
    style: View.propTypes.style
}

LoadingImage.propTypes = propTypes;

LoadingImage.defaultProps = {
    disabled: false
};

最后通过我们设定好的配置参数来配置我们的Image:

<Image 
    style={this.props.style}
    source={this.state.finishError ? this.props.errorSource : this.props.source}
    onLoad={this.LoadingSuccess}
    onLoadEnd={this.loadingFinish}/>
/> 

到此,我们自定义的ErrorImage就全部完工了,我们可以通过movielist程序来进行测试:

<ErrorImage style={styles.imageShow}
            errorSource = {require('./img/icon_error.png')}
            source = {{uri : movie.posters.thumbnail}} />

step 4: 总结

通过以上三个步骤,了解了自定义UI组件的封装过程,以及相关的属性、设置,为高级组件封装做了基本的了解。然而在此次的UI组件封装过程中,也是出现了不少的遗漏,比如如何将LoadListView当作单独的组件剥离出来,如何实现IOS的Image的defaultSource : A static image to display while loading the image source 的效果(即占位图的效果)等等。其实Android的原生组件的封装思路和React Native的组件封装思路就我个人而言,有着大致相同的思想,所以我们思考的时候,可以通过例如:生命周期、状态设置、方法回调等等的方式来达到目的。在此推荐一个React Native Component封装的网站,里面提供了比较多的自定义组件:
Native Components
在这个网站上有一个叫做React-Native-Image-Progress的组件,获取能达成类似LoadingImage的效果:
React-Native-Image-Progress

发布了23 篇原创文章 · 获赞 10 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/byxyrq/article/details/51854065
今日推荐