我试图像这个应用程序一样构建一个粘性组件
http://www.screencapture.ru/file/E88F08Fc
交易,产品,事件选项卡/ segmentControl实际上是从底部开始,当您点击标题的底部时滚动它停止并在内容保持滚动时开始粘贴
这是我的代码
<View style={styles.container}>
<ScrollView
style={styles.container}
scrollEventThrottle={16}
onScroll={
Animated.event(
[{nativeEvent:{contentOffset: {y: this.state.scrollY}}}]
)
}
>
{this._renderScrollViewContent()}
</ScrollView>
<View style={styles.stickyStuff}></View>
<Animated.View
style={[styles.header, {height: headerHeight}]}
>
<Animated.Image
source={require('../../assets/images/pvj.jpg')}
style={[styles.backgroundImage, {opacity: imageOpacity}]}
/>
<Animated.View style={[styles.bar, {transform: [{translateY: titleTranslateY}, {translateX: titleTranslateX}]}]}>
<Text style={styles.title}>PARIS VAN JAVA</Text>
</Animated.View>
</Animated.View>
</View>
答案 0 :(得分:35)
使用ScrollView组件非常简单。已经有一些名为&#34; stickyHeaderIndices&#34;它使得孩子的索引变得粘滞。在下面的代码中,当你滚动时,renderComponent2里面的内容会保持粘滞状态。
<ScrollView
stickyHeaderIndices={[1]}
showsVerticalScrollIndicator={false}
>
{this.renderComponent1()}
{this.renderComponent2()}
{this.renderComponent3()}
</ScrollView>
参考:https://facebook.github.io/react-native/docs/scrollview.html#stickyheaderindices
答案 1 :(得分:9)
嘿伙计们感谢你回答我的第一个问题(是的!),我发现它现在是怎么做的...所以基本上我做的是我欺骗F8App代码,如果它不是F8Scrolling原生模块可以使用
if (!NativeModules.F8Scrolling) {
var distance = EMPTY_CELL_HEIGHT - this.state.stickyHeaderHeight;
var translateY = 0; this.state.anim.interpolate({
inputRange: [0, distance],
outputRange: [distance, 0],
extrapolateRight: 'clamp',
});
transform = [{translateY}];
}
给出了动画我的粘性视图的想法,所以这里是我最终
const stickySegmentControlX = this.state.scrollY.interpolate({
inputRange: [0, STICKY_SCROLL_DISTANCE],
outputRange: [INIT_STICKY_HEADER, HEADER_MIN_HEIGHT],
extrapolate: 'clamp'
})
...
return(
....
<Animated.View ref="stickyHeader" style={[styles.stickyStuff, {top: stickySegmentControlX}]}>
<Text>Go Stick</Text>
</Animated.View>
....
);
所以基本上我所做的是动画{position:'absolute'}视图的顶部位置,而输出范围的值在底部位置到我的标题高度之间(所以它会停止在我的标题下方,输入范围介于0和顶部位置与标题高度之间的差异...最终动画在滚动滚动视图时会感觉自然......
你去了一个粘性标题自定义视图......这是结果
如果你觉得我的答案令人困惑,那你最好去janic duplessis中文帖子:
React Native ScrollView animated header
答案 2 :(得分:3)
这很简单,您只需要知道在滚动视图组件内滚动时希望它粘住的索引组件。下面是一个例子:
<ScrollView
style={styles.screen}
stickyHeaderIndices={[0]}
>
<View><Text>Hello1</Text></View>
<View><Text>Hello2</Text></View>
<View><Text>Hello3</Text></View>
</ScrollView>
所以当滚动Hello1文本会粘在ScrollView的顶部
祝你好运
答案 3 :(得分:0)
您可以将所有内容放在ScrollView
中,将粘性标题放在那里,然后在主视图中使用第二个ScrollView
来获取其余内容。像这样:
<View style={styles.container}>
<ScrollView style={styles.mainScrollView}>
<View style={styles.header}>
// Header content here
</View>
<ScrollView style={styles.scrollableContent}>
// Scrollable content here
</ScrollView>
</ScrollView>
</View>
为第二个height
设置ScrollView
,使其仅占用可用空间的一部分,这样标题将在顶部保持可见。
答案 4 :(得分:0)
我找到了这个视差滚动视图模块。它做了那种行为。
答案 5 :(得分:0)
这可以证明是有用的
import android.os.Build
import android.util.Log
import android.view.View
import android.view.ViewGroup
import android.widget.ScrollView
import androidx.annotation.RequiresApi
import kotlinx.coroutines.handleCoroutineException
@RequiresApi(Build.VERSION_CODES.M)
class StickyViewHistory(val stickyViewInflateListener: StickyViewInflateListener, val scrollView: ScrollView) {
interface StickyViewInflateListener {
fun inflate(view : View)
fun getHeaderViews() : List<View>
fun removeSticky()
fun getStickyHolderView() : View
fun getStickyLayout() : View
}
var currentVisibleHeader : ViewOverLapState? = null
private var isStickyVisible = false
init {
scrollView.setOnScrollChangeListener { view: View, i: Int, newScrollY: Int, i2: Int, oldScrollY: Int ->
val overlapStates = getOverlappStates()
val numberOfHeadersAboveScroll = overlapStates.filter { it == ViewOverLapState.IS_ABOVE }.count()
//If all of the headers are either visible or below the screen, we remove the sticky
if(numberOfHeadersAboveScroll == 0){
if(isStickyVisible == true){
removeSticky()
}
}
else {
if(newScrollY < oldScrollY){
displayHeaderWhenScrollingDown(overlapStates)
}
else {
displayHeaderWhileScrollingUp(overlapStates)
}
}
}
}
/**
* Assumes there is atleast one header view which has scrolled above screen
*/
fun displayHeaderWhileScrollingUp(overlapStates : List<ViewOverLapState>){
val mostRecentAboveHeader = getMostRecentAboveHeader(overlapStates)
val highestVisibleHeader = getHighestVisibleHeader(overlapStates)
//If there is a header on screen which is just about to overlap the current sticky layout we remove the Sticky altogether
highestVisibleHeader?.let {
if(isOverlappingCurrentStickyLayout(it.view)){
removeSticky()
return
}
}
// If the most recent header that has scrolled above screen is the current sticky,
// we dont do anything since its already being displayed as sticky
if(currentVisibleHeader != null && currentVisibleHeader!!.view == mostRecentAboveHeader.view){
//Do nothing
}
else {
//We sticky the most recent header which has scrolled above screen
setSticky(mostRecentAboveHeader)
}
isStickyVisible = true
}
fun displayHeaderWhenScrollingDown(overlapStates : List<ViewOverLapState>){
currentVisibleHeader?.let { cvh ->
val highestVisibleHeader = getHighestVisibleHeader(overlapStates)
highestVisibleHeader?.let { hvh ->
if(isOverlappingCurrentStickyLayout(hvh.view)){
removeSticky()
return
}
}
}
if(currentVisibleHeader == null){
val mostRecentAboveHeader = getMostRecentAboveHeader(overlapStates)
setSticky(mostRecentAboveHeader)
}
}
/**
* Checks if a view is overlapping with any part of the current sticky layout
*/
fun isOverlappingCurrentStickyLayout(childView: View) : Boolean{
val locationOfSticky = IntArray(2)
stickyViewInflateListener.getStickyLayout().getLocationOnScreen(locationOfSticky)
val childLocation = IntArray(2)
childView.getLocationOnScreen(childLocation)
val locationOfScrollView = IntArray(2)
scrollView.getLocationOnScreen(locationOfScrollView)
if(childLocation[1] <= locationOfSticky[1]+stickyViewInflateListener.getStickyLayout().height &&
childLocation[1] >= locationOfScrollView[1]){
return true
}
return false
}
/**
* Get the headerview which most recently scrolled above screen
*/
fun getMostRecentAboveHeader(overlapStates : List<ViewOverLapState>) : ViewOverLapState {
val aboveViews = overlapStates.filter { it == ViewOverLapState.IS_ABOVE }
var lowestAboveView : ViewOverLapState? = null
var highestYCooridnatesoFar = Int.MIN_VALUE
aboveViews.forEach {
val locationOnScreen = IntArray(2)
it.view.getLocationOnScreen(locationOnScreen)
if(locationOnScreen[1] > highestYCooridnatesoFar){
lowestAboveView = it
highestYCooridnatesoFar = locationOnScreen[1]
}
}
return lowestAboveView!!
}
/**
* Highest here does not mean the highest [y] value but its position on the screen, so lower y means higher
*/
fun getHighestVisibleHeader(overlapStates: List<ViewOverLapState>) : ViewOverLapState? {
val insideViews = overlapStates.filter { it == ViewOverLapState.IS_INSIDE }
var highestInsideView : ViewOverLapState? = null
var lowestYCordinatesoFar = Int.MAX_VALUE
insideViews.forEach {
val locationOnScreen = IntArray(2)
it.view.getLocationOnScreen(locationOnScreen)
if(locationOnScreen[1] < lowestYCordinatesoFar){
highestInsideView = it
lowestYCordinatesoFar = locationOnScreen[1]
}
}
return highestInsideView
}
fun getOverlappStates() : List<ViewOverLapState>{
return stickyViewInflateListener.getHeaderViews()
.map {
getOverlapState(scrollView,it)
}
}
/**
* For all of the view headers, get their overlap state with the scroll view
*/
fun getOverlapState(parentView : View, childView : View) : ViewOverLapState{
val scrollViewLocation = IntArray(2)
parentView.getLocationOnScreen(scrollViewLocation)
val childLocation = IntArray(2)
childView.getLocationOnScreen(childLocation)
if(childLocation[1] < scrollViewLocation[1]){
return ViewOverLapState.IS_ABOVE.apply { view = childView }
}
else if(childLocation[1] > (scrollViewLocation[1]+parentView.height)){
return ViewOverLapState.IS_BELOW.apply { view =childView }
}
else {
return ViewOverLapState.IS_INSIDE.apply { view = childView }
}
}
fun setSticky(viewOverLapState: ViewOverLapState){
stickyViewInflateListener.inflate(viewOverLapState.view)
currentVisibleHeader = viewOverLapState
isStickyVisible = true
}
fun removeSticky(){
currentVisibleHeader = null
stickyViewInflateListener.removeSticky()
isStickyVisible = false
}
enum class ViewOverLapState(val text : String) {
IS_INSIDE("inside"),IS_ABOVE("above"),IS_BELOW("below");
lateinit var view : View
}
}