ReactNative学习之自定义Button

不管是Android还是ios,Button控件都在这两个原生开发中都已经被封装好了,我们可以直接使用。但是在RN中并没有直接提供这种组件给我们,而是给我们提供了一个可点击的组件:Touchable系列(如TouchableOpacity, TouchableHighlight等)。那么今天我们就一起来学习封装属于我们自己的Button。看一下我们的效果图

CustomButton.gif
CustomButton_Android.gif

上面的效果图中有三个Button,若不封装的话,我们就肯定会写很多重复代码,这对于一个面向对象的程序员来说肯定是不能接受的。好了,那我们就一起来实现封装的代码吧。若不熟悉Touchable系列组件的,可以先看看我之前写的文章。

实现步骤

  • step 1 先封装一个可点击的button

      _renderTouchableHighlight(selectedColor, type,style) {
    
          return (
              <TouchableHighlight
                  underlayColor={selectedColor}
                  onPress={this._onPress}
                  style={[styles.container, type,style, this.state.disable && {backgroundColor: this.props.disableColor}]}
                  disabled={this.state.disable}
              >
                  <Text style={this.props.textStyle}>{this.props.text}</Text>
    
              </TouchableHighlight>
          );
      }
      
      
      
      _renderTouchableOpacity(type,style) {
    
          return (
              <TouchableOpacity
                  onPress={this._onPress}
                  style={[styles.container, type, style]}
                  disabled={this.state.disable}
              >
                  <Text style={this.props.textStyle}>{this.props.text}</Text>
    
              </TouchableOpacity>
          );
      }
    

这里用两个方法来渲染不同类型的button,其实它们的不同之处在于:当Button被点击的时候,button需要呈现出什么样的状态来进行视觉交互。RN已经给Opacity类型的button设置了selected状态,但我们若需要自己定义selected按钮状态的话,就需要使用TouchableHighlight类型的。

方法中的selectedColor用来判断使用者是选择何种类型的button,若传来了selectedColor,那么就作为TouchableHighlight的underlayColor。对于整个button长什么样,由使用者去定制,不过我们可肯定的是,button的文字肯定是居中的,所以设置了styles.container,代码如下:

container: {
    justifyContent: 'center',
    alignItems: 'center',
    overflow: 'hidden'  //这个属性定义溢出元素内容区的内容会如何处理,内容会被修剪,并且其余内容是不可见的。
},

方法中第二个参数type是用来决定用户需要什么样的button,是实心,空心或者仅是text。

static _setDifferentButtonStyle(buttonColor, buttonRadius, buttonType, borderWidth) {

    if (buttonType == "normal") {

        return CustomButton._setDifferentStyle(buttonColor, buttonRadius, buttonColor);

    } else if (buttonType == 'stroke') {

        return CustomButton._setDifferentStyle('transparent', buttonRadius, buttonColor, borderWidth);

    } else if (buttonType == 'text') {

        return CustomButton._setDifferentStyle('transparent', 0, 'transparent');
    }
}

static _setDifferentStyle(backgroundColor, borderRadius, borderColor, borderWidth) {

    return {
        backgroundColor: backgroundColor,
        borderRadius: borderRadius,
        borderColor: borderColor,
        borderWidth: borderWidth
    };
}

上面代码中,可以根据使用者传过来的buttonType类型来返回对应的button样式;至于第三个参数style,是使用者设置button时传过来的具体的样式,我们可以直接拿来用。

  • step 2 传递点击事件

Touchable系列中有个onPress方法,这是用来处理点击事件的。我们不可能把外面传过来的事件在这个类中去处理,而是需要使用者自己处理。那如何做了?也就是事件的传递了,另外一种说法就是回调。在最上面代码中,有看到onPress={this._onPress},那么这个this._onPress到底是谁了?

_onPress() {
    if (this.props.onPress) {
        
        this.props.onPress();
    }
}

从上面的代码中可以看出,最终是调到传过来的onPress方法。不过我们若直接这样调的话,肯定会报错。为什么?因为这个方法是我们自己定义的,但没有像组件的生命周期方法一样,在一个组件被创建时就已经被初始化了。所以,我们需要将我们自己定义的方法与初始时进行绑定。初始化操作我们一般放在构造方法中进行。

constructor(props) {
    super(props);
    this._onPress = this._onPress.bind(this);
}
  • step 3 防重复点击

网络请求数据一般是耗时操作,为了防止用户多次点击button去请求数据,我们还需要设置在做耗时操作时,不能让button变得可点击,并且给出视觉交互。那如何来实现了?我们可以通过状态的改变来决定是否可以点击。有两种实现方式:

1、对外提供两个方法,让使用者通过拿到我们自定义button的实例来调用这个暴露出去的方法,从而达到点击与不可点击的切换

2、我们可以将某个改变点击状态的方法传给使用者进行回调,让使用者决定什么时候可改变button的点击状态。

第一种相当来说比较简单,我们来使用第二种方式。

constructor(props) {
    super(props);
    this._onPress = this._onPress.bind(this);
    this._enable = this._enable.bind(this);
    this._disable = this._disable.bind(this);

    this.state = {
        disable: false
    }
}

_onPress() {
    if (this.props.onPress) {
        this._disable();
        this.props.onPress(this._enable);
    }
}

_enable() {
    this.setState({
        disable: false
    });
};

_disable() {
    this.setState({
        disable: true
    });
};

我们通过一个状态值来保存button的可点击状态,在button被点击时,马上将这个button置为不可点击,至于什么时候可以点击,我们将enable方法回调给了使用者,由使用者决定。如上面所说,我们自定义的方法都必须先在构造方法中进行初始化。

  • step 4 设置属性类型和默认值

我们自定义的属性需要什么类型,使用者并不知道,所以我们需要声明我们自定义属性的类型,可以通过PropTypes,并且还可以强制用户必须传哪些属性。

//属性类型
CustomButton.propTypes = {

    text: PropTypes.string.isRequired,
    textStyle: Text.propTypes.style,
    buttonType: PropTypes.oneOf(['normal', 'stroke', 'text']).isRequired,
    selectedColor: PropTypes.string,
    onPress: PropTypes.func,
    buttonColor:PropTypes.string,
    buttonRadius:PropTypes.number,
    borderWidth:PropTypes.number,
};

//属性默认值
CustomButton.defaultProps = {

    borderWidth: 1
};

最后是整个类的渲染

render() {
    
    //这里是将props中属性进行解构,es6语法,可查看阮一峰的《ES6标准与入门》
    let {selectedColor, buttonColor, buttonRadius, buttonType, borderWidth, style}=this.props;
    let type = CustomButton._setDifferentButtonStyle(buttonColor, buttonRadius, buttonType, borderWidth);

    if (selectedColor) {
        {
            return this._renderTouchableHighlight(selectedColor, type, style);
        }
    } else {
        {
            return this._renderTouchableOpacity(type, style);
        }
    }
}
  • step 5 进行测试

      <View style={styles.container}>
              <CustomButton
                  text="确定"
                  buttonColor="red"
                  buttonRadius={20}
                  buttonType="normal"
                  textStyle={styles.textStyle}
                  style={styles.customButton}
                  selectedColor="green"
                  disableColor="yellow"
                  onPress={(callback)=> {
                      setTimeout(()=> {
    
                          callback();
    
                      }, 3000);
                  }}
              />
              <CustomButton
                  text="确定"
                  buttonColor="red"
                  buttonRadius={20}
                  buttonType="stroke"
                  textStyle={styles.textStyle}
                  style={styles.customButton}
                  selectedColor="green"
                  disableColor="yellow"
                  onPress={(callback)=> {
                      setTimeout(()=> {
    
                          callback();
    
                      }, 3000);
                  }}
              />
              <CustomButton
                  text="确定"
                  buttonColor="red"
                  buttonRadius={20}
                  buttonType="text"
                  textStyle={styles.textStyle}
                  selectedColor="green"
                  disableColor="yellow"
                  style={{marginTop:20}}
                  onPress={(callback)=> {
                      setTimeout(()=> {
    
                          callback();
    
                      }, 3000);
                  }}
              />
          </View>
    

好了,自定义button就封装完了以及学习了自定义一个组件需要做哪些事。这里面稍微有一点难度的就是方法的传递进行回调。在java中是不允许方法作为参数传递的。不过,在java中不能干的事,在js中可以干是非常常见的。我们今天做的button主要是文字,其实还可以对其进行拓展,那就是这个button为image时,那个比较简单,有兴趣的朋友可以进一步进行封装。

本人目前对于RN也还是处于学习的阶段,若在写文章时出现了错误或者代码可以优化时,请各位朋友不吝告知啊!

完整代码

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

推荐阅读更多精彩内容

  • ¥开启¥ 【iAPP实现进入界面执行逐一显】 〖2017-08-25 15:22:14〗 《//首先开一个线程,因...
    小菜c阅读 6,351评论 0 17
  • View 个人感觉View就类似于html中的div标签,支持flexbox布局。 一个简单的练习,类似携程的格子...
    45b645c5912e阅读 1,172评论 0 0
  • 一. 简介 React Native没有像Web开发那样给元素绑定click事件,Text组件有onPress事件...
    飞奔的小马阅读 1,383评论 0 0
  • 前言 本文有配套视频,可以酌情观看。 文中内容因各人理解不同,可能会有所偏差,欢迎朋友们联系我。 文中所有内容仅供...
    珍此良辰阅读 1,494评论 13 7
  • (为了你,这座古镇已等待了千年) 踏进凤凰的那一刻,感觉是要冷死我了,只能说呆在湛江久了对气温会产生一种钝觉。当然...
    森陌ht阅读 139评论 0 0