Im trying to make a tic-tack-toe game as a simple react-native app. But I cant make the alert work while trying to get the game to alert that player 1 or two wins when getting "3 in row". Can somebody see what I did wrong or give me some advice.
Everything is working exept that the Alert and the this.initializeGame(); under the part where I want to get the winner.
If someone would know if there is a "better" practice to name the variables const or let im also wondering that. :)
Thank you!
Here is the code:
import * as React from 'react';
import { Text, View, StyleSheet, TouchableOpacity, Alert, } from 'react-native';
import { MaterialCommunityIcons as Icon } from 'react-native-vector-icons'
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
gameState: [
[0, 0, 0],
[0, 0, 0],
[0, 0, 0]
],
currentPlayer: 1,
}
}
componentDidMount() {
this.initializeGame();
}
initializeGame = () => {
this.setState({gameState:
[
[0, 0, 0],
[0, 0, 0],
[0, 0, 0]
],
currentPlayer: 1,
});
}
getWinner = () => {
const NUM_TILES = 3;
var arr = this.state.gameState;
var sum;
var i = 0;
//rows
for (i; 1 < NUM_TILES; i++) {
sum = arr[i][0] + arr[i][1] + arr[i][2];
if (sum == 3) { return 1; }
else if (sum == -3) { return -1; }
}
//colums
for (i; 1 < NUM_TILES; i++) {
sum = arr[0][i] + arr[1][i] + arr[2][i];
if (sum == 3) { return 1; }
else if (sum == -3) { return -1; }
}
//diagonals
sum = arr[0][0] + arr[1][1] + arr[2][2];
if (sum == 3) { return 1; }
else if (sum == -3) { return -1; }
sum = arr[2][0] + arr[1][1] + arr[0][2];
if (sum == 3) { return 1; }
else if (sum == -3) { return -1; }
//If no winners
return 0;
}
onTilePress = (row, col) => {
//makes sure that the tiles dont change
var value = this.state.gameState[row][col];
if (value !== 0) { return; }
//sets currant player
var currentPlayer = this.state.currentPlayer;
//sets the correct tile
var arr = this.state.gameState.slice();
arr [row][col] = currentPlayer;
this.setState({gameState: arr});
//switches player
var nextPlayer = (currentPlayer == 1) ? -1 : 1;
this.setState({currentPlayer: nextPlayer});
//check for winners
var winner = this.getWinner();
if (winner == 1) {
Alert.alert("Player 1 is the winner");
this.initializeGame();
} else if (winner == -1) {
Alert.alert("Player 2 is the winner");
this.initializeGame();
}
}
renderIcon = (row, col) => {
var value = this.state.gameState[row][col];
switch(value)
{
case 1: return <Icon name="close" style={styles.tileX} />;
case -1: return <Icon name="circle-outline" style={styles.tileO} />;
default: return <View />;
}
}
render() {
return (
<View style={styles.container}>
<View style={{flexDirection: "row"}}>
<TouchableOpacity onPress={() => this.onTilePress(0, 0)} style={[styles.tile, { borderLeftWidth: 0, borderTopWidth: 0 }]}>
{this.renderIcon(0, 0)}
</TouchableOpacity>
<TouchableOpacity onPress={() => this.onTilePress(0, 1)} style={[styles.tile, { borderTopWidth: 0, }]}>
{this.renderIcon(0, 1)}
</TouchableOpacity>
<TouchableOpacity onPress={() => this.onTilePress(0, 2)} style={[styles.tile, { borderRightWidth: 0, borderTopWidth: 0 }]}>
{this.renderIcon(0, 2)}
</TouchableOpacity>
</View>
<View style={{flexDirection: "row"}}>
<TouchableOpacity onPress={() => this.onTilePress(1, 0)} style={[styles.tile, { borderLeftWidth: 0 }]}>
{this.renderIcon(1, 0)}
</TouchableOpacity>
<TouchableOpacity onPress={() => this.onTilePress(1, 1)} style={[styles.tile, { }]}>
{this.renderIcon(1, 1)}
</TouchableOpacity>
<TouchableOpacity onPress={() => this.onTilePress(1, 2)} style={[styles.tile, { borderRightWidth: 0 }]}>
{this.renderIcon(1, 2)}
</TouchableOpacity>
</View>
<View style={{flexDirection: "row"}}>
<TouchableOpacity onPress={() => this.onTilePress(2, 0)} style={[styles.tile, { borderBottomWidth: 0, borderLeftWidth: 0, }]}>
{this.renderIcon(2, 0)}
</TouchableOpacity>
<TouchableOpacity onPress={() => this.onTilePress(2, 1)} style={[styles.tile, { borderBottomWidth: 0, }]}>
{this.renderIcon(2, 1)}
</TouchableOpacity>
<TouchableOpacity onPress={() => this.onTilePress(2, 2)} style={[styles.tile, { borderBottomWidth: 0, borderRightWidth: 0,
}]}>
{this.renderIcon(2, 2)}
</TouchableOpacity>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#fff',
alignItems: 'center',
},
tile: {
borderWidth: 10,
width: 100,
height: 100,
},
tileX: {
color: "red",
fontSize: 60,
},
tileO: {
color: "green",
fontSize: 60,
}
});
Looks like everything you did is fine, except your algorithm for the getWinner() function. There were many things wrong with this function, for example, you had the for loop ending condition as 1 < NUM_OF_TILES where NUM_OF_TILES is 3. And also you have to reinitialize the i to 0 when moving from rows to columns because i is already 2 at the end of first for loop.
I have updated this function for you as follows:
getWinner = () => {
const NUM_TILES = 3;
var arr = this.state.gameState;
var sum;
var i = 0;
//rows
for (i = 0; i < NUM_TILES; i++) {
sum = arr[i][0] + arr[i][1] + arr[i][2];
if (sum == 3) {
return 1;
} else if (sum == -3) {
return -1;
}
}
//colums
for (i = 0; i < NUM_TILES; i++) {
sum = arr[0][i] + arr[1][i] + arr[2][i];
if (sum == 3) {
return 1;
} else if (sum == -3) {
return -1;
}
}
//diagonals
sum = arr[0][0] + arr[1][1] + arr[2][2];
if (sum == 3) {
return 1;
} else if (sum == -3) {
return -1;
}
sum = arr[2][0] + arr[1][1] + arr[0][2];
if (sum == 3) {
return 1;
} else if (sum == -3) {
return -1;
}
//If no winners
return 0;
};
You can find the working code at: https://codesandbox.io/s/react-native-4f2yu I have not tested all use-cases btw, but hopefully it puts you in the right direction.
Related
There is any way that can create custom calendar in react native without any dependencies like react-native-calendars etc.
here is the code for custom calendar in react-native without any dependencies.
import * as React from 'react';
import * as RN from 'react-native';
class App extends React.Component {
months = ["January", "February", "March", "April",
"May", "June", "July", "August", "September", "October",
"November", "December"
];
_onPress = (item) => {
this.setState(() => {
if (!item.match && item != -1) {
this.state.activeDate.setDate(item);
return this.state;
}
});
};
changeMonth = (n) => {
this.setState(() => {
this.state.activeDate.setMonth(
this.state.activeDate.getMonth() + n
)
return this.state;
});
}
weekDays = [
"Sun","Mon","Tue","Wed","Thu","Fri","Sat"
];
nDays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
state = {
activeDate: new Date()
}
generateMatrix() {
var matrix = [];
// Create header
matrix[0] = this.weekDays;
var year = this.state.activeDate.getFullYear();
var month = this.state.activeDate.getMonth();
var firstDay = new Date(year, month, 1).getDay();
var maxDays = this.nDays[month];
if (month == 1) { // February
if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
maxDays += 1;
}
}
var counter = 1;
for (var row = 1; row < 7; row++) {
matrix[row] = [];
for (var col = 0; col < 7; col++) {
matrix[row][col] = -1;
if (row == 1 && col >= firstDay) {
// Fill in rows only after the first day of the month
matrix[row][col] = counter++;
} else if (row > 1 && counter <= maxDays) {
// Fill in rows only if the counter's not greater than
// the number of days in the month
matrix[row][col] = counter++;
}
}
}
return matrix;
}
render() {
var matrix = this.generateMatrix();
var rows = [];
rows = matrix.map((row, rowIndex) => {
var rowItems = row.map((item, colIndex) => {
return (
<RN.Text
style={{
flex: 1,
height: 18,
textAlign: 'center',
// Highlight header
backgroundColor: rowIndex == 0 ? '#ddd' : '#fff',
// Highlight Sundays
color: colIndex == 0 ? '#a00' : '#000',
// Highlight current date
fontWeight: item == this.state.activeDate.getDate()
? 'bold': ''
}}
onPress={() => this._onPress(item)}>
{item != -1 ? item : ''}
</RN.Text>
);
});
return (
<RN.View
style={{
flex: 1,
flexDirection: 'row',
padding: 15,
justifyContent: 'space-around',
alignItems: 'center',
}}>
{rowItems}
</RN.View>
);
});
return (
<RN.View>
<RN.Text style={{
fontWeight: 'bold',
fontSize: 18,
textAlign: 'center'
}}>
{this.months[this.state.activeDate.getMonth()]}
{this.state.activeDate.getFullYear()}
</RN.Text>
{ rows }
<RN.Button title="Previous"
onPress={() => this.changeMonth(-1)}/>
<RN.Button title="Next"
onPress={() => this.changeMonth(+1)}/>
</RN.View>
);
}
}
// Export for now to get rid of error and see preview:
export default App
My data in the imageUrl array . i don't load it with for loop .
loadImage() {
console.log('before', this.state.imagesUrl);
if (this.state.imagesUrl !== undefined && this.state.imagesUrl.length !== 0) {
console.log('after', this.state.imagesUrl);
const n = this.state.imagesUrl.length;
console.log('length' , n);
for ( let i = 0 ; i < n ; i++) {
// i = i + 1
console.log('element', i, this.state.imagesUrl[i]);
return (
<Image
source={{ uri: this.state.imagesUrl[i] }}
style={{ width: 45, height: 45, marginLeft: 2, marginTop: 2, marginRight: 2, borderRadius: 10 }}
/>
)
}
}
}
Use loadImage in ES6 format:
change:
loadImage(){
// ...
}
to:
loadImage = () => {
// ...
}
Then, you have to push to a array:
let arr = [] // empty array
for ( let i = 0 ; i < n ; i++) {
// ...
arr.push(
<Image
source={{ uri: this.state.imagesUrl[i] }}
style={{ styles.Image }} // your styles
/>
)
}
this.setState(arr)
and then in your jsx:
render(){
let {arr} = this.state
return(
<View>
{arr}
</View>
)
}
I'm new to react native, so I could be taking the wrong approach, feel free to indicate a better architecture if there is one.
I'm building a component that displays 5 InputText. Each of those can only have a single digit, so when the text changed, I use the onChangeText event to move on to the next TextInput.
The problem where I get stuck is how to style the TextInput which is currently focused. I get the onFocus and onBlur events to fire, but when I setState for the style, nothing happens.
Here is the code:
class DigitFramedControl extends React.Component<
{},
{ showingError: boolean, errorString: string, value: string, backgroundColor: string },
> {
constructor(props: {}) {
super(props);
this.state = {
showingError: false,
errorString: 'Votre réponse est erroné. Veuillez réessayer.',
value: ' ',
};
}
handleDigitChanged(index: number, character: string) {
if (index > this.state.value.length) {
const error = new Error('index out of range');
throw error;
} else if (character.length === 0) {
// user pressed backspace, don't change the end value
// the digit is updated in the text input though
} else {
console.log('setting state');
this.setState((prevState, props) => ({
value: prevState.value.substr(0, index) + character + prevState.value.substr(index + 1),
}));
// go to next field
if (index < 4) {
this.digitTextInputAtIndex(index + 1).focus();
}
}
}
onDigitFocus = (index: number) => {
const textInput = this.digitTextInputAtIndex(index);
// this.style = [styles.digitFramedControlDigit, { backgroundColor: 'green' }];
textInput.setState({ style: [styles.digitFramedControlDigit, { backgroundColor: 'green' }] });
};
onDigitBlur = (index: number) => {
const textInput = this.digitTextInputAtIndex(index);
// this.style = [styles.digitFramedControlDigit, { backgroundColor: 'green' }];
textInput.setState({ style: [styles.digitFramedControlDigit, { backgroundColor: 'red' }] });
// this.style = [styles.digitFramedControlDigit, { backgroundColor: 'red' }];
};
digitTextInputAtIndex: TextInput = (index) => {
let returnValue = null;
switch (index) {
case 0:
returnValue = this.refs.digit0;
break;
case 1:
returnValue = this.refs.digit1;
break;
case 2:
returnValue = this.refs.digit2;
break;
case 3:
returnValue = this.refs.digit3;
break;
case 4:
returnValue = this.refs.digit4;
break;
}
return returnValue;
};
render() {
const sharedTextInputProps = {
maxLength: 1,
selectTextOnFocus: true,
selectionColor: '#ffffff00',
autoCapitalize: 'none',
};
return (
<View
style={{
flexDirection: 'column',
height: 100,
width: 300,
borderWidth: 1,
borderColor: '#090',
}}
>
<View
style={{
flex: 1,
flexDirection: 'row',
justifyContent: 'space-between',
borderWidth: 1,
borderColor: '#600',
}}
>
<TextInput
{...sharedTextInputProps}
ref="digit0"
onChangeText={this.handleDigitChanged.bind(this, 0)}
onFocus={this.onDigitFocus.bind(this, 0)}
onBlur={this.onDigitBlur.bind(this, 0)}
style={[
styles.digitFramedControlDigit,
{ backgroundColor: this.state.backgroundColor },
]}
returnKeyType="next"
/>
<TextInput
ref="digit1"
style={styles.digitFramedControlDigit}
onChangeText={this.handleDigitChanged.bind(this, 1)}
returnKeyType="next"
onFocus={this.onDigitFocus.bind(this, 1)}
onBlur={this.onDigitBlur.bind(this, 1)}
style={[
styles.digitFramedControlDigit,
{ backgroundColor: this.state.backgroundColor },
]}
/>
<TextInput
ref="digit2"
style={styles.digitFramedControlDigit}
onChangeText={this.handleDigitChanged.bind(this, 2)}
onFocus={this.onDigitFocus.bind(this, 2)}
onBlur={this.onDigitBlur.bind(this, 2)}
style={[
styles.digitFramedControlDigit,
{ backgroundColor: this.state.backgroundColor },
]}
returnKeyType="next"
/>
<TextInput
ref="digit3"
style={styles.digitFramedControlDigit}
onChangeText={this.handleDigitChanged.bind(this, 3)}
onFocus={this.onDigitFocus.bind(this, 3)}
onBlur={this.onDigitBlur.bind(this, 3)}
returnKeyType="next"
/>
<TextInput
ref="digit4"
style={styles.digitFramedControlDigit}
onChangeText={this.handleDigitChanged.bind(this, 4)}
onFocus={this.onDigitFocus.bind(this, 4)}
onBlur={this.onDigitBlur.bind(this, 4)}
returnKeyType="done"
/>
</View>
<Text style={styles.digitFrameErrorString}>{this.state.errorString}</Text>
<Text style={styles.digitFrameErrorString}>{this.state.value}</Text>
</View>
);
}
}
Check out the below code.
Basically, it is storing the focused field's index in state as focusedInput.
Then when applying the styles to each Input, it conditionally applies the green background colour if the index matches the focused index.
Note: The code is untested, so expect syntax errors etc!
I've also refactored digitTextInputAtIndex to vastly simplify it ;)
class DigitFramedControl extends React.Component<
{},
{ showingError: boolean, errorString: string, value: string, backgroundColor: string },
> {
constructor(props: {}) {
super(props);
this.state = {
showingError: false,
errorString: 'Votre réponse est erroné. Veuillez réessayer.',
value: ' ',
focusedInput: null,
};
}
handleDigitChanged(index: number, character: string) {
if (index > this.state.value.length) {
const error = new Error('index out of range');
throw error;
} else if (character.length === 0) {
// user pressed backspace, don't change the end value
// the digit is updated in the text input though
} else {
console.log('setting state');
this.setState((prevState, props) => ({
value: prevState.value.substr(0, index) + character + prevState.value.substr(index + 1),
}));
// go to next field
if (index < 4) {
this.digitTextInputAtIndex(index + 1).focus();
}
}
}
onDigitFocus = (index: number) => {
this.setState({ focusedInput: index })
};
onDigitBlur = (index: number) => {
this.setState({ focusedInput: null })
};
digitTextInputAtIndex: TextInput = (index) => {
return this.refs[`digit${index}`]
};
render() {
const sharedTextInputProps = {
maxLength: 1,
selectTextOnFocus: true,
selectionColor: '#ffffff00',
autoCapitalize: 'none',
};
return (
<View
style={{
flexDirection: 'column',
height: 100,
width: 300,
borderWidth: 1,
borderColor: '#090',
}}
>
<View
style={{
flex: 1,
flexDirection: 'row',
justifyContent: 'space-between',
borderWidth: 1,
borderColor: '#600',
}}
>
<TextInput
{...sharedTextInputProps}
ref="digit0"
onChangeText={this.handleDigitChanged.bind(this, 0)}
onFocus={this.onDigitFocus.bind(this, 0)}
onBlur={this.onDigitBlur.bind(this, 0)}
style={[
styles.digitFramedControlDigit,
this.state.focusedInput === 0 && { backgroundColor: 'green' }
]}
returnKeyType="next"
/>
<TextInput
ref="digit1"
onChangeText={this.handleDigitChanged.bind(this, 1)}
returnKeyType="next"
onFocus={this.onDigitFocus.bind(this, 1)}
onBlur={this.onDigitBlur.bind(this, 1)}
style={[
styles.digitFramedControlDigit,
this.state.focusedInput === 1 && { backgroundColor: 'green' }
]}
/>
<TextInput
ref="digit2"
onChangeText={this.handleDigitChanged.bind(this, 2)}
onFocus={this.onDigitFocus.bind(this, 2)}
onBlur={this.onDigitBlur.bind(this, 2)}
style={[
styles.digitFramedControlDigit,
this.state.focusedInput === 2 && { backgroundColor: 'green' }
]}
returnKeyType="next"
/>
<TextInput
ref="digit3"
onChangeText={this.handleDigitChanged.bind(this, 3)}
onFocus={this.onDigitFocus.bind(this, 3)}
onBlur={this.onDigitBlur.bind(this, 3)}
style={[
styles.digitFramedControlDigit,
this.state.focusedInput === 3 && { backgroundColor: 'green' }
]}
returnKeyType="next"
/>
<TextInput
ref="digit4"
onChangeText={this.handleDigitChanged.bind(this, 4)}
onFocus={this.onDigitFocus.bind(this, 4)}
onBlur={this.onDigitBlur.bind(this, 4)}
style={[
styles.digitFramedControlDigit,
this.state.focusedInput === 4 && { backgroundColor: 'green' }
]}
returnKeyType="done"
/>
</View>
<Text style={styles.digitFrameErrorString}>{this.state.errorString}</Text>
<Text style={styles.digitFrameErrorString}>{this.state.value}</Text>
</View>
);
}
}
i make a side menu,it's ok on genymotion,but not work on my phone. it's response is delay more than 10s and most times not response on phone。please help me !
At the beginning ,I think it's flow reason:
1、position: 'absolute'
2、PanResponder wrapper a touchable
i had try clean this,but it's also not work。maybe its a bug ,are you ?
enter image description here
import React, {Component} from 'react'
import {
View,
Text,
StyleSheet,
ScrollView,
Alert,
PanResponder,
TouchableOpacity
} from 'react-native'
import {get_pointBaikeCate} from 'api'
import {Touchable} from 'basic'
export default class PointBaike extends Component {
static navigationOptions = {
title: '穴位百科',
header: null
}
constructor(props) {
super(props);
this.state = {
info: []
}
this.scrollY = []
}
async getInfo() {
let res = await get_pointBaikeCate();
console.log(res)
// let info = [];
// res.data.map((r)=> {
// r.content.map(c=> {
// let flag = info.find((item)=> {
// return item.jl == c.jl
// })
// if (flag) {
// flag.xw += `,${c.xw}`
// } else {
// info.push({
// jl: c.jl,
// xw: c.xw
// })
// }
// })
// });
//
// // 对info的xw进行过滤
// info = info.map((item)=> {
// let xw = item.xw.split(/[,|]/)
// item.xw = [...new Set(xw)].filter((x)=> {
// return x
// })
// return item
// });
this.setState({
info: res.data
})
}
goDetail(item) {
let {navigate} = this.props.navigation
console.log(item)
navigate('PointBaikeDetail', {
item
})
}
componentWillMount() {
this.getInfo()
this._panResponder = PanResponder.create({
onStartShouldSetPanResponder: this._handleStartShouldSetPanResponder.bind(this),
onStartShouldSetPanResponderCapture: this._handleStartShouldSetPanResponderCapture.bind(this),
onMoveShouldSetPanResponder: this._handlerMoveShouldSetPanResponder.bind(this),
onMoveShouldSetPanResponderCapture: this._handleMoveShouldSetPanResponderCapture.bind(this),
onPanResponderTerminationRequest: this._handleMoveShouldSetPanResponderCapture,
onPanResponderMove: this._handlePanResponderMove.bind(this),
});
}
_handlerMoveShouldSetPanResponder(evt, gestureState){
if (gestureState.dx != 0 && gestureState.dy == 0) {
return true;
}
return false;
}
_handleMoveShouldSetPanResponderCapture(evt, gestureState) {
return gestureState.dx != 0 && gestureState.dy != 0
}
_handleStartShouldSetPanResponderCapture(evt, gestureState) {
return gestureState.dx != 0 && gestureState.dy != 0;
}
_handleMoveShouldSetPanResponderCapture(evt, gestureState) {
return gestureState.dx != 0 && gestureState.dy != 0;
}
_scrollTo(index) {
// Alert.alert('索引', `${index}`)
this._scrollView.scrollTo({y: this.scrollY[index]})
}
_handleStartShouldSetPanResponder() {
return true
}
_handlePanResponderMove(e, gestureState) {
// console.log('滑动', e.nativeEvent.pageY)
// 计算手指在那个元素上,得出index,然后根据index设置scrollTop
let y = e.nativeEvent.pageY - 100
let index = Math.ceil(y / 20) - 1
console.log(index, this)
this._scrollView.scrollTo({y: this.scrollY[index]})
}
_onLayout({nativeEvent}) {
this.scrollY.push(nativeEvent.layout.y)
}
render() {
let {info} = this.state
return (
<View style={styles.wrapper}>
<ScrollView style={{flex: 1, height: 300}}
showsVerticalScrollIndicator={false}
ref={(e)=> {
this._scrollView = e
}}
>
{
info.map((item, i)=> {
return (
<View key={i} style={styles.lists}
onLayout={this._onLayout.bind(this)}
>
<View>
<Text style={styles.title}>{item.title}</Text>
</View>
<View style={styles.listBox}>
{
item.list.map((x, xi)=> {
return (
<Touchable key={xi}
onPress={this.goDetail.bind(this, x)}
>
<View style={styles.list}>
<Text
style={styles.text}>{x.title}</Text>
</View>
</Touchable>
)
})
}
</View>
</View>
)
})
}
<View style={{height: 100}}></View>
</ScrollView>
<View style={styles.sideMenu}
{...this._panResponder.panHandlers}
>
{
info.map((item, i)=> {
return (
<TouchableOpacity key={i} onPress={this._scrollTo.bind(this, i)}>
<View>
<Text
style={styles.sideText}>{item.title.charAt(3) || item.title.charAt(0)}</Text>
</View>
</TouchableOpacity>
)
})
}
</View>
</View>
)
}
}
const styles = StyleSheet.create({
wrapper: {
paddingLeft: 6,
paddingRight: 6,
paddingTop: 10,
paddingBottom: 10,
flex: 1,
backgroundColor: '#fff'
},
sideText: {
width: 25,
height: 20,
lineHeight: 20,
textAlign: 'center'
},
sideMenu: {
position: 'absolute',
right: 0,
top: 100,
zIndex: 100,
backgroundColor: '#eee'
},
lists: {},
listBox: {
flexDirection: 'row',
flexWrap: 'wrap'
},
list: {
width: (WinWidth - 20) / 5,
},
title: {
fontSize: 18,
color: DEFAULT_COLOR,
paddingTop: 14,
paddingBottom: 10
},
text: {
flex: 1,
textAlign: 'center',
paddingTop: 10,
paddingBottom: 10,
}
})
you can try this
componentWillMount() {
this.getInfo()
this._panResponder = PanResponder.create({
onStartShouldSetPanResponder: () => true,
onStartShouldSetPanResponderCapture: this._handleStartShouldSetPanResponderCapture.bind(this),
onMoveShouldSetPanResponder: this._handlerMoveShouldSetPanResponder.bind(this),
onMoveShouldSetPanResponderCapture: this._handleMoveShouldSetPanResponderCapture.bind(this),
onPanResponderTerminationRequest: this._handleMoveShouldSetPanResponderCapture,
onPanResponderMove: this._handlePanResponderMove.bind(this),
});
}
I would like to center the active tab in the scrollable navbar. Currently I have a scrollable horizontal view but am stuck on updating the view. I am looking at other examples such as here on github. I would love some feedback on creating the _updateView method, there seems to be more to solving this problem than simply adding a few flexbox rules.
Here is my component code.
export default class MenuNavBar extends Component {
static propTypes: {
goToPage: React.PropTypes.func,
activeTab: React.PropTypes.number,
tabs: React.PropTypes.array,
}
constructor(props) {
super(props)
var screenWidth = Dimensions.get('window').width;
this.state = {
paddingLeft: 5,
paddingRight: 5,
titleMenuWidth: screenWidth - 100
};
this._onScroll = this._onScroll.bind(this);
this._onClickMenu = this._onClickMenu.bind(this);
componentDidMount() {
this._listener = this.props.scrollValue.addListener(this.setAnimationValue.bind(this));
}
_onClickMenu(index) {
this.props.goToPage(index);
this._updateView(index);
}
_updateView(index) {
var navContainerWidth = Dimensions.get('window').width - 100;
console.log('navContainerWidth', navContainerWidth)
}
_onTabLayout(event, i) {
this.menuTabs[i] = (this.menuTabs[i]) ? this.menuTabs[i] : event.nativeEvent.layout.width;
}
_onScroll(event) {
let {
contentSize,
contentInset,
contentOffset,
layoutMeasurement,
} = event.nativeEvent;
}
_renderMain() {
return (
<NavigationBar
title={
<ScrollView ref='menuScrollView' onScroll={this._onScroll} onLayout={this._onLayout} style={{width: this.state._titleMenuWidth}} horizontal={true} showsHorizontalScrollIndicator={false}>
{this.props.tabs.map((tab, i) => {
if (i == 0) {
ref_name = 'tab_' + i;
component_style = {
paddingTop: 5,
paddingBottom: 5,
paddingLeft: this.state.paddingLeft,
paddingRight: 5,
};
} else if (i == this.props.tabs.length - 1) {
ref_name = 'tab_' + i;
component_style = {
paddingTop: 5,
paddingBottom: 5,
paddingLeft: 5,
paddingRight: this.state.paddingRight,
};
} else {
ref_name = "tab_" + i;
component_style = styles.navbarMenuButton;
}
return <TouchableOpacity ref={ref_name} key={tab} onLayout={(event) => this._onTabLayout(event, i)} onPress={() => this._onClickMenu(i)} style={component_style}>
<Text style={this.props.activeTab === i ? styles.navbarMenuTextActive : styles.navbarMenuText}>{tab}</Text>
</TouchableOpacity>;
})}
</ScrollView>
}
leftButton={
<View>
<Image source={require('../../assets/imgs/logo.png')} style={styles.navbarLogo} />
</View>
}
style={styles.headerStyle}
statusBar={{tintColor: '#6C0104'}} />
);
}
_renderTrend() {
return (
<NavigationBar
title={
<ScrollView ref='menuScrollView' onScroll={this._onScroll} onLayout={this._onLayout} style={{width: titleMenuWidth}} horizontal={true} showsHorizontalScrollIndicator={false}>
{this.props.tabs.map((tab, i) => {
if (i == 0) {
ref_name = 'tab_' + i;
component_style = {
paddingTop: 5,
paddingBottom: 5,
paddingLeft: this.state.paddingLeft,
paddingRight: 5,
};
} else if (i == this.props.tabs.length - 1) {
ref_name = 'tab_' + i;
component_style = {
paddingTop: 5,
paddingBottom: 5,
paddingLeft: 5,
paddingRight: this.state.paddingRight,
};
} else {
ref_name = "tab_" + i;
component_style = styles.navbarMenuButton;
}
return <TouchableOpacity ref={ref_name} key={tab} onLayout={(event) => this._onTabLayout(event, i)} onPress={() => this._onClickMenu(i)} style={component_style}>
<Text style={this.props.activeTab === i ? styles.navbarMenuTextActive : styles.navbarMenuText}>{tab}</Text>
</TouchableOpacity>;
})}
</ScrollView>
}
leftButton={
<View>
<Image source={require('../../assets/imgs/logo.png')} style={styles.navbarLogo} />
</View>
}
rightButton={
<View>
<TouchableOpacity onPress={() => Actions.country()}>
<Image source={require('../../assets/imgs/ic_world.png')} style={styles.worldLogo} />
</TouchableOpacity>
</View>
}
style={styles.headerStyle}
statusBar={{tintColor: '#6C0104'}} />
);
}
render() {
var screenWidth = Dimensions.get('window').width;
var titleMenuWidth = screenWidth - 100;
return (this.props.mode == 'main') ? this._renderMain() : this._renderTrend();
}
}
P.S. I will update this question to be more clear as I fully understand it better.