問題
各スワイプで常に 1 つの画面全体 (つまり、1 つの「ページ」) をスクロールする水平スクロール可能なリストを開発しています。それが最初のページにあり、ユーザーが再び右に引っ張ると、リスト全体を更新する必要があります。
垂直方向については、ビューが一番上の位置にあるときにユーザーがプルダウンするとScrollView
、標準が表示されます。RefreshControl
ただし、水平バージョンの場合、標準的なソリューションはありません。
私の実験
アイデア
PanResponder
感動的で感動的なイベントをキャプチャするために を追加しようとしました。VirtualizedList
アプリケーションでa を使用していることに注意してください。基本的なロジックは次のとおりです。
- を含む非表示
View
(デフォルトで幅が 0 に設定されている) を作成しActivityIndicator
、このビューを として挿入しますListHeaderComponent
。VirtualizedList
VirtualizedList
のイベントをリッスンしonScroll
、最新のスクロール位置を保存し続けます- スクロール位置がほぼ 0 で、ユーザーが右に引っ張ると (イベントが を介してジェスチャをキャプチャ
PanResponder
)、非表示の View の幅を ActivityIndicator でアニメーション化して、RefreshControl の動作をシミュレートします。
結果
ただし、PanResponder
は目的のイベントを適切にキャプチャしません。VirtualizedList
ほとんどの場合、 (Android デバイスで)開始時に右に引っ張ると、標準のヒット エッジ アニメーションのみが表示されます (グラデーション ブルー レイヤーが表示されます)。
onPanResponderGrant
いくつかのログを追加することで、が時々トリガーされる可能性があることがわかりましたがonPanResponderMove
、 、onPanResponderRelease
、onPanResponderTerminate
またはのいずれも起動されませんonPanResponderTerminationRequest
。
がonPanResponderGrant
トリガーされると、非表示が部分的に表示ActivityIndicator
されることがあります。「右に引っ張る」効果は短時間しか現れず、隠れたビューの非常に狭い部分だけが表示され、常に即座に停止します。
基になっているものScrollView
がイベントを盗み、コードが応答しないようにしていると思います。しかしonPanResponderTerminationRequest
、常に戻るように設定しても、false
まだ役に立ちません。を aに直接代入VirtualizedList
してScrollView
も、同じ結果が得られます。
ScrollView のスクロールが一番上にあるときに、タッチと移動のイベントを適切にキャプチャする方法を知っている人はいますか? または、の水平バージョンを実装する別の実行可能な方法はありRefreshControl
ますか?
実験コード
import React from 'react'
import {
Text, View, ActivityIndicator, ScrollView, VirtualizedList,
Dimensions, PanResponder, Animated, Easing
} from 'react-native'
let DATA = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
class Test extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
refreshIndicatorWidth: new Animated.Value(0),
};
this.onScroll = evt => this._scrollPos = evt.nativeEvent.contentOffset.x;
this.onPullToRefresh = dist => Animated.timing(this.state.refreshIndicatorWidth, {
toValue: dist,
duration: 0,
easing: Easing.linear,
}).start();
this.resetRefreshStatus = () => {
Animated.timing(this.state.refreshIndicatorWidth, {
toValue: 0,
}).start();
};
const shouldRespondPan = ({dx, dy}) => (this._scrollPos||0) < 50 && dx > 10 && Math.abs(dx) > Math.abs(dy);
this._panResponder = PanResponder.create({
onMoveShouldSetPanResponder: (evt, gestureState) => {
return shouldRespondPan(gestureState)
},
onMoveShouldSetPanResponderCapture: (evt, gestureState) => {
return shouldRespondPan(gestureState)
},
onPanResponderGrant: (evt, gestureState) => {
console.warn('----pull-Grant: ' + JSON.stringify(gestureState));
},
onPanResponderMove: (evt, gestureState) => {
console.warn('----pull-Move: ' + JSON.stringify(gestureState));
this.onPullToRefresh(gestureState.dx)
},
onPanResponderTerminationRequest: () => {
console.warn('----pull-TerminationRequest: ' + JSON.stringify(gestureState));
return false
},
onPanResponderRelease: (evt, gestureState) => {
console.warn('----pull-Release: ' + JSON.stringify(gestureState));
this.props.refreshMoodList();
},
onPanResponderTerminate: () => {
console.warn('----pull-Terminate: ' + JSON.stringify(gestureState));
this.resetRefreshStatus();
},
});
this.renderRow = this.renderRow.bind(this);
}
componentDidUpdate(prevProps, prevState, prevContext) {
console.warn('----updated: ' + JSON.stringify(this.props));
this.resetRefreshStatus();
}
renderRow({item, index}) {
return (
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center', width: Dimensions.get('window').width, height: '100%'}}>
<Text>{item}</Text>
</View>
);
}
render() {
/******* Test with a VirtualizedList *******/
return (
<VirtualizedList
{...this._panResponder.panHandlers}
data={DATA}
ListEmptyComponent={<View/>}
getItem={getItem}
getItemCount={getItemCount}
keyExtractor={keyExtractor}
renderItem={this.renderRow}
horizontal={true}
pagingEnabled={true}
showsHorizontalScrollIndicator={false}
ListHeaderComponent={(
<Animated.View style={{flex: 1, height: '100%', width: this.state.refreshIndicatorWidth, justifyContent: 'center', alignItems: 'center'}}>
<ActivityIndicator size="large"/>
</Animated.View>
)}
onScroll={this.onScroll}
/>
);
/******* Test with a ScrollView *******/
return (
<View style={{height:Dimensions.get('window').height}}>
<ScrollView
{...this._panResponder.panHandlers}
horizontal={true}
>
<View style={{width: Dimensions.get('window').width * 2, height: 100, backgroundColor: 'green'}}>
<Text>123</Text>
</View>
</ScrollView>
</View>
);
}
}
const getItem = (data, index) => data[index];
const getItemCount = data => data.length;
const keyExtractor = (item, index) => 'R' + index;
export default Test;
一時的な回避策 (完璧ではない)
内部で直接更新イベント (およびアニメーション) を発生させます
onPanResponderGrant
。この方法では、まだタッチの動きを追跡できません。そのため、スクロール ペインはタッチ ポイントをたどることができません。追加の条件で操作を移動し
onPanResponderMove
ますonMoveShouldSetPanResponder
(たとえば、独自の「許可」条件判定を追加する): この方法では、タッチの動きを追跡できますが、実際の「許可」を取得していないため、キャプチャできません。 //イベント (つまり、いつ touch イベントが終了する*Release
かわかりません。したがって、実際にいつ更新イベントを開始するかはわかりません!*Terminate
)*End
完璧なソリューション
あなたのアイデアを待っています...