Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Android] ScrollView is missing initial scroll position for Android #6849

Closed
bcalik opened this issue Apr 6, 2016 · 82 comments
Closed

[Android] ScrollView is missing initial scroll position for Android #6849

bcalik opened this issue Apr 6, 2016 · 82 comments
Assignees
Labels
Platform: Android Android applications. Ran Commands One of our bots successfully processed a command. Resolution: Locked This issue was locked by the bot. Type: Enhancement A new feature or enhancement of an existing feature.

Comments

@bcalik
Copy link
Contributor

bcalik commented Apr 6, 2016

ScrollView has a contentOffset prop for iOS, which sets the initial scroll offset.
But it lacks on Android, so you can not set the initial scroll position.

For anyone who can make this, these are the attributes in Android's native ScrollView.

android:scrollX     The initial horizontal scroll offset, in pixels. 
android:scrollY     The initial vertical scroll offset, in pixels. 
@hufeng
Copy link

hufeng commented Apr 8, 2016

render() {
  return (
    <ScrollView ref={(view) => this._scrollView = view}>
    </ScrollView>
  );
}

this._scrollView.getScrollResponder().scrollTo({x: 0, y: 0, animated: true})

@bcalik
Copy link
Contributor Author

bcalik commented Apr 8, 2016

@hufeng That will cause flickering after render, first you will see the 0,0 position, then it will scroll.

Also if you are using a ListView, then you will have to render all the data until the scroll position you want, then you can scroll there.. Which will slow your app dramatically.
But in iOS, you can just set initialListSize={1} and it can just start rendering where-ever you want by setting contentOffset.

@mangogogos
Copy link
Contributor

https://productpains.com/post/react-native/content-offsetinset-for-scrollview

I started a product pains post for this a while back, but it hasn't gained any traction. While @hufeng's approach does the correct thing, it's very janky and even if you set animated: false, it will be noticeable to the end-user that the scrollview is jumping to its initial position. I've wanted to take a crack at a PR for this for a while but haven't had the bandwidth outside of work but for me this would be one of the most welcomed features for Android/iOS parity

@madox2
Copy link

madox2 commented Jun 10, 2016

When I try to call scrollTo from componentDidMount method it does not scroll. I have to use workaround with setTimeout to make it work:

componentDidMount() {
  setTimeout(() => this.refs._scrollView.scrollTo({ x: 100, y: 0 }) , 0);
}

@satya164
Copy link
Contributor

It'll be awesome if someone could send a pull request for it.

@bcalik
Copy link
Contributor Author

bcalik commented Jun 10, 2016

@madox2 Because the scroll position you want to scroll is not rendered yet, you have to wait for all rows to be rendered before you scroll to that position. So setTimeout is not a solution, which may not work.

iOS ListView does't have this issue, because it has a initial scroll position property, so it can start rendering from that position, not from zero.

@mkonicek
Copy link
Contributor

Hi there! This issue is being closed because it has been inactive for a while.

But don't worry, it will live on with ProductPains! Check out its new home: https://productpains.com/post/react-native/android-scrollview-is-missing-initial-scroll-position-for-android

Product Pains has been very useful in highlighting the top bugs and feature requests:
https://productpains.com/product/react-native?tab=top

Also, if this issue is a bug, please consider sending a pull request with a fix.

@arv
Copy link
Contributor

arv commented Nov 8, 2016

Was this fixed or just closed?

The productpain page links back to this issue.

@uqix
Copy link

uqix commented Nov 18, 2016

+1

@cherniv
Copy link

cherniv commented Nov 21, 2016

+1 , please

@aymericbouzy
Copy link

+1

@satya164
Copy link
Contributor

If someone is willing to send a PR, please do

@tangkunyin
Copy link

Insted of initial scroll position for android, you might look at using ViewPagerAndroid instead, and setting the initialPage property

@bcalik
Copy link
Contributor Author

bcalik commented Jan 4, 2017

@tangkunyin No. What if you need a to use it on an infinite scrolling page?

Think about the instagram app. Go to the explore tab, you will see the grids of posts, then click on an any post, the new page is an infinite scrolling listview, but started from an initial scroll position.

So because of this issue, you can't make an instagram clone for android using React Native.

@studiobrain
Copy link

studiobrain commented Feb 17, 2017

Has there been any further discussion on this issue? I would really love to set:
pagingEnabled={true} horizontal={true}
and have the ScrollView set the x position to the 2nd page initially without the flash of (0, 0). A workaround is fine if there is a solution...

@alvaromb
Copy link
Contributor

Using scrollTo in componentDidMount is working for us in a couple of apps.

@SudoPlz
Copy link
Contributor

SudoPlz commented Mar 14, 2017

@bcalik
Copy link
Contributor Author

bcalik commented Mar 14, 2017

@alvaromb It won't work when you try to scroll to position that is not rendered yet. initial scroll position lets your app to start rendering from your initial scroll position, not from zero. You you won't have to wait for all of the rows to be rendered, which is a massive performance impact.

@alvaromb
Copy link
Contributor

@bcalik yeah, I agree, it's not an ideal solution but for some cases it's working for us.

@cdimitroulas
Copy link

cdimitroulas commented Mar 20, 2017

has anybody found a solution for this?

I'm trying to start at the end of a horizontal ScrollView and animate towards the left once the component has rendered (making it obvious that this is a section which is scrollable)

@SudoPlz
Copy link
Contributor

SudoPlz commented Mar 20, 2017

@cdimitroulas I just did what @alvaromb suggested.
I use scrollTo in componentDidMount.
Δοκίμασέ το!

@cdimitroulas
Copy link

Thanks @SudoPlz but I tried this and it didn't seem to do anything at all.

  componentDidMount() {
    const _scrollView = this.scrollView;
    _scrollView.scrollTo({x: 100});
  }

my ScrollView looks something like this:

  <ScrollView horizontal={true}
          ref={scrollView => this.scrollView = scrollView}
          contentContainerStyle={style.profileKeyInfo}
          showsHorizontalScrollIndicator={false}
        >
      .......
  </ScrollView>

@cdimitroulas
Copy link

Nevermind, I got it to work by making sure the Navigator animation had finished before using scrollTo using the InteractionManager.runAfterInteractions() method

@SudoPlz
Copy link
Contributor

SudoPlz commented Mar 20, 2017

@cdimitroulas Oooh right, I had to put it inside a setTimeout to make it work.

Try

componentDidMount() {
	setTimeout(() => {
		this.scrollView.scrollTo({x: 100});
	}, 0);
}

Keep in mind that InteractionManager.runAfterInteractions() won't dispatch your function if there are no interactions taking place.

@aymericbouzy
Copy link

This SO question seems to give directions on how to implement this : http://stackoverflow.com/questions/22307239/how-to-set-the-starting-position-of-a-scrollview

@bcalik where did you find about these attributes?

I have no experience whatsoever in Android dev, but I really need this feature. Can anyone help me make a PR?

@hramos hramos removed the Bug Report label Feb 6, 2019
jackyzhen added a commit to movio/react-native-scrollable-tab-view that referenced this issue Mar 13, 2019
Because `contentOffset` on ScrollView is not implemented on android. See facebook/react-native#6849
jackyzhen added a commit to movio/react-native-scrollable-tab-view that referenced this issue Mar 13, 2019
Because `contentOffset` on ScrollView is not implemented on android. See facebook/react-native#6849
@grabbou
Copy link
Contributor

grabbou commented Mar 19, 2019

I am closing this issue because according to the comment by @shergin from the past, we have decided that setting offset by a prop is not optimal and method scrollTo should be used instead.

That said, we will not be adding support for the (to be deprecated) prop on iOS.

@grabbou grabbou closed this as completed Mar 19, 2019
@grabbou grabbou added Type: Enhancement A new feature or enhancement of an existing feature. and removed Type: Bug Report labels Mar 19, 2019
@AlexSugak
Copy link

@grabbou what do you mean by "not optimal"? Currently, it is using scrollTo which feels not optimal, as user sees initial position of the list before it is scrolled to the desired position. There is still no way for me to implement e.g. the instagram example from @bcalik using react native.

@mmazzarolo
Copy link

mmazzarolo commented Mar 20, 2019

@AlexSugak I think the issue is that is that the ScrollView (at least on Android) doesn't know its size until it has been laid out. This means you're forced to manually call scrollTo once the ScrollView onLayout has been invoked. @grabbou correct me if I'm wrong.
I know, it's not optimal, but I'm not sure how this issue could be really solved. Maybe by applying the scroll in the ScrollView onPreDraw?
In the meanwhile I'd suggest rendering the content of the ScrollView without making it visible (e.g.: by positioning it underneath your current screen) and show it to the user only once you have scrolled it to the desired position... this workaround has its drawbacks (e.g.: if the ScrollView content is huge it might take some time to be shown to the user) but at least the user won't see the "flashing" ScrollView.

@AlexSugak
Copy link

@mmazzarolo thanks, the "visibility" trick is interesting, will give it a try!

In my case I am trying to use FlatList to implement a swiper (image gallery). Here I know exactly the size of each element in the list (I am forced to set it for FlatList items anyway to be able to use paging). So I would expect for underlying android control to be able to render itself with initial offset (but I am no android expert).

@ghost
Copy link

ghost commented Mar 20, 2019

Guys, I solve this this by using onContentSizeChange prop:

<ScrollView
            ref={scrollView => this.scrollView = scrollView}
            onContentSizeChange={() => {
                this._onContentSizeChange();
            }}
        ></ScrollView>

And my method:

_onContentSizeChange() {
     let initialYScroll = 200;
       this.scrollView.scrollTo({x: 0, y: initialYScroll, animated: false});
    };

@mmazzarolo
Copy link

Guys, I solve this this by using onContentSizeChange prop

Yup, I think it's the same as using the onLayout 👍

@osdnk
Copy link
Contributor

osdnk commented Apr 26, 2019

Is it big deal to add it as style property? Then I’ll be able to animate it with native support with rn-reanimated.

@alirezamirsepassi
Copy link

These simple things just make me disappointed to use RN for my projects..

@tonmanayo
Copy link

This is still an issue, using scrollTo causes a flash which most people here don't want but rather want to just start at that possition

@jorrharris
Copy link

I can't believe this issue has been open for 3 years and there is still no better solution to the problem... For me, I am using scrollTo() in componentDidMount() with setTimeout at 0ms, but flickering still occurs once every 3 refreshes or so... Will there every be a solution for this? Because it seems like a pretty common feature that a lot of people need...

@mmazzarolo
Copy link

These simple things just make me disappointed to use RN for my projects..

This is still an issue, using scrollTo causes a flash which most people here don't want but rather want to just start at that possition

I can't believe this issue has been open for 3 years and there is still no better solution to the problem... For me, I am using scrollTo() in componentDidMount() with setTimeout at 0ms, but flickering still occurs once every 3 refreshes or so... Will there every be a solution for this? Because it seems like a pretty common feature that a lot of people need...

I would suggest you to give it a try to on of these solutions if you can and, if it works, send a PR or start a discussion.
As you can see, the problem is that in native Android there are no easy solutions to set the initial position (while on iOS it works out of the box).

@woodpav
Copy link

woodpav commented Jun 18, 2019

I have not read this issue but I have found that the initialScrollIndex of FlatList works well for some use cases. You need to implement getItemLayout however.

@msotnikov
Copy link

msotnikov commented Jul 22, 2019

Workaround for "blinking" - is use the overlay.

  constructor(props) {
    super(props);
    this.state = {
      overlayActive: true
    }
    this.scrollRef= React.createRef();
  }

I apply both of InteractionManager and setTimeout in componentDidMount. And then hide overlay

  componentDidMount() {
    const {offset} = this.props
    if (typeof offset === 'object') {
      InteractionManager.runAfterInteractions(() => {
        setTimeout(() => {
          this.scrollRef.current.scrollTo({...offset, animated: false})
          setTimeout(() => {
            this.setState({overlayActive: false})
          }, 1)
        }, 1)
      })
    }
  }
    return (
      <View>
        <ScrollView
          horizontal
          ref={this.scrollRef}
        >
          {renderArray}
        </ScrollView>

        { overlayActive &&
          <View style={[styles.overlay]} />
        }
      </View>
    )

Overlay stylesheet:

const styles = StyleSheet.create({
  overlay: {
    flex: 1,
    position: 'absolute',
    zIndex: 3,
    left: 0,
    top: 0,
    bottom: 0,
    right: 0,
    opacity: 1,
    backgroundColor: 'white',
  },
})

@caoweiju
Copy link

caoweiju commented Aug 7, 2019

i have tried many methonds,

  1. use scrollto after componentdidupdate,scrollto is called in settimeout or
    InteractionManager or requestanimationframe
  2. onlayout
  3. oncontentsizechange
  4. contentcontainerstyle set minheight to make sure scrollto can work

no one can fix the blinking or flash problem,it is a big problem。i am hurted so much。。。。

@cuongtora1996
Copy link

cuongtora1996 commented Aug 21, 2019

Guys, I solve this this by using onContentSizeChange prop:

this is the best

@alexsoul95
Copy link

such an important feature still not solved for andorid :( its almost 2020

@tapz
Copy link

tapz commented Jan 21, 2020

I really hate that React Native has these iOS only and Android only features. The team should implement the missing features to each platform to make the components behave identically.

@dsernst
Copy link

dsernst commented Feb 19, 2020

Kind of surprising that this is still not fixed upstream.

Based on the suggestions mentioned, I developed this drop-in ScrollViewOffset replacement component that adds contentOffset support for Android: https://github.com/dsernst/react-native-scrollview-offset/blob/master/ScrollViewOffset.tsx

It starts with opacity: 0, waits until after render to call scrollTo(props.contentOffset), then sets opacity: 1.

It also adds a new prop option — startAtEnd (boolean, default: false) — to set the initial scroll position to the end instead of needing to manually calculate it for contentOffset.

@malacca
Copy link
Contributor

malacca commented Mar 12, 2020

4 year have passed, blinking problem is still, so sad.....

@facebook facebook locked as resolved and limited conversation to collaborators Mar 20, 2020
@react-native-bot react-native-bot added the Resolution: Locked This issue was locked by the bot. label Mar 20, 2020
@JoshuaGross
Copy link
Contributor

Support for contentOffset has been added for Android in ed29ba1

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Platform: Android Android applications. Ran Commands One of our bots successfully processed a command. Resolution: Locked This issue was locked by the bot. Type: Enhancement A new feature or enhancement of an existing feature.
Projects
None yet
Development

No branches or pull requests