package uk.co.chrisjenx.paralloid;
import android.graphics.drawable.Drawable;
import android.view.View;
import java.util.Iterator;
import java.util.Set;
import java.util.WeakHashMap;
import uk.co.chrisjenx.paralloid.graphics.ParallaxDrawable;
import uk.co.chrisjenx.paralloid.transform.Transformer;
import uk.co.chrisjenx.paralloid.utils.ParallaxHelper;
/**
* Parallax Controller that take input from anything.
* <p/>
* Created by chris on 23/10/2013
* Project: Paralloid
*/
public class ParallaxController<T extends Object> implements ParallaxorListener {
public static final String TAG = ParallaxController.class.getSimpleName();
/**
* HashMap which contains the parallaxed views.
*/
protected WeakHashMap<View, ParallaxViewInfo> mViewHashMap;
/**
* The Background of Views to Parallax
*/
protected WeakHashMap<View, ParallaxDrawable> mParallaxDrawableMap;
/**
* The Optional Scroll Changed Listener for the user to listen to scroll events.
*/
protected OnScrollChangedListener mScrollChangedListener;
/**
* Gets set to true if a View which has its own OnScrollListener
*/
protected boolean mIgnoreOnScrollListener;
//
protected int mLastScrollX = 0;
protected int mLastScrollY = 0;
/**
* The Wrapped Object
*/
protected final T mWrapped;
protected ParallaxController(T wrapped) {
mWrapped = wrapped;
}
public T getWrapped() {
return mWrapped;
}
/**
* Add a view to be parallax'd by. If already set this will replace the current factor.
*
* @param view passing in yourself will throw an exception.
* @param multiplier passing in
*/
public void parallaxViewBy(View view, float multiplier) {
parallaxViewBy(view, null, multiplier);
}
@Override
public void parallaxViewBy(View view, Transformer transformer, float multiplier) {
if (view == null) return;
if (view == mWrapped)
throw new IllegalArgumentException("You can't parallax yourself, this would end badly, Parallax other Views");
checkViewMap();
mViewHashMap.put(view, new ParallaxViewInfo(multiplier, transformer));
// We force this to update the view just added
onScrollChanged(mLastScrollX, mLastScrollY, mLastScrollX, mLastScrollY, true);
}
/**
* @param view View which to attach the drawable to. (null will do nothing)
* @param drawable Drawable to Parallax (null will do nothing)
* @param multiplier How much to move it by, 1 would be a 1:1 relationship. 0.5 would move the.
* Passing in 0 will make the background not move at all.
*/
@Override
public void parallaxViewBackgroundBy(final View view, final Drawable drawable, final float multiplier) {
if (view == null) return;
checkBackgroundMap();
final ParallaxDrawable parallaxBackground = ParallaxHelper.getParallaxDrawable(drawable, multiplier);
ParallaxHelper.setParallaxBackground(view, parallaxBackground);
mParallaxDrawableMap.put(view, parallaxBackground);
}
/**
* Feel free to implement {@link OnScrollChangedListener} to get call
* backs to the wrapped view for scroll changed events.
* <p/>
* <b>Note</b>: this will get called, AFTER any parallax modification.
*
* @param onScrollChangedListener Null is valid (it will remove it if set).
*/
@Override
public void setOnScrollListener(OnScrollChangedListener onScrollChangedListener) {
mScrollChangedListener = onScrollChangedListener;
}
/**
* Used to do parallax, Your implementation should call directly through to this. Some implementations ignore this
* on purpose (e.g. the ListView)
*
* @param who if Who != getWrapped we ignore scrolling, only the object the controller is attached too can
* scroll it.
*/
@Override
public final void onScrollChanged(Object who, int l, int t, int oldl, int oldt) {
if (who != getWrapped() || mIgnoreOnScrollListener) return;
onScrollChanged(l, t, oldl, oldt, false);
}
protected final void onScrollChanged(int offsetX, int offsetY, int oldOffsetX, int oldOffsetY, boolean force) {
if (offsetX != oldOffsetX || offsetY != oldOffsetY || force) {
doScrollChanged(offsetX, offsetY, oldOffsetX, oldOffsetY);
}
}
/**
* Will do the scroll changed stuff.
*
* @param x currentX of Parallaxor View
* @param y currentX of Parallaxor View
* @param oldX Previous X
* @param oldY Previous Y
*/
protected final void doScrollChanged(final int x, final int y, final int oldX, final int oldY) {
doScrollViews(x, y);
//Parallax this background if we can
doScrollBackground(x, y);
// Scroll Changed Listener
doScrollListener(getWrapped(), x, y, oldX, oldY);
// Set new LastScrollPos
mLastScrollX = x;
mLastScrollY = y;
}
// --
// doScrollChanged Pointers to keep memory consumption down during fast scrolling
//
private Set<View> keySetPointer;
private Iterator<View> iteratorPointer;
private ParallaxViewInfo parallaxInfoPointer;
private ParallaxDrawable parallaxDrawablePointer;
private View viewPointer;
// --
private final void doScrollViews(final int x, final int y) {
if (mViewHashMap == null) return;
keySetPointer = mViewHashMap.keySet();
iteratorPointer = keySetPointer.iterator();
while (iteratorPointer.hasNext()) {
viewPointer = iteratorPointer.next();
if (viewPointer == null)
continue;
// Get Value
parallaxInfoPointer = mViewHashMap.get(viewPointer);
// Parallax the other view
ParallaxHelper.scrollViewBy(viewPointer, x, y, parallaxInfoPointer.interpolator, parallaxInfoPointer.factor);
}
}
private final void doScrollBackground(final int x, final int y) {
if (mParallaxDrawableMap == null) return;
keySetPointer = mParallaxDrawableMap.keySet();
iteratorPointer = keySetPointer.iterator();
while (iteratorPointer.hasNext()) {
viewPointer = iteratorPointer.next();
if (viewPointer == null)
continue;
// Get Value
parallaxDrawablePointer = mParallaxDrawableMap.get(viewPointer);
// Parallax the drawable
ParallaxHelper.scrollParallaxDrawableBy(parallaxDrawablePointer, x, y);
}
}
private final void doScrollListener(final Object who, final int x, final int y, final int oldX, final int oldY) {
if (mScrollChangedListener != null && (x != oldX || y != oldY)) {
mScrollChangedListener.onScrollChanged(who, x, y, oldX, oldY);
}
}
/**
* Checks the viewMap is ready to use.
*/
private final void checkViewMap() {
if (mViewHashMap == null)
mViewHashMap = new WeakHashMap<View, ParallaxViewInfo>();
}
/**
* Checks the BackgroundMap is ready to use.
*/
private final void checkBackgroundMap() {
if (mParallaxDrawableMap == null)
mParallaxDrawableMap = new WeakHashMap<View, ParallaxDrawable>();
}
}