Issue1.提示訊息框並沒有ScrollView的功能。
Issue2.提示訊息框呈現半透明,且如果UI有重疊的話會干擾點擊。
新的測試後再多一個Issue:
Issue3.Android的提示框會將下面的物件擠下去。
Gitgub上index.js原始碼如下:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {
ListView,
Platform,
StyleSheet,
Text,
TextInput,
View,
ViewPropTypes as RNViewPropTypes
} from 'react-native';
const ViewPropTypes = RNViewPropTypes || View.propTypes;
class Autocomplete extends Component {
static propTypes = {
...TextInput.propTypes,
/**
* These styles will be applied to the container which
* surrounds the autocomplete component.
*/
containerStyle: ViewPropTypes.style,
/**
* Assign an array of data objects which should be
* rendered in respect to the entered text.
*/
data: PropTypes.array,
/**
* Set to `true` to hide the suggestion list.
*/
hideResults: PropTypes.bool,
/*
* These styles will be applied to the container which surrounds
* the textInput component.
*/
inputContainerStyle: ViewPropTypes.style,
/*
* Set `keyboardShouldPersistTaps` to true if RN version is <= 0.39.
*/
keyboardShouldPersistTaps: PropTypes.oneOfType([
PropTypes.string,
PropTypes.bool
]),
/*
* These styles will be applied to the container which surrounds
* the result list.
*/
listContainerStyle: ViewPropTypes.style,
/**
* These style will be applied to the result list.
*/
listStyle: ListView.propTypes.style,
/**
* `onShowResults` will be called when list is going to
* show/hide results.
*/
onShowResults: PropTypes.func,
/**
* method for intercepting swipe on ListView. Used for ScrollView support on Android
*/
onStartShouldSetResponderCapture: PropTypes.func,
/**
* `renderItem` will be called to render the data objects
* which will be displayed in the result view below the
* text input.
*/
renderItem: PropTypes.func,
/**
* `renderSeparator` will be called to render the list separators
* which will be displayed between the list elements in the result view
* below the text input.
*/
renderSeparator: PropTypes.func,
/**
* renders custom TextInput. All props passed to this function.
*/
renderTextInput: PropTypes.func,
/**
* `rowHasChanged` will be used for data objects comparison for dataSource
*/
rowHasChanged: PropTypes.func
};
static defaultProps = {
data: [],
defaultValue: '',
keyboardShouldPersistTaps: 'always',
onStartShouldSetResponderCapture: () => false,
renderItem: rowData => <Text>{rowData}</Text>,
renderSeparator: null,
renderTextInput: props => <TextInput {...props} />,
rowHasChanged: (r1, r2) => r1 !== r2
};
constructor(props) {
super(props);
const ds = new ListView.DataSource({ rowHasChanged: props.rowHasChanged });
this.state = { dataSource: ds.cloneWithRows(props.data) };
this.resultList = null;
}
componentWillReceiveProps({ data }) {
const dataSource = this.state.dataSource.cloneWithRows(data);
this.setState({ dataSource });
}
/**
* Proxy `blur()` to autocomplete's text input.
*/
blur() {
const { textInput } = this;
textInput && textInput.blur();
}
/**
* Proxy `focus()` to autocomplete's text input.
*/
focus() {
const { textInput } = this;
textInput && textInput.focus();
}
renderResultList() {
const { dataSource } = this.state;
const {
listStyle,
renderItem,
renderSeparator,
keyboardShouldPersistTaps,
onEndReached,
onEndReachedThreshold
} = this.props;
return (
<ListView
ref={(resultList) => { this.resultList = resultList; }}
dataSource={dataSource}
keyboardShouldPersistTaps={keyboardShouldPersistTaps}
renderRow={renderItem}
renderSeparator={renderSeparator}
onEndReached={onEndReached}
onEndReachedThreshold={onEndReachedThreshold}
style={[styles.list, listStyle]}
/>
);
}
renderTextInput() {
const { onEndEditing, renderTextInput, style } = this.props;
const props = {
style: [styles.input, style],
ref: ref => (this.textInput = ref),
onEndEditing: e => onEndEditing && onEndEditing(e),
...this.props
};
return renderTextInput(props);
}
render() {
const { dataSource } = this.state;
const {
containerStyle,
hideResults,
inputContainerStyle,
listContainerStyle,
onShowResults,
onStartShouldSetResponderCapture
} = this.props;
const showResults = dataSource.getRowCount() > 0;
// Notify listener if the suggestion will be shown.
onShowResults && onShowResults(showResults);
return (
<View style={[styles.container, containerStyle]}>
<View style={[styles.inputContainer, inputContainerStyle]}>
{this.renderTextInput()}
</View>
{!hideResults && (
<View
style={listContainerStyle}
onStartShouldSetResponderCapture={onStartShouldSetResponderCapture}
>
{showResults && this.renderResultList()}
</View>
)}
</View>
);
}
}
const border = {
borderColor: '#b9b9b9',
borderRadius: 1,
borderWidth: 1
};
const androidStyles = {
container: {
flex: 1
},
inputContainer: {
...border,
marginBottom: 0
},
list: {
...border,
backgroundColor: 'white',
borderTopWidth: 0,
margin: 10,
marginTop: 0
}
};
const iosStyles = {
container: {
zIndex: 1
},
inputContainer: {
...border
},
input: {
backgroundColor: 'white',
height: 40,
paddingLeft: 3
},
list: {
...border,
backgroundColor: 'white',
borderTopWidth: 0,
left: 0,
position: 'absolute',
right: 0
}
};
const styles = StyleSheet.create({
input: {
backgroundColor: 'white',
height: 40,
paddingLeft: 3
},
...Platform.select({
android: { ...androidStyles },
ios: { ...iosStyles }
})
});
export default Autocomplete;
以下先這個範例做個簡單的筆記:1. Issue1: 186行可以知道,提示框是由renderResultList這個函式回傳的,136行可以知道是用ListView的方式呈現,因此本身是具有ScrollView的功能,因此猜測應該是因為View的高度關係造成沒有ScrollView的效果,144行可以知道style是由list與listStyle(props)兩個屬性負責,有兩種方式,一種是建構Autocomplete傳入listStyle的屬性設定maxHeight,或是直接修改原始碼的list(style)。
2. Issue2: 關於這個問題我一開始認知應為zIndex的設定即可,但測試後發現Android與iOS設定的不一樣,iOS是設定zIndex,Android是設定elevation,且位置不一樣,目前找一些資料還不知道為什麼zIndex在Android上會怪怪的,提示框直接沒有顯示出來,這邊筆記一下iOS該怎麼設定:
如此圖,我是將Autocomplete在包裝成一個物件來使用,而zIndex應要加上View這個標籤中,這樣就能解決半透明且干擾點擊的情況,但是Android這裡不能設定zIndex,應維持default。
那Android要設定的位置是下圖Issue3的elevation參數,且是設定在list中(或是透過listStyle傳入)。
3. Issue3: 245行可以得知,Android與iOS的style設定方式是不同的,按照Android的設定flex為1(202行)的話,會影響到其他的UI,所以可以將它改成與iOS相同,因此這個屬性就不需要分版本了,修改成以下並寫在245行中即可,其中主要的排版是使用position將他設定為absolute:
綜合以上,我將這個範例簡單改寫成以下:
Issue1: 在39行部份解決
Issue2: 在33行部份解決
export default class AutocompleteExample extends Component {
static propTypes = {
films: PropTypes.array,
placeholder: PropTypes.string,
};
static defaultProps = {
films: [],
placeholder: '',
};
constructor(props) {
super(props);
this.state = {
text: ''
};
}
findFilm(text) {
{/*從輸入找出陣列符合*/}
if (text === '') {
return [];
}
const { films } = this.props;
const regex = new RegExp(`${text.trim()}`, 'i');
return films.filter(film => film.id.search(regex) >= 0);
}
render() {
const { text } = this.state;
const films = this.findFilm(text);
const comp = (a, b) => a.toLowerCase().trim() === b.toLowerCase().trim();
const { placeholder } = this.props;
return (
<View style={styles.Autocomplete}>
<Autocomplete
autoCapitalize="none"
autoCorrect={false}
keyboardShouldPersistTaps='always'
containerStyle={styles.autocompleteContainer}
listStyle={{maxHeight:120,}}
inputContainerStyle={{minWidth:115,maxWidth:200}}
data={films.length === 1 && comp(text, films[0].id) ? [] : films}
defaultValue={text}
onChangeText={text => this.setState({ text: text })}
placeholder={ placeholder }
renderItem={
({id}) => (
<TouchableOpacity
style={{padding:10}}
onPress={() => this.setState({ text: id })}
>
{/*autocomplete 內容*/}
<Text style={styles.itemText}>
{id}
</Text>
</TouchableOpacity>
)
}
/>
</View>
);
}
}
const androidStyles = {
Autocomplete: {
},
};
const iosStyles = {
Autocomplete: {
zIndex:1
},
};
const styles = StyleSheet.create({
...Platform.select({
android: { ...androidStyles },
ios: { ...iosStyles }
}),
itemText: {
fontSize: 24,
backgroundColor: "#FFFFFF",
opacity:100,
},
autocompleteContainer: {
marginLeft: 10,
marginRight: 10
},
});
export default AutocompleteExample;
Github上的原始碼也有做一些小調整,如下:
1.Issue2、3: 移除208、229行的list,整合合併在245行的部分。
list: {
...border,
backgroundColor: 'white',
borderTopWidth: 0,
position: 'absolute',
left: 0,
right: 0,
elevation: 2,
},
2.Issue3: 移除201、202行androidStylescontainer的設定。最後呼叫調整完畢的AutocompleteExample如下:
export default class App extends Component {
constructor(props) {
super(props);
}
render() {
return (
<View style={styles.container}>
<AutocompleteExample
placeholder = 'test'
films= {
[
{id:'001'},
{id:'002'},
{id:'003'},
{id:'004'},
{id:'005'},
{id:'006'},
{id:'007'},
{id:'008'},
{id:'009'},
{id:'010'},
{id:'011'},
{id:'012'},
{id:'013'},
{id:'014'},
]
}
/>
</View>
);
}
}
畫面如下:

沒有留言:
張貼留言