Skip to content

Commit f9511c1

Browse files
committed
Update the Android status bar to match the background color
1 parent b9f674a commit f9511c1

File tree

2 files changed

+93
-109
lines changed

2 files changed

+93
-109
lines changed

AppIntro.js

+27-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import assign from 'assign-deep';
22
import React, { Component, PropTypes } from 'react';
33
import {
4+
StatusBar,
45
StyleSheet,
56
Text,
67
View,
@@ -240,7 +241,7 @@ export default class AppIntro extends Component {
240241
const AnimatedStyle1 = this.getTransform(index, 10, level);
241242
const AnimatedStyle2 = this.getTransform(index, 0, level);
242243
const AnimatedStyle3 = this.getTransform(index, 15, level);
243-
const imgSource = (typeof img === 'string') ? {uri: img} : img;
244+
const imgSource = (typeof img === 'string') ? {uri: img} : img;
244245
const pageView = (
245246
<View style={[this.styles.slide, { backgroundColor }]} showsPagination={false} key={index}>
246247
<Animated.View style={[this.styles.header, ...AnimatedStyle1.transform]}>
@@ -284,6 +285,22 @@ export default class AppIntro extends Component {
284285
return animatedChild;
285286
}
286287

288+
shadeStatusBarColor(color, percent) {
289+
const first = parseInt(color.slice(1), 16);
290+
const black = first & 0x0000FF;
291+
const green = first >> 8 & 0x00FF;
292+
const percentage = percent < 0 ? percent * -1 : percent;
293+
const red = first >> 16;
294+
const theme = percent < 0 ? 0 : 255;
295+
const finalColor = (0x1000000 + (Math.round((theme - red) * percentage) + red) * 0x10000 + (Math.round((theme - green) * percentage) + green) * 0x100 + (Math.round((theme - black) * percentage) + black)).toString(16).slice(1);
296+
297+
return `#${finalColor}`;
298+
}
299+
300+
isToTintStatusBar() {
301+
return this.props.pageArray && this.props.pageArray.length > 0 && Platform.OS === 'android'
302+
}
303+
287304
render() {
288305
const childrens = this.props.children;
289306
const { pageArray } = this.props;
@@ -314,6 +331,11 @@ export default class AppIntro extends Component {
314331
});
315332
}
316333
}
334+
335+
if (this.isToTintStatusBar()) {
336+
StatusBar.setBackgroundColor(this.shadeStatusBarColor(this.props.pageArray[0].backgroundColor, -0.3), false);
337+
}
338+
317339
return (
318340
<View>
319341
{androidPages}
@@ -322,6 +344,10 @@ export default class AppIntro extends Component {
322344
index={this.props.defaultIndex}
323345
renderPagination={this.renderPagination}
324346
onMomentumScrollEnd={(e, state) => {
347+
if (this.isToTintStatusBar()) {
348+
StatusBar.setBackgroundColor(this.shadeStatusBarColor(this.props.pageArray[state.index].backgroundColor, -0.3), false);
349+
}
350+
325351
this.props.onSlideChange(state.index, state.total);
326352
}}
327353
onScroll={Animated.event(

Example/AppIntro.js

+66-108
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import assign from 'assign-deep';
22
import React, { Component, PropTypes } from 'react';
33
import {
4+
StatusBar,
45
StyleSheet,
56
Text,
67
View,
@@ -11,6 +12,10 @@ import {
1112
Platform,
1213
} from 'react-native';
1314
import Swiper from 'react-native-swiper';
15+
import DoneButton from './components/DoneButton';
16+
import SkipButton from './components/SkipButton';
17+
import RenderDots from './components/Dots';
18+
1419
const windowsWidth = Dimensions.get('window').width;
1520
const windowsHeight = Dimensions.get('window').height;
1621

@@ -62,13 +67,6 @@ const defaulStyles = {
6267
},
6368
activeDotStyle: {
6469
backgroundColor: '#fff',
65-
width: 13,
66-
height: 13,
67-
borderRadius: 7,
68-
marginLeft: 7,
69-
marginRight: 7,
70-
marginTop: 7,
71-
marginBottom: 7,
7270
},
7371
paginationContainer: {
7472
position: 'absolute',
@@ -189,21 +187,6 @@ export default class AppIntro extends Component {
189187
}
190188

191189
renderPagination = (index, total, context) => {
192-
const { activeDotColor, dotColor, rightTextColor, leftTextColor } = this.props;
193-
const ActiveDot = (
194-
<View
195-
style={[this.styles.activeDotStyle, { backgroundColor: activeDotColor }]}
196-
/>
197-
);
198-
const Dot = <View style={[this.styles.dotStyle, { backgroundColor: dotColor }]} />;
199-
let dots = [];
200-
for (let i = 0; i < total; i++) {
201-
dots.push(i === index ?
202-
React.cloneElement(ActiveDot, { key: i })
203-
:
204-
React.cloneElement(Dot, { key: i })
205-
);
206-
}
207190
let isDoneBtnShow;
208191
let isSkipBtnShow;
209192
if (index === total - 1) {
@@ -219,91 +202,31 @@ export default class AppIntro extends Component {
219202
isDoneBtnShow = false;
220203
isSkipBtnShow = true;
221204
}
222-
let controllBts;
223-
if (Platform.OS === 'ios') {
224-
controllBts = (
225-
<View style={this.styles.paginationContainer}>
226-
<Animated.View style={[this.styles.btnContainer, {
227-
opacity: this.state.skipFadeOpacity,
228-
transform: [{
229-
translateX: this.state.skipFadeOpacity.interpolate({
230-
inputRange: [0, 1],
231-
outputRange: [0, 15],
232-
}),
233-
}],
234-
}]}
235-
>
236-
<TouchableOpacity
237-
style={this.styles.full}
238-
onPress={isSkipBtnShow ? () => this.props.onSkipBtnClick(index) : null}
239-
>
240-
<Text style={[this.styles.controllText, { color: leftTextColor }]}>{this.props.skipBtnLabel}</Text>
241-
</TouchableOpacity>
242-
</Animated.View>
243-
<View style={this.styles.dotContainer}>
244-
{dots}
245-
</View>
246-
<View style={this.styles.btnContainer}>
247-
<Animated.View style={[this.styles.full, { height: 0 }, {
248-
opacity: this.state.doneFadeOpacity,
249-
transform: [{
250-
translateX: this.state.skipFadeOpacity.interpolate({
251-
inputRange: [0, 1],
252-
outputRange: [0, 20],
253-
}),
254-
}],
255-
}]}
256-
>
257-
<View style={this.styles.full}>
258-
<Text style={[this.styles.controllText, {
259-
color: rightTextColor, paddingRight: 30,
260-
}]}
261-
>{this.props.doneBtnLabel}</Text>
262-
</View>
263-
</Animated.View>
264-
<Animated.View style={[this.styles.full, { height: 0 }, { opacity: this.state.nextOpacity }]}>
265-
<TouchableOpacity style={this.styles.full}
266-
onPress={ isDoneBtnShow ?
267-
this.props.onDoneBtnClick : this.onNextBtnClick.bind(this, context)}
268-
>
269-
<Text style={[this.styles.nextButtonText, { color: rightTextColor }]}>{this.props.nextBtnLabel}</Text>
270-
</TouchableOpacity>
271-
</Animated.View>
272-
</View>
273-
</View>
274-
);
275-
} else {
276-
controllBts = (
277-
<View style={this.styles.paginationContainer}>
278-
<View style={[this.styles.btnContainer, {
279-
paddingBottom: 5,
280-
opacity: isSkipBtnShow ? 1 : 0,
281-
}]}
282-
>
283-
<TouchableOpacity
284-
style={this.styles.full}
285-
onPress={isSkipBtnShow ? () => this.props.onSkipBtnClick(index) : null}
286-
>
287-
<Text style={[this.styles.controllText, { color: leftTextColor }]}>{this.props.skipBtnLabel}</Text>
288-
</TouchableOpacity>
289-
</View>
290-
<View style={this.styles.dotContainer}>
291-
{dots}
292-
</View>
293-
<View style={[this.styles.btnContainer, { height: 0, paddingBottom: 5 }]}>
294-
<TouchableOpacity style={this.styles.full}
295-
onPress={ isDoneBtnShow ?
296-
this.props.onDoneBtnClick : this.onNextBtnClick.bind(this, context)}
297-
>
298-
<Text style={[this.styles.nextButtonText, { color: rightTextColor }]}>
299-
{isDoneBtnShow ? this.props.doneBtnLabel : this.props.nextBtnLabel}
300-
</Text>
301-
</TouchableOpacity>
302-
</View>
303-
</View>
304-
);
305-
}
306-
return controllBts;
205+
return (
206+
<View style={[this.styles.paginationContainer]}>
207+
{this.props.showSkipButton ? <SkipButton
208+
{...this.props}
209+
{...this.state}
210+
isSkipBtnShow={isSkipBtnShow}
211+
styles={this.styles}
212+
onSkipBtnClick={() => this.props.onSkipBtnClick(index)} /> :
213+
<View style={this.styles.btnContainer} />
214+
}
215+
{this.props.showDots && RenderDots(index, total, {
216+
...this.props,
217+
styles: this.styles
218+
})}
219+
{this.props.showDoneButton ? <DoneButton
220+
{...this.props}
221+
{...this.state}
222+
isDoneBtnShow={isDoneBtnShow}
223+
styles={this.styles}
224+
onNextBtnClick={this.onNextBtnClick.bind(this, context)}
225+
onDoneBtnClick={this.props.onDoneBtnClick} /> :
226+
<View style={this.styles.btnContainer} />
227+
}
228+
</View>
229+
);
307230
}
308231

309232
renderBasicSlidePage = (index, {
@@ -318,10 +241,11 @@ export default class AppIntro extends Component {
318241
const AnimatedStyle1 = this.getTransform(index, 10, level);
319242
const AnimatedStyle2 = this.getTransform(index, 0, level);
320243
const AnimatedStyle3 = this.getTransform(index, 15, level);
244+
const imgSource = (typeof img === 'string') ? {uri: img} : img;
321245
const pageView = (
322246
<View style={[this.styles.slide, { backgroundColor }]} showsPagination={false} key={index}>
323247
<Animated.View style={[this.styles.header, ...AnimatedStyle1.transform]}>
324-
<Image style={imgStyle} source={{ uri: img }} />
248+
<Image style={imgStyle} source={imgSource} />
325249
</Animated.View>
326250
<View style={this.styles.info}>
327251
<Animated.View style={AnimatedStyle2.transform}>
@@ -361,6 +285,22 @@ export default class AppIntro extends Component {
361285
return animatedChild;
362286
}
363287

288+
shadeStatusBarColor(color, percent) {
289+
const first = parseInt(color.slice(1), 16);
290+
const black = first & 0x0000FF;
291+
const green = first >> 8 & 0x00FF;
292+
const percentage = percent < 0 ? percent * -1 : percent;
293+
const red = first >> 16;
294+
const theme = percent < 0 ? 0 : 255;
295+
const finalColor = (0x1000000 + (Math.round((theme - red) * percentage) + red) * 0x10000 + (Math.round((theme - green) * percentage) + green) * 0x100 + (Math.round((theme - black) * percentage) + black)).toString(16).slice(1);
296+
297+
return `#${finalColor}`;
298+
}
299+
300+
isToTintStatusBar() {
301+
return this.props.pageArray && this.props.pageArray.length > 0 && Platform.OS === 'android'
302+
}
303+
364304
render() {
365305
const childrens = this.props.children;
366306
const { pageArray } = this.props;
@@ -391,13 +331,23 @@ export default class AppIntro extends Component {
391331
});
392332
}
393333
}
334+
335+
if (this.isToTintStatusBar()) {
336+
StatusBar.setBackgroundColor(this.shadeStatusBarColor(this.props.pageArray[0].backgroundColor, -0.3), false);
337+
}
338+
394339
return (
395340
<View>
396341
{androidPages}
397342
<Swiper
398343
loop={false}
344+
index={this.props.defaultIndex}
399345
renderPagination={this.renderPagination}
400346
onMomentumScrollEnd={(e, state) => {
347+
if (this.isToTintStatusBar()) {
348+
StatusBar.setBackgroundColor(this.shadeStatusBarColor(this.props.pageArray[state.index].backgroundColor, -0.3), false);
349+
}
350+
401351
this.props.onSlideChange(state.index, state.total);
402352
}}
403353
onScroll={Animated.event(
@@ -434,6 +384,10 @@ AppIntro.propTypes = {
434384
PropTypes.element,
435385
]),
436386
customStyles: PropTypes.object,
387+
defaultIndex: PropTypes.number,
388+
showSkipButton: PropTypes.bool,
389+
showDoneButton: PropTypes.bool,
390+
showDots: PropTypes.bool,
437391
};
438392

439393
AppIntro.defaultProps = {
@@ -449,4 +403,8 @@ AppIntro.defaultProps = {
449403
doneBtnLabel: 'Done',
450404
skipBtnLabel: 'Skip',
451405
nextBtnLabel: '›',
406+
defaultIndex: 0,
407+
showSkipButton: true,
408+
showDoneButton: true,
409+
showDots: true
452410
};

0 commit comments

Comments
 (0)