package com.florianmski.tracktoid.utils;
import android.support.v4.app.Fragment;
import android.support.v4.util.SparseArrayCompat;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.AbsListView;
import android.widget.GridView;
import android.widget.ScrollView;
import com.florianmski.tracktoid.ui.fragments.base.list.ScrollListenerProvider;
import com.florianmski.tracktoid.ui.widgets.NotifyingScrollView;
import com.florianmski.tracktoid.ui.widgets.NotifyingWebView;
import java.util.ArrayList;
import java.util.List;
public class ScrollHeaderHelper
{
private View header;
private int heightToKeep = 0;
private List<OnHeaderListener> listeners = new ArrayList<OnHeaderListener>();
private boolean headerLayout = false;
private float latestTranslationY = 0;
private int activeScrollView = -1;
public ScrollHeaderHelper(View header)
{
this.header = header;
header.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener()
{
@Override
public void onGlobalLayout()
{
ScrollHeaderHelper.this.header.getViewTreeObserver().removeGlobalOnLayoutListener(this);
headerLayout = true;
dispatchOnHeaderLayoutEvent();
}
});
}
private void translateHeader(int key, View v, int top)
{
float translationY = Math.max(heightToKeep - v.getPaddingTop(), top - v.getPaddingTop());
header.setTranslationY(translationY);
dispatchOnHeaderTranslateEvent(key, translationY);
latestTranslationY = translationY;
}
public void setHeightToKeep(int heightToKeep)
{
this.heightToKeep = heightToKeep;
}
public int getHeightToKeep()
{
return heightToKeep;
}
public View getHeader()
{
return header;
}
public void addHeaderListener(OnHeaderListener listener)
{
listeners.add(listener);
if(headerLayout)
listener.onHeaderLayout();
}
public void removeHeaderListener(OnHeaderListener listener)
{
listeners.remove(listener);
}
private void dispatchOnHeaderLayoutEvent()
{
for(OnHeaderListener listener : listeners)
listener.onHeaderLayout();
}
private void dispatchOnHeaderTranslateEvent(int key, float translationY)
{
for(OnHeaderListener listener : listeners)
listener.onHeaderTranslate(key, translationY);
}
private SparseArrayCompat<ScrollViewContainer<?, ?>> map = new SparseArrayCompat<ScrollViewContainer<?, ?>>();
private void addScrollableView(int key, NotifyingScrollView scrollView, ScrollListenerProvider<NotifyingScrollView.OnScrollChangedListener> scrollListenerProvider)
{
map.put(key, new NotifyingScrollViewContainer(key, scrollView, scrollListenerProvider));
}
private void addScrollableView(int key, NotifyingWebView scrollView, ScrollListenerProvider<NotifyingWebView.OnScrollChangedListener> scrollListenerProvider)
{
map.put(key, new NotifyingWebViewContainer(key, scrollView, scrollListenerProvider));
}
private void addScrollableView(int key, AbsListView scrollView, ScrollListenerProvider<AbsListView.OnScrollListener> scrollListenerProvider)
{
map.put(key, new AbsListViewContainer(key, scrollView, scrollListenerProvider));
}
private void addScrollableView(int key, RecyclerView recyclerView, ScrollListenerProvider<RecyclerView.OnScrollListener> scrollListenerProvider)
{
map.put(key, new RecyclerViewContainer(key, recyclerView, scrollListenerProvider));
}
private void removeScrollableView(int key)
{
ScrollViewContainer<?, ?> container = map.get(key);
if(container != null)
{
container.clean();
map.remove(key);
}
}
public static void addScrollableView(Fragment f, int key, NotifyingScrollView scrollView, ScrollListenerProvider<NotifyingScrollView.OnScrollChangedListener> scrollListenerProvider)
{
ScrollHeaderHelper shh = from(f);
if(shh != null)
shh.addScrollableView(key, scrollView, scrollListenerProvider);
}
public static void addScrollableView(Fragment f, int key, NotifyingWebView scrollView, ScrollListenerProvider<NotifyingWebView.OnScrollChangedListener> scrollListenerProvider)
{
ScrollHeaderHelper shh = from(f);
if(shh != null)
shh.addScrollableView(key, scrollView, scrollListenerProvider);
}
public static void addScrollableView(Fragment f, int key, AbsListView scrollView, ScrollListenerProvider<AbsListView.OnScrollListener> scrollListenerProvider)
{
ScrollHeaderHelper shh = from(f);
if(shh != null)
shh.addScrollableView(key, scrollView, scrollListenerProvider);
}
public static void addScrollableView(Fragment f, int key, RecyclerView scrollView, ScrollListenerProvider<RecyclerView.OnScrollListener> scrollListenerProvider)
{
ScrollHeaderHelper shh = from(f);
if(shh != null)
shh.addScrollableView(key, scrollView, scrollListenerProvider);
}
public static void removeScrollableView(Fragment f, int key)
{
ScrollHeaderHelper shh = from(f);
if(shh != null)
shh.removeScrollableView(key);
}
public static ScrollHeaderHelper from(Fragment f)
{
Fragment parentFragment = f.getParentFragment();
if(parentFragment != null && parentFragment instanceof ScrollHeaderHelper.Provider)
return ((ScrollHeaderHelper.Provider)parentFragment).getScrollHeaderHelper();
return null;
}
public void setActiveScrollView(int key)
{
this.activeScrollView = key;
}
public abstract class ScrollViewContainer<T extends ViewGroup, S> implements OnHeaderListener
{
protected int key;
protected T scrollView;
protected ScrollListenerProvider<S> scrollListenerProvider;
public ScrollViewContainer(int key, T scrollView, ScrollListenerProvider<S> scrollListenerProvider)
{
this.key = key;
this.scrollView = scrollView;
this.scrollListenerProvider = scrollListenerProvider;
}
@Override
public void onHeaderLayout()
{
scrollView.setClipToPadding(false);
scrollView.setPadding(scrollView.getPaddingLeft(), header.getHeight(), scrollView.getPaddingRight(), scrollView.getPaddingBottom());
scroll(header.getTranslationY());
scrollListenerProvider.addScrollListener(getScrollListener());
// scrollView.setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener()
// {
// @Override
// public void onChildViewAdded(View parent, View child)
// {
// setPaddingBottom();
// }
//
// @Override
// public void onChildViewRemoved(View parent, View child)
// {
// setPaddingBottom();
// }
// });
}
private void setPaddingBottom()
{
if(scrollView.getChildCount() > 0)
{
// we would like to make it scroll height of content - size of the scrollview - heightToKeep
int contentHeight = scrollView.getChildAt(0).getHeight() * Math.round((scrollView.getChildCount() * 1f) / ((scrollView instanceof GridView) ? ((GridView)scrollView).getNumColumns() : 1));
int paddingBottom = scrollView.getBottom() - contentHeight - heightToKeep;
// if(paddingBottom > (header.getHeight() - heightToKeep))
scrollView.setPadding(0, scrollView.getPaddingTop(), 0, paddingBottom);
}
}
protected void clean()
{
scrollListenerProvider.removeScrollListener(getScrollListener());
}
protected abstract void scroll(float translationY);
protected abstract S getScrollListener();
}
public class AbsListViewContainer extends ScrollViewContainer<AbsListView, AbsListView.OnScrollListener>
{
private AbsListView.OnScrollListener scrollListener = new AbsListView.OnScrollListener()
{
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)
{
if(key != activeScrollView || visibleItemCount == 0 || firstVisibleItem != 0)
return;
int top = view.getChildAt(0).getTop();
translateHeader(key, view, top);
}
};
public AbsListViewContainer(int key, AbsListView scrollView, ScrollListenerProvider<AbsListView.OnScrollListener> scrollListenerProvider)
{
super(key, scrollView, scrollListenerProvider);
addHeaderListener(this);
}
@Override
protected void scroll(float translationY)
{
if(scrollView.getTranslationY() != 0)
scrollView.setTranslationY(0);
// this is the only trick I've found to make it work
// the commented line doesn't work great, it make the listview jump to an old position...
scrollView.smoothScrollToPositionFromTop(0, (int) translationY, 0);
// scrollView.scrollBy(0, (int) (latestTranslationY - translationY));
}
@Override
protected AbsListView.OnScrollListener getScrollListener()
{
return scrollListener;
}
@Override
public void onHeaderTranslate(int key, float translationY)
{
if(key != this.key)
scroll(translationY);
}
public void clean()
{
super.clean();
removeHeaderListener(this);
}
}
public class NotifyingScrollViewContainer extends ScrollViewContainer<NotifyingScrollView, NotifyingScrollView.OnScrollChangedListener>
{
private NotifyingScrollView.OnScrollChangedListener scrollListener = new NotifyingScrollView.OnScrollChangedListener()
{
@Override
public void onScrollChanged(ScrollView who, int l, int t, int oldl, int oldt)
{
if(key != activeScrollView || who.getChildCount() == 0)
return;
translateHeader(key, who, who.getChildAt(0).getTop() - t);
}
};
public NotifyingScrollViewContainer(int key, NotifyingScrollView scrollView, ScrollListenerProvider<NotifyingScrollView.OnScrollChangedListener> scrollListenerProvider)
{
super(key, scrollView, scrollListenerProvider);
addHeaderListener(this);
}
@Override
protected void scroll(float translationY)
{
if(scrollView.getTranslationY() != 0)
scrollView.setTranslationY(0);
scrollView.scrollBy(0, (int) (latestTranslationY - translationY));
}
@Override
protected NotifyingScrollView.OnScrollChangedListener getScrollListener()
{
return scrollListener;
}
@Override
public void onHeaderTranslate(int key, float translationY)
{
if(key != this.key)
{
// Log.e("test", "scroll ScrollView with key : " + key + " by " + (latestTranslationY - translationY));
scroll(translationY);
}
}
public void clean()
{
super.clean();
removeHeaderListener(this);
}
}
public class NotifyingWebViewContainer extends ScrollViewContainer<NotifyingWebView, NotifyingWebView.OnScrollChangedListener>
{
private NotifyingWebView.OnScrollChangedListener scrollListener = new NotifyingWebView.OnScrollChangedListener()
{
@Override
public void onScrollChanged(NotifyingWebView who, int l, int t, int oldl, int oldt)
{
if(key != activeScrollView || who.getChildCount() == 0)
return;
translateHeader(key, who, who.getChildAt(0).getTop() - t);
}
};
public NotifyingWebViewContainer(int key, NotifyingWebView scrollView, ScrollListenerProvider<NotifyingWebView.OnScrollChangedListener> scrollListenerProvider)
{
super(key, scrollView, scrollListenerProvider);
addHeaderListener(this);
}
@Override
protected void scroll(float translationY)
{
if(scrollView.getTranslationY() != 0)
scrollView.setTranslationY(0);
scrollView.scrollBy(0, (int) (latestTranslationY - translationY));
}
@Override
protected NotifyingWebView.OnScrollChangedListener getScrollListener()
{
return scrollListener;
}
@Override
public void onHeaderTranslate(int key, float translationY)
{
if(key != this.key)
{
// Log.e("test", "scroll ScrollView with key : " + key + " by " + (latestTranslationY - translationY));
scroll(translationY);
}
}
public void clean()
{
super.clean();
removeHeaderListener(this);
}
}
public class RecyclerViewContainer extends ScrollViewContainer<RecyclerView, RecyclerView.OnScrollListener>
{
private RecyclerView.OnScrollListener scrollListener = new RecyclerView.OnScrollListener()
{
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy)
{
if(key != activeScrollView || recyclerView.getChildCount() == 0)
return;
translateHeader(key, recyclerView, recyclerView.getChildAt(0).getTop() - dy);
}
};
public RecyclerViewContainer(int key, RecyclerView recyclerView, ScrollListenerProvider<RecyclerView.OnScrollListener> scrollListenerProvider)
{
super(key, recyclerView, scrollListenerProvider);
addHeaderListener(this);
}
@Override
protected void scroll(float translationY)
{
if(scrollView.getTranslationY() != 0)
scrollView.setTranslationY(0);
scrollView.scrollBy(0, (int) (latestTranslationY - translationY));
}
@Override
protected RecyclerView.OnScrollListener getScrollListener()
{
return scrollListener;
}
@Override
public void onHeaderTranslate(int key, float translationY)
{
if(key != this.key)
{
// Log.e("test", "scroll ScrollView with key : " + key + " by " + (latestTranslationY - translationY));
scroll(translationY);
}
}
public void clean()
{
super.clean();
removeHeaderListener(this);
}
}
public interface OnHeaderListener
{
public void onHeaderLayout();
public void onHeaderTranslate(int key, float translationY);
}
public interface Provider
{
public ScrollHeaderHelper getScrollHeaderHelper();
}
}