不管是Android還是ios,Button控件都在這兩個原生開發中都已經被封裝好了,我們可以直接使用。但是在RN中并沒有直接提供這種組件給我們,而是給我們提供了一個可點擊的組件:Touchable系列(如TouchableOpacity, TouchableHighlight等)。那么今天我們就一起來學習封裝屬于我們自己的Button。看一下我們的效果圖
上面的效果圖中有三個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也還是處于學習的階段,若在寫文章時出現了錯誤或者代碼可以優化時,請各位朋友不吝告知啊!