Skip to content
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,28 @@ You can register to `ScrollViewResponder` events `onKeyboardWillShow` and `onKey
</KeyboardAwareScrollView>
```

## Android Support
First, Android natively has this feature, you can easily enable it by setting `windowSoftInputMode` in `AndroidManifest.xml`. Check [here](https://developer.android.com/guide/topics/manifest/activity-element.html#wsoft).

But if you want to use feature like `extraHeight`, you need to enable Android Support with the following steps:

- Make sure you are using react-native `0.46` or above.
- Set `windowSoftInputMode` to `adjustPan` in `AndroidManifest.xml`.
- Set `enableOnAndroid` property to `true`.

Android Suppor is not perfect, here is the support list:

| **Prop** | **Android Support** |
|----------|-----------------|
| `viewIsInsideTabBar` | Yes |
| `resetScrollToCoords` | Yes |
| `enableAutoAutomaticScroll` | Yes |
| `extraHeight` | Yes |
| `extraScrollHeight` | Yes |
| `enableResetScrollToCoords` | Yes |
| `keyboardOpeningTime` | No |


## API
### Props
All the `ScrollView`/`ListView` props will be passed.
Expand All @@ -104,6 +126,7 @@ All the `ScrollView`/`ListView` props will be passed.
| `extraScrollHeight` | `number` | Adds an extra offset to the keyboard. Useful if you want to stick elements above the keyboard. |
| `enableResetScrollToCoords` | `boolean` | Lets the user enable or disable automatic resetScrollToCoords. |
| `keyboardOpeningTime` | `number` | Sets the delay time before scrolling to new position, default is 250 |
| `enableOnAndroid` | `boolean` | Enable Android Support |

| **Method** | **Parameter** | **Description** |
|------------|---------------|-----------------|
Expand Down
22 changes: 20 additions & 2 deletions lib/KeyboardAwareListView.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import React from 'react'
import createReactClass from 'create-react-class'
import { ListView } from 'react-native'
import { ListView, Platform } from 'react-native'
import PropTypes from 'prop-types'
import KeyboardAwareMixin from './KeyboardAwareMixin'

Expand All @@ -13,6 +13,7 @@ const KeyboardAwareListView = createReactClass({
x: PropTypes.number,
y: PropTypes.number,
}),
enableOnAndroid: React.PropTypes.bool,
},
mixins: [KeyboardAwareMixin],

Expand All @@ -22,14 +23,31 @@ const KeyboardAwareListView = createReactClass({
},

render: function () {
const {
enableOnAndroid,
contentContainerStyle,
} = this.props

const {
keyboardSpace,
} = this.state

let newContentContainerStyle

if (Platform.OS === 'android' && enableOnAndroid) {
newContentContainerStyle = Object.assign({}, contentContainerStyle)
newContentContainerStyle.paddingBottom = (newContentContainerStyle.paddingBottom || 0) + keyboardSpace
}

return (
<ListView
ref='_rnkasv_keyboardView'
keyboardDismissMode='interactive'
contentInset={{bottom: this.state.keyboardSpace}}
contentInset={{bottom: keyboardSpace}}
showsVerticalScrollIndicator={true}
scrollEventThrottle={0}
{...this.props}
contentContainerStyle={newContentContainerStyle || contentContainerStyle}
onScroll={e => {
this.handleOnScroll(e)
this.props.onScroll && this.props.onScroll(e)
Expand Down
39 changes: 34 additions & 5 deletions lib/KeyboardAwareMixin.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* @flow */

import PropTypes from 'prop-types'
import ReactNative, { TextInput, Keyboard, UIManager } from 'react-native'
import ReactNative, { TextInput, Keyboard, UIManager, Platform } from 'react-native'
import TimerMixin from 'react-timer-mixin'

import type { Event } from 'react-native'
Expand Down Expand Up @@ -72,8 +72,28 @@ const KeyboardAwareMixin = {
if (isAncestor) {
// Check if the TextInput will be hidden by the keyboard
UIManager.measureInWindow(currentlyFocusedField, (x, y, width, height) => {
if (y + height > frames.endCoordinates.screenY - this.props.extraScrollHeight - this.props.extraHeight) {
this.scrollToFocusedInputWithNodeHandle(currentlyFocusedField)
const textInputBottomPosition = y + height
const keyboardPosition = frames.endCoordinates.screenY
const totalExtraHeight = this.props.extraScrollHeight + this.props.extraHeight

if (Platform.OS === 'ios') {
if (textInputBottomPosition > keyboardPosition - totalExtraHeight) {
this.scrollToFocusedInputWithNodeHandle(currentlyFocusedField)
}
} else {

// on android, the system would scroll the text input just above the keyboard
// so we just neet to scroll the extra height part
if (textInputBottomPosition > keyboardPosition) {
// since the system already scrolled the whole view up
// we should reduce that amount
keyboardSpace = keyboardSpace - (textInputBottomPosition - keyboardPosition)
this.setState({keyboardSpace})

this.scrollForExtraHeightOnAndroid(totalExtraHeight)
} else if (textInputBottomPosition > keyboardPosition - totalExtraHeight) {
this.scrollForExtraHeightOnAndroid(totalExtraHeight - (keyboardPosition - textInputBottomPosition))
}
}
})
}
Expand Down Expand Up @@ -108,8 +128,13 @@ const KeyboardAwareMixin = {

componentDidMount: function () {
// Keyboard events
this.keyboardWillShowEvent = Keyboard.addListener('keyboardWillShow', this.updateKeyboardSpace)
this.keyboardWillHideEvent = Keyboard.addListener('keyboardWillHide', this.resetKeyboardSpace)
if (Platform.OS === 'ios') {
this.keyboardWillShowEvent = Keyboard.addListener('keyboardWillShow', this.updateKeyboardSpace)
this.keyboardWillHideEvent = Keyboard.addListener('keyboardWillHide', this.resetKeyboardSpace)
} else if (Platform.OS === 'android' && this.props.enableOnAndroid) {
this.keyboardWillShowEvent = Keyboard.addListener('keyboardDidShow', this.updateKeyboardSpace)
this.keyboardWillHideEvent = Keyboard.addListener('keyboardDidHide', this.resetKeyboardSpace)
}
},

componentWillUnmount: function () {
Expand All @@ -131,6 +156,10 @@ const KeyboardAwareMixin = {
responder && responder.scrollResponderScrollToEnd({animated: animated})
},

scrollForExtraHeightOnAndroid(extraHeight: number) {
this.scrollToPosition(0, this.position.y + extraHeight, true)
},

/**
* @param keyboardOpeningTime: takes a different keyboardOpeningTime in consideration.
* @param extraHeight: takes an extra height in consideration.
Expand Down
22 changes: 20 additions & 2 deletions lib/KeyboardAwareScrollView.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import React from 'react'
import createReactClass from 'create-react-class'
import { ScrollView } from 'react-native'
import { ScrollView, Platform } from 'react-native'
import PropTypes from 'prop-types'
import KeyboardAwareMixin from './KeyboardAwareMixin'

Expand All @@ -14,6 +14,7 @@ const KeyboardAwareScrollView = createReactClass({
x: PropTypes.number,
y: PropTypes.number,
}),
enableOnAndroid: React.PropTypes.bool,
},
mixins: [KeyboardAwareMixin],

Expand All @@ -23,14 +24,31 @@ const KeyboardAwareScrollView = createReactClass({
},

render: function () {
const {
enableOnAndroid,
contentContainerStyle,
} = this.props

const {
keyboardSpace,
} = this.state

let newContentContainerStyle

if (Platform.OS === 'android' && enableOnAndroid) {
newContentContainerStyle = Object.assign({}, contentContainerStyle)
newContentContainerStyle.paddingBottom = (newContentContainerStyle.paddingBottom || 0) + keyboardSpace
}

return (
<ScrollView
ref='_rnkasv_keyboardView'
keyboardDismissMode='interactive'
contentInset={{bottom: this.state.keyboardSpace}}
contentInset={{bottom: keyboardSpace}}
showsVerticalScrollIndicator={true}
scrollEventThrottle={0}
{...this.props}
contentContainerStyle={newContentContainerStyle || contentContainerStyle}
onScroll={e => {
this.handleOnScroll(e)
this.props.onScroll && this.props.onScroll(e)
Expand Down