Synchronize fragment scroll in ViewPager

android, android-fragments, android-viewpager, scrollview

Solution

UPDATE:

I've just released this solution (SynchronizedScrollView) on gradle/maven! It also has a couple of fix and differences from the last posted, so check the code anyway!

Feel free to use it and contribute! Thanks. :)

Ok, I have changed all the structure of the app and also fix this part, it was easier than expected.

Just tested in a few minutes, so maybe there are problems with this but seems to work pretty good. I basically made a class that will keep the responsability to synchronize all the ScrollViews, and the ScrollViews simply notify this Synchronizer when they're scrolled.

public class Synchronizer {

    private static Synchronizer instance;

    private List<Synchronizable> synchronizableList;

    private Synchronizer() {}

    public static Synchronizer getInstance() {
        if (instance == null)
            instance = new Synchronizer();
        return instance;
    }

    public void registerSynchronizable(Synchronizable synchronizable) {
        if(synchronizableList == null)
            synchronizableList = new ArrayList<>();

        synchronizableList.add(synchronizable);
    }

    public void update(Synchronizable sender, int update) {
        if(update < 0) update = 0;

        for(Synchronizable s : synchronizableList) {
            if(s != sender)
                s.onUpdate(update);
        }
    }

    public interface Synchronizable {
        public void onUpdate(int update);
    }
}

and the ScrollView will implement the `Synchronizable` interface, and they will "register" at the creation

public class MyScrollView extends ScrollView implements Synchronizer.Synchronizable {

    public MyScrollView(Context context) {
        super(context);
        init();
    }

    public MyScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public MyScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }   

    private void init() {
        // init your ScrollView with style and stuff and then register it

        Synchronizer.getInstance().registerSynchronizable(this);
    }

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);

        // listen for "your" scrolls and notify the Synchronizer
        Synchronizer.getInstance().update(this, getScrollY());
    }

    @Override
    public void onUpdate(int update) {
        // listen for the scrolls of your "siblings" from the Synchronizer
        setScrollY(update);
    }
}

Hope this will help!

Problem

I'm trying to replicate something like the Yahoo! Weather app, but I'm quite lost on how to sync all the scrollviews on the same height in the ViewPager. I'm using a ScrollListener inside the Fragments to set the opacity of the background and to update the other fragments ``` final ViewTreeObserver.OnScrollChangedListener onScrollChangedListener = new ViewTreeObserver.OnScrollChangedListener() { @Override public void onScrollChanged() { if(scrollView.getScrollY() > 0 && scrollView.getScrollY() < 1200) { overlay.getBackground().setAlpha((scrollView.getScrollY()/4)); if(isVisible()) { Activity parent = getActivity(); if(parent != null && parent instanceof MainActivity) { ((MainActivity)parent).updateHeight(scrollView.getScrollY()); } } } } }; ``` and inside the MainActivity I'm doing something like this: ``` public void updateHeight(int scroll) { for(int i = 0; i<mPager.getChildCount(); i++) { View v = mPager.getChildAt(i); ScrollView scrollView = (ScrollView) v.findViewById(R.id.frag_scrollview); scrollView.setScrollY(scroll); } } ``` This is "almost" working, but not really. I've tried to modify the code to avoid the update of the current visible page, but with no success: ``` if(i != mPager.getCurrentItem()) { View v = mPager.getChildAt(i); ScrollView scrollView = (ScrollView) v.findViewById(R.id.frag_scrollview); scrollView.setScrollY(scroll); } ``` PS: I'm also quite unsure on the correct pattern in order to get updates on the ScrollView. The OnScrollChangedListener seems to work but it's called LOTS of times, so I'm not sure if it's the correct approach. Thanks in advance.

Original source