ネイティブアプリがウィンドウを自動スクロールするこのハックを見てきましたが、React Nativeでそれを行うには最善の方法を考えています... <TextInput>
フィールドがフォーカスを取得してビューの低い位置に配置されると、キーボードがテキストフィールドを覆います。
この問題は、UIExplorerのTextInputExample.js
ビューの例で確認できます。
誰かが良い解決策を持っていますか?
ネイティブアプリがウィンドウを自動スクロールするこのハックを見てきましたが、React Nativeでそれを行うには最善の方法を考えています... <TextInput>
フィールドがフォーカスを取得してビューの低い位置に配置されると、キーボードがテキストフィールドを覆います。
この問題は、UIExplorerのTextInputExample.js
ビューの例で確認できます。
誰かが良い解決策を持っていますか?
回答:
2017年の回答
KeyboardAvoidingView
おそらく今行くための最良の方法です。こちらのドキュメントをご覧ください。Keyboard
アニメーションを実行するためのより多くの制御を開発者に提供するモジュールと比較して、それは本当に簡単です。Spencer Carliは、彼のメディアブログですべての可能な方法を示しました。
2015年の回答
でこれを行う正しい方法react-native
は、外部ライブラリを必要とせず、ネイティブコードを利用し、アニメーションを含みます。
最初にonFocus
、それぞれTextInput
(またはスクロールしたい他のコンポーネント)のイベントを処理する関数を定義します。
// Scroll a component into view. Just pass the component ref string.
inputFocused (refName) {
setTimeout(() => {
let scrollResponder = this.refs.scrollView.getScrollResponder();
scrollResponder.scrollResponderScrollNativeHandleToKeyboard(
React.findNodeHandle(this.refs[refName]),
110, //additionalOffset
true
);
}, 50);
}
次に、レンダリング関数で:
render () {
return (
<ScrollView ref='scrollView'>
<TextInput ref='username'
onFocus={this.inputFocused.bind(this, 'username')}
</ScrollView>
)
}
これは、RCTDeviceEventEmitter
キーボードイベントとサイズ変更にを使用しRCTUIManager.measureLayout
、を使用してコンポーネントの位置を測定し、で必要な正確なスクロール移動を計算しscrollResponderInputMeasureAndScrollToKeyboard
ます。
あなたはで遊んすることをお勧めしますadditionalOffset
、あなたの特定のUIデザインのニーズに合わせて、パラメータ。
import {findNodeHandle} from 'react-native'
stackoverflow.com
DeviceEventEmitter.addListener('keyboardDidShow', this.keyboardDidShow.bind(this));
コード形式のいくつかのreact-native-keyboard-spacerと@Sherlockのコードを組み合わせて、TextInput要素を持つ任意のビューをラップできるKeyboardHandlerコンポーネントを作成しました。魅力的な作品!:-)
/**
* Handle resizing enclosed View and scrolling to input
* Usage:
* <KeyboardHandler ref='kh' offset={50}>
* <View>
* ...
* <TextInput ref='username'
* onFocus={()=>this.refs.kh.inputFocused(this,'username')}/>
* ...
* </View>
* </KeyboardHandler>
*
* offset is optional and defaults to 34
* Any other specified props will be passed on to ScrollView
*/
'use strict';
var React=require('react-native');
var {
ScrollView,
View,
DeviceEventEmitter,
}=React;
var myprops={
offset:34,
}
var KeyboardHandler=React.createClass({
propTypes:{
offset: React.PropTypes.number,
},
getDefaultProps(){
return myprops;
},
getInitialState(){
DeviceEventEmitter.addListener('keyboardDidShow',(frames)=>{
if (!frames.endCoordinates) return;
this.setState({keyboardSpace: frames.endCoordinates.height});
});
DeviceEventEmitter.addListener('keyboardWillHide',(frames)=>{
this.setState({keyboardSpace:0});
});
this.scrollviewProps={
automaticallyAdjustContentInsets:true,
scrollEventThrottle:200,
};
// pass on any props we don't own to ScrollView
Object.keys(this.props).filter((n)=>{return n!='children'})
.forEach((e)=>{if(!myprops[e])this.scrollviewProps[e]=this.props[e]});
return {
keyboardSpace:0,
};
},
render(){
return (
<ScrollView ref='scrollView' {...this.scrollviewProps}>
{this.props.children}
<View style={{height:this.state.keyboardSpace}}></View>
</ScrollView>
);
},
inputFocused(_this,refName){
setTimeout(()=>{
let scrollResponder=this.refs.scrollView.getScrollResponder();
scrollResponder.scrollResponderScrollNativeHandleToKeyboard(
React.findNodeHandle(_this.refs[refName]),
this.props.offset, //additionalOffset
true
);
}, 50);
}
}) // KeyboardHandler
module.exports=KeyboardHandler;
まず、react-native-keyboardeventsをインストールする必要があります。
次に、JavaScriptランドに戻ります。
react-native-keyboardeventsをインポートする必要があります。
var KeyboardEvents = require('react-native-keyboardevents');
var KeyboardEventEmitter = KeyboardEvents.Emitter;
次に、ビューでキーボードスペースの状態を追加し、キーボードイベントをリッスンして更新します。
getInitialState: function() {
KeyboardEventEmitter.on(KeyboardEvents.KeyboardDidShowEvent, (frames) => {
this.setState({keyboardSpace: frames.end.height});
});
KeyboardEventEmitter.on(KeyboardEvents.KeyboardWillHideEvent, (frames) => {
this.setState({keyboardSpace: 0});
});
return {
keyboardSpace: 0,
};
},
最後に、すべての下にあるレンダー関数にスペーサーを追加して、サイズが大きくなるとアイテムを押し上げます。
<View style={{height: this.state.keyboardSpace}}></View>
アニメーションAPIを使用することもできますが、簡単にするために、アニメーションの後で調整します。
this.listView.getScrollResponder().scrollTo(rowID * rowHeight);
あることがわかりました。これは、onFocusイベントを受け取ると、行のTextInputで呼び出されます。
react-native-keyboard-aware-scroll-viewは私のために問題を解決しました。 GitHubのreact-native-keyboard-aware-scroll-view
これを試して:
import React, {
DeviceEventEmitter,
Dimensions
} from 'react-native';
...
getInitialState: function() {
return {
visibleHeight: Dimensions.get('window').height
}
},
...
componentDidMount: function() {
let self = this;
DeviceEventEmitter.addListener('keyboardWillShow', function(e: Event) {
self.keyboardWillShow(e);
});
DeviceEventEmitter.addListener('keyboardWillHide', function(e: Event) {
self.keyboardWillHide(e);
});
}
...
keyboardWillShow (e) {
let newSize = Dimensions.get('window').height - e.endCoordinates.height;
this.setState({visibleHeight: newSize});
},
keyboardWillHide (e) {
this.setState({visibleHeight: Dimensions.get('window').height});
},
...
render: function() {
return (<View style={{height: this.state.visibleHeight}}>your view code here...</View>);
}
...
それは私のために働いた。ビューは基本的に、キーボードが表示されると縮小し、非表示になると元に戻ります。
言及したかったが、今はKeyboardAvoidingView
RNにある。インポートして、RNの他のモジュールと同じように使用してください。
RNのコミットへのリンクは次のとおりです。
https://github.com/facebook/react-native/commit/8b78846a9501ef9c5ce9d1e18ee104bfae76af2e
0.29.0から利用可能
UIExplorerの例も含まれています。
多分遅くなるかもしれませんが、最善の解決策は、ネイティブライブラリIQKeyboardManagerを使用することです
IQKeyboardManagerディレクトリをデモプロジェクトからiOSプロジェクトにドラッグアンドドロップするだけです。それでおしまい。また、isToolbarを有効にしたり、AppDelegate.mファイルでテキスト入力とキーボードの間のスペースを設定したりすることもできます。カスタマイズの詳細については、追加したGitHubページのリンクを参照してください。
TextInput.onFocusとScrollView.scrollToを使用しました。
...
<ScrollView ref="scrollView">
...
<TextInput onFocus={this.scrolldown}>
...
scrolldown: function(){
this.refs.scrollView.scrollTo(width*2/3);
},
@Stephen
キーボードが表示されるのとまったく同じ速度で高さをアニメーション化しなくても構わない場合は、LayoutAnimationを使用して、少なくとも高さが所定の位置にジャンプしないようにすることができます。例えば
react-nativeからLayoutAnimationをインポートし、次のメソッドをコンポーネントに追加します。
getInitialState: function() {
return {keyboardSpace: 0};
},
updateKeyboardSpace: function(frames) {
LayoutAnimation.configureNext(animations.layout.spring);
this.setState({keyboardSpace: frames.end.height});
},
resetKeyboardSpace: function() {
LayoutAnimation.configureNext(animations.layout.spring);
this.setState({keyboardSpace: 0});
},
componentDidMount: function() {
KeyboardEventEmitter.on(KeyboardEvents.KeyboardDidShowEvent, this.updateKeyboardSpace);
KeyboardEventEmitter.on(KeyboardEvents.KeyboardWillHideEvent, this.resetKeyboardSpace);
},
componentWillUnmount: function() {
KeyboardEventEmitter.off(KeyboardEvents.KeyboardDidShowEvent, this.updateKeyboardSpace);
KeyboardEventEmitter.off(KeyboardEvents.KeyboardWillHideEvent, this.resetKeyboardSpace);
},
いくつかのアニメーション例は(私は上の春のものを使用しています):
var animations = {
layout: {
spring: {
duration: 400,
create: {
duration: 300,
type: LayoutAnimation.Types.easeInEaseOut,
property: LayoutAnimation.Properties.opacity,
},
update: {
type: LayoutAnimation.Types.spring,
springDamping: 400,
},
},
easeInEaseOut: {
duration: 400,
create: {
type: LayoutAnimation.Types.easeInEaseOut,
property: LayoutAnimation.Properties.scaleXY,
},
update: {
type: LayoutAnimation.Types.easeInEaseOut,
},
},
},
};
更新:
以下の@sherlockの回答を参照してください。react-native0.11以降、キーボードのサイズ変更は組み込みの機能を使用して解決できます。
いくつかのメソッドを組み合わせて、もう少し単純なものにすることができます。
入力にonFocusリスナーをアタッチします
<TextInput ref="password" secureTextEntry={true}
onFocus={this.scrolldown.bind(this,'password')}
/>
スクロールダウンメソッドは次のようになります。
scrolldown(ref) {
const self = this;
this.refs[ref].measure((ox, oy, width, height, px, py) => {
self.refs.scrollView.scrollTo({y: oy - 200});
});
}
これは、スクロールビュー(参照を追加することを忘れないでください)に、フォーカスされた入力の位置-200(キーボードのサイズとほぼ同じ)まで下にスクロールするように指示します。
componentWillMount() {
this.keyboardDidHideListener = Keyboard.addListener(
'keyboardWillHide',
this.keyboardDidHide.bind(this)
)
}
componentWillUnmount() {
this.keyboardDidHideListener.remove()
}
keyboardDidHide(e) {
this.refs.scrollView.scrollTo({y: 0});
}
ここで、スクロールビューを上部にリセットします。
より単純な方法を使用していますが、まだアニメーション化されていません。「bumpedUp」というコンポーネントの状態があり、デフォルトでは0ですが、textInputがフォーカスを取得すると、次のように1に設定されます。
私のtextInputで:
onFocus={() => this.setState({bumpedUp: 1})}
onEndEditing={() => this.setState({bumpedUp: 0})}
次のように、画面上のすべてのラッピングコンテナーに下マージンと負の上マージンを与えるスタイルもあります。
mythingscontainer: {
flex: 1,
justifyContent: "center",
alignItems: "center",
flexDirection: "column",
},
bumpedcontainer: {
marginBottom: 210,
marginTop: -210,
},
そして、ラッピングコンテナーで、次のようにスタイルを設定します。
<View style={[styles.mythingscontainer, this.state.bumpedUp && styles.bumpedcontainer]}>
したがって、「bumpedUp」状態が1に設定されると、bumpedcontainerスタイルが開始され、コンテンツが上に移動します。
ちょっとハッキーでマージンはハードコードされていますが、動作します:)
私はbrysgo回答を使用して、スクロールビューの下部を上げます。次に、onScrollを使用して、スクロールビューの現在の位置を更新します。次に、このReact Native:要素の位置を取得して、textinputの位置を取得しました。次に、入力が現在のビューにあるかどうかを判断するために簡単な計算を行います。次に、scrollToを使用して、最小量とマージンを移動します。それはかなり滑らかです。スクロール部分のコードを次に示します。
focusOn: function(target) {
return () => {
var handle = React.findNodeHandle(this.refs[target]);
UIManager.measureLayoutRelativeToParent( handle,
(e) => {console.error(e)},
(x,y,w,h) => {
var offs = this.scrollPosition + 250;
var subHeaderHeight = (Sizes.width > 320) ? Sizes.height * 0.067 : Sizes.height * 0.077;
var headerHeight = Sizes.height / 9;
var largeSpace = (Sizes.height - (subHeaderHeight + headerHeight));
var shortSpace = largeSpace - this.keyboardOffset;
if(y+h >= this.scrollPosition + shortSpace) {
this.refs.sv.scrollTo(y+h - shortSpace + 20);
}
if(y < this.scrollPosition) this.refs.sv.scrollTo(this.scrollPosition - (this.scrollPosition-y) - 20 );
}
);
};
},
私もこの質問に答えます。最後に、次のように各シーンの高さを定義することで解決します。
<Navigator
...
sceneStyle={{height: **}}
/>
また、サードパーティのモジュールhttps://github.com/jaysoo/react-native-extra-dimensions-androidを使用して実際の高さを取得しています。