使用RN開發了一段時間,最近遇到了一個比較棘手的問題,就是用react寫個城市選擇列表,固然這個若是用Android原生來寫,網上的例子數不勝數,隨便就能找到,可是react卻不多,也沒有一個和我這個需求相匹配的,因此就只能本身動手擼一個出來咯.react
這個城市列表和其餘的有點區別android
1,有當前定位城市 2,有熱門城市 3,每一個子項是一個相似GridView的效果,而不是ListView 實現的效果圖以下:ios
{
"key": "A",
"data": [{
"citys": [
{
"city": "阿拉善盟",
"id": 152900
},
{
"city": "鞍山",
"id": 210300
},
{
"city": "安慶",
"id": 340800
},
{
"city": "安陽",
"id": 410500
},
.....]
}
複製代碼
stickySectionHeadersEnabled=true
,這樣在滾動的時候就有實現了粘性效果;代碼以下:git
<SectionList
ref="sectionList"
renderSectionHeader={this.renderSectionHeader}
renderItem={this.renderItem}
stickySectionHeadersEnabled={true}
showsHorizontalScrollIndicator={false}
sections={this.state.sections}
keyExtractor={this._extraUniqueKey}
/>
複製代碼
/*右側索引*/
sectionItemView() {
const sectionItem = this.state.sections.map((item, index) => {
if (index === 0) {
return null
}
return <Text key={index}
style={
[cityStyle.sectionItemStyle,
{backgroundColor: this.state.isTouchDown ? touchDownBGColor : touchUpBGColor}]
}>
{item.key}
</Text>
});
return (
<ScrollView style={cityStyle.sectionItemViewStyle}
ref="sectionItemView"
onStartShouldSetResponder={() => true}
onMoveShouldSetResponder={() => true}
onResponderTerminationRequest={() => true}
onResponderGrant={this.responderGrant}
onResponderMove={this.responderMove}
onResponderRelease={this.responderRelease}
>
{sectionItem}
</ScrollView>
);
}
複製代碼
這裏的幾個方法須要具體說明一下(React Native手勢響應,就和android的onTouchEvent一個意思): 經過實施正確的處理方法,視圖能夠成爲接觸響應器。有兩種方法來詢問視圖是否想成爲響應器:github
事件處理:json
/*用戶手指開始觸摸*/
responderGrant(event) {
this.scrollSectionList(event);
this.setState({
isTouchDown: true,
})
}
/*用戶手指在屏幕上移動手指,沒有停下也沒有離開*/
responderMove(event) {
console.log("responderMove")
this.scrollSectionList(event);
this.setState({
isTouchDown: true,
})
}
/*用戶手指離開屏幕*/
responderRelease(event) {
console.log("onTouchUp")
this.setState({
isTouchDown: false,
})
}
/*手指滑動,觸發事件*/
scrollSectionList(event) {
const touch = event.nativeEvent.touches[0];
// 手指滑動範圍 從 A-Q 範圍從50 到 50 + sectionItemHeight * cities.length
if (touch.pageY >= sectionTopBottomHeight+headerHeight+statusHeight
&& touch.pageY <= statusHeight +headerHeight+sectionTopBottomHeight + sectionItemHeight * 25
&& touch.pageX >= width - sectionWidth
&& touch.pageX <= width
) {
console.log("touchx" + touch.pageX + '.=======touchY' + touch.pageY)
const index = (touch.pageY - sectionTopBottomHeight - headerHeight) / sectionItemHeight;
console.log("index" + index);
if (Math.round(index)>=0&&Math.round(index)<=25){
this.setState({
selectText: this.state.sections[Math.round(index)].key
})
//默認跳轉到 第 index 個section 的第 1 個 item
this.refs.sectionList.scrollToLocation({
animated: true,
sectionIndex: Math.round(index),
itemIndex: 0,
viewOffset: headerHeight
});
}
}
}
複製代碼
這裏根據手指在右邊索引欄的滑動事件,獲取到當前的x軸和y軸的具體值,而後計算出具體的子項目的標題,讓SectionList滾動到具體的index位置;bash
<FlatList
data={info.section.data[0].citys}
horizontal={false}
numColumns={4}
showsHorizontalScrollIndicator={false}
renderItem={({item}) => this._createItem(item)}
keyExtractor={this._extraUniqueKey2}
/>
複製代碼
這樣基本大致的效果就實現了ui
React native實現這個有個很坑爹的地方,就是渲染列表會花費很長的時間,Android是這樣,ios沒試過,因此若是沒有渲染完就去滑動索引欄會報這個scrolltoindex-should-be-used-in-conjunction-with-getitemlayout-or-on
異常,網上找了不少資料,說是SectionList沒有渲染完就調用scrollToLocation,若是要在沒有渲染完以前調用scrollToLocation須要配合getitemlayout使用,可是這個getitemlayout又須要傳入具體item的高度,然而個人FlatList的高度是不肯定的,因此就很坑爹了,找不到辦法解決,只能延時加載右邊索引欄; 代碼以下:this
componentDidMount() {
setTimeout(() => {
this.setState({
canTouch: true
})
}, 1600)
}
複製代碼
這樣所有基本就完成了 項目地址: github.com/mouxuefei/R…spa