package com.iwhys.mylistview.jazzy;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewPropertyAnimator;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.AbsListView;
import android.widget.ListView;
import com.iwhys.mylistview.R;
import java.util.HashSet;
public class JazzyHelper implements AbsListView.OnScrollListener {
public static final int DURATION = 600;
private JazzyEffect mTransitionEffect = null;
private boolean mIsScrolling = false;
private int mFirstVisibleItem = -1;
private int mLastVisibleItem = -1;
private int mPreviousFirstVisibleItem = 0;
private long mPreviousEventTime = 0;
private double mSpeed = 0;
private int mMaxVelocity = 0;
public static final int MAX_VELOCITY_OFF = 0;
private AbsListView.OnScrollListener mAdditionalOnScrollListener;
private boolean mOnlyAnimateNewItems;
private boolean mOnlyAnimateOnFling;
private boolean mIsFlingEvent;
private boolean mSimulateGridWithList;
private final HashSet<Integer> mAlreadyAnimatedItems;
public JazzyHelper(Context context, AttributeSet attrs) {
mAlreadyAnimatedItems = new HashSet<Integer>();
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LoadMoreListView);
int maxVelocity = a.getInteger(R.styleable.LoadMoreListView_max_velocity, MAX_VELOCITY_OFF);
mOnlyAnimateNewItems = a.getBoolean(R.styleable.LoadMoreListView_only_animate_new_items, false);
mOnlyAnimateOnFling = a.getBoolean(R.styleable.LoadMoreListView_max_velocity, false);
mSimulateGridWithList = a.getBoolean(R.styleable.LoadMoreListView_simulate_grid_with_list, false);
a.recycle();
setMaxAnimationVelocity(maxVelocity);
}
public void setOnScrollListener(AbsListView.OnScrollListener l) {
// hijack the scroll listener setter and have this list also notify the additional listener
mAdditionalOnScrollListener = l;
}
/**
* @see android.widget.AbsListView.OnScrollListener#onScroll
*/
@Override
public final void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
boolean shouldAnimateItems = (mFirstVisibleItem != -1 && mLastVisibleItem != -1);
int lastVisibleItem = firstVisibleItem + visibleItemCount - 1;
if (mIsScrolling && shouldAnimateItems) {
setVelocity(firstVisibleItem, totalItemCount);
int indexAfterFirst = 0;
while (firstVisibleItem + indexAfterFirst < mFirstVisibleItem) {
View item = view.getChildAt(indexAfterFirst);
doJazziness(item, firstVisibleItem + indexAfterFirst, -1, view);
indexAfterFirst++;
}
int indexBeforeLast = 0;
while (lastVisibleItem - indexBeforeLast > mLastVisibleItem) {
View item = view.getChildAt(lastVisibleItem - firstVisibleItem - indexBeforeLast);
doJazziness(item, lastVisibleItem - indexBeforeLast, 1, view);
indexBeforeLast++;
}
} else if (!shouldAnimateItems) {
for (int i = firstVisibleItem; i < visibleItemCount; i++) {
mAlreadyAnimatedItems.add(i);
}
}
mFirstVisibleItem = firstVisibleItem;
mLastVisibleItem = lastVisibleItem;
notifyAdditionalOnScrollListener(view, firstVisibleItem, visibleItemCount, totalItemCount);
}
/**
* Should be called in onScroll to keep take of current Velocity.
*
* @param firstVisibleItem The index of the first visible item in the ListView.
*/
private void setVelocity(int firstVisibleItem, int totalItemCount) {
if (mMaxVelocity > MAX_VELOCITY_OFF && mPreviousFirstVisibleItem != firstVisibleItem) {
long currTime = System.currentTimeMillis();
long timeToScrollOneItem = currTime - mPreviousEventTime;
if (timeToScrollOneItem < 1) {
double newSpeed = ((1.0d / timeToScrollOneItem) * 1000);
// We need to normalize velocity so different size item don't
// give largely different velocities.
if (newSpeed < (0.9f * mSpeed)) {
mSpeed *= 0.9f;
} else if (newSpeed > (1.1f * mSpeed)) {
mSpeed *= 1.1f;
} else {
mSpeed = newSpeed;
}
} else {
mSpeed = ((1.0d / timeToScrollOneItem) * 1000);
}
mPreviousFirstVisibleItem = firstVisibleItem;
mPreviousEventTime = currTime;
}
}
/**
* @return Returns the current Velocity of the ListView's scrolling in items
* per second.
*/
private double getVelocity() {
return mSpeed;
}
/**
* Initializes the item view and triggers the animation.
*
* @param item The view to be animated.
* @param position The index of the view in the list.
* @param scrollDirection Positive number indicating scrolling down, or negative number indicating scrolling up.
*/
private void doJazziness(View item, int position, int scrollDirection, AbsListView view) {
if (mIsScrolling) {
if (mOnlyAnimateNewItems && mAlreadyAnimatedItems.contains(position))
return;
if (mOnlyAnimateOnFling && !mIsFlingEvent)
return;
//不对ListView的头部及底部执行动画
if ((view instanceof ListView)) {
ListView listView = (ListView) view;
if (position < listView.getHeaderViewsCount() || position >= (listView.getAdapter().getCount() - listView.getFooterViewsCount())) {
return;
}
}
if (mMaxVelocity > MAX_VELOCITY_OFF && mMaxVelocity < getVelocity())
return;
if (mSimulateGridWithList) {
ViewGroup itemRow = (ViewGroup) item;
for (int i = 0; i < itemRow.getChildCount(); i++)
doJazzinessImpl(itemRow.getChildAt(i), position, scrollDirection);
} else {
doJazzinessImpl(item, position, scrollDirection);
}
mAlreadyAnimatedItems.add(position);
}
}
private void doJazzinessImpl(View item, int position, int scrollDirection) {
if (item == null) return;
ViewPropertyAnimator animator = item.animate()
.setDuration(DURATION)
.setInterpolator(new AccelerateDecelerateInterpolator());
scrollDirection = scrollDirection > 0 ? 1 : -1;
if (mTransitionEffect == null) return;
mTransitionEffect.initView(item, position, scrollDirection);
mTransitionEffect.setupAnimation(item, position, scrollDirection, animator);
animator.start();
}
/**
* @see android.widget.AbsListView.OnScrollListener#onScrollStateChanged
*/
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
switch (scrollState) {
case AbsListView.OnScrollListener.SCROLL_STATE_IDLE:
mIsScrolling = false;
mIsFlingEvent = false;
break;
case AbsListView.OnScrollListener.SCROLL_STATE_FLING:
mIsFlingEvent = true;
break;
case AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
mIsScrolling = true;
mIsFlingEvent = false;
break;
default:
break;
}
notifyAdditionalOnScrollStateChangedListener(view, scrollState);
}
public void setTransitionEffect(JazzyEffect transitionEffect) {
mTransitionEffect = transitionEffect;
}
public void setShouldOnlyAnimateNewItems(boolean onlyAnimateNew) {
mOnlyAnimateNewItems = onlyAnimateNew;
}
public void setShouldOnlyAnimateFling(boolean onlyFling) {
mOnlyAnimateOnFling = onlyFling;
}
public void setMaxAnimationVelocity(int itemsPerSecond) {
mMaxVelocity = itemsPerSecond;
}
public void setSimulateGridWithList(boolean simulateGridWithList) {
mSimulateGridWithList = simulateGridWithList;
}
/**
* Notifies the OnScrollListener of an onScroll event, since JazzyListView is the primary listener for onScroll events.
*/
private void notifyAdditionalOnScrollListener(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (mAdditionalOnScrollListener != null) {
mAdditionalOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
}
}
/**
* Notifies the OnScrollListener of an onScrollStateChanged event, since JazzyListView is the primary listener for onScrollStateChanged events.
*/
private void notifyAdditionalOnScrollStateChangedListener(AbsListView view, int scrollState) {
if (mAdditionalOnScrollListener != null) {
mAdditionalOnScrollListener.onScrollStateChanged(view, scrollState);
}
}
}