package com.gnod.geekr.widget;
import android.content.Context;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;
import android.view.animation.Transformation;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.gnod.geekr.R;
import com.gnod.geekr.app.AppConfig;
import com.gnod.geekr.tool.manager.Utils;
public class PullToRefreshListView extends ListView implements OnScrollListener {
private final static String TAG = PullToRefreshListView.class.getSimpleName();
//Flag
private final static int PULL_To_REFRESH = 0;
private final static int RELEASE_To_REFRESH = 1;
private final static int REFRESHING = 2;
private final static int DONE = 3;
private LayoutInflater inflater;
private LinearLayout mHeaderView;
private TextView mTipsText;
private ImageView mArrowView;
private ProgressBar mSpinner;
private RotateAnimation animRotate;
private RotateAnimation animReverseRotate;
private boolean isRecored;
private int mHeaderViewPaddingTop;
private int mHeaderOrgPaddingTop;
private GestureDetector gestureDetector;
private int mPullState = DONE;
public OnRefreshListener refreshListener;
public OnLastItemVisibleListener lastItemVisibleListener;
private boolean lastItemVisible;
private boolean isHeadItemVisible;
private boolean firstItemVisualFlag = false;
private int mPullThrehold;
public interface OnRefreshListener {
public void onRefresh();
}
public interface OnLastItemVisibleListener {
public void onLastItemVisible(int lastIndex);
}
public interface OnHeadVisualChangeListener {
public void onVisualChange(boolean visible);
}
public PullToRefreshListView(Context context) {
this(context, null);
}
public PullToRefreshListView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context) {
initArrowAnimation();
initPullHeader(context);
setOnScrollListener(this);
gestureDetector = new GestureDetector(context, gestureListener);
}
private void initPullHeader(Context context) {
inflater = LayoutInflater.from(context);
mHeaderView = (LinearLayout) inflater.inflate(
R.layout.pull_to_refresh_head, null);
mArrowView = (ImageView) mHeaderView
.findViewById(R.id.head_arrowImageView);
mSpinner = (ProgressBar) mHeaderView
.findViewById(R.id.head_progressBar);
mTipsText = (TextView) mHeaderView.findViewById(R.id.head_tipsTextView);
measureView(mHeaderView);
mHeaderViewPaddingTop = 0;
mPullThrehold = mHeaderView.getMeasuredHeight() + 20;
mHeaderOrgPaddingTop = mPullThrehold;
addHeaderView(mHeaderView);
}
private void setHeaderPaddingTop(int paddingTop) {
mHeaderView.setPadding(mHeaderView.getPaddingLeft(), paddingTop,
mHeaderView.getPaddingRight(), mHeaderView.getPaddingBottom());
}
/**
* 设置滑动效果
*/
private void initArrowAnimation() {
animRotate = new RotateAnimation(0, -180,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
animRotate.setInterpolator(new LinearInterpolator());
animRotate.setDuration(100);
animRotate.setFillAfter(true);
animReverseRotate = new RotateAnimation(-180, 0,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
animReverseRotate.setInterpolator(new LinearInterpolator());
animReverseRotate.setDuration(100);
animReverseRotate.setFillAfter(true);
}
@Override
public void onScroll(AbsListView view, int firstVisiableItem,
int visibleItemCount, int totalItemCount) {
detectLastItemVisible(firstVisiableItem, visibleItemCount, totalItemCount);
if(firstVisiableItem > 0 ^ firstItemVisualFlag ){
firstItemVisualFlag = !firstItemVisualFlag;
if(headVisualChangeListener != null)
headVisualChangeListener.onVisualChange(firstVisiableItem == 0);
}
}
private void detectLastItemVisible(int firstVisiableItem,
int visibleItemCount, int totalItemCount) {
isHeadItemVisible = firstVisiableItem == 0? true : false;
if (firstVisiableItem + visibleItemCount >= totalItemCount) {
if (mPullState != REFRESHING && lastItemVisible == false
&& lastItemVisibleListener != null) {
lastItemVisible = true;
// including Header View,here using totalItemCount - 2
lastItemVisibleListener.onLastItemVisible(totalItemCount - 2);
}
} else {
lastItemVisible = false;
}
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_FLING) {
if (!Utils.hasHoneycomb()) {
AppConfig.sImageFetcher.setPauseWork(true);
}
} else {
AppConfig.sImageFetcher.setPauseWork(false);
}
}
public boolean dispatchTouchEvent(MotionEvent event) {
if(onTouched.onTouchEvent(event)){
return true;
}
return super.dispatchTouchEvent(event);
}
private interface OnTouchEventListener {
public boolean onTouchEvent(MotionEvent ev);
}
private OnTouchEventListener onTouched = new OnTouchEventListener() {
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
if (isRecored) {
requestDisallowInterceptTouchEvent(false);
if (mPullState != REFRESHING) {
if (mPullState == PULL_To_REFRESH) {
mPullState = DONE;
changeHeaderViewByState(mPullState);
} else if (mPullState == RELEASE_To_REFRESH) {
mPullState = REFRESHING;
changeHeaderViewByState(mPullState);
setSelection(0);
onRefresh();
} else if(mHeaderView.getPaddingTop() > 0) {
mPullState = DONE;
changeHeaderViewByState(mPullState);
}
}
isRecored = false;
return true;
}
break;
}
return gestureDetector.onTouchEvent(event);
}
};
private OnGestureListener gestureListener = new OnGestureListener() {
@Override
public boolean onSingleTapUp(MotionEvent e) {
return false;
}
@Override
public void onShowPress(MotionEvent e) {
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
int deltaY = (int) (e1.getY() - e2.getY());
if(mPullState != REFRESHING) {
if (!isRecored && isHeadItemVisible && deltaY < 0) {
isRecored = true;
requestDisallowInterceptTouchEvent(true);
}
if (isRecored) {
int paddingTop = mHeaderView.getPaddingTop();
if(paddingTop < mPullThrehold && paddingTop > mHeaderViewPaddingTop) {
if(mPullState == RELEASE_To_REFRESH) {
changeHeaderViewByState(PULL_To_REFRESH);
}
mPullState = PULL_To_REFRESH;
} else if(paddingTop >= mPullThrehold) {
if(mPullState == PULL_To_REFRESH) {
changeHeaderViewByState(RELEASE_To_REFRESH);
}
mPullState = RELEASE_To_REFRESH;
}
int topPadding = (int) (mHeaderViewPaddingTop - deltaY/2);
mHeaderView.setPadding(mHeaderView.getPaddingLeft(), topPadding,
mHeaderView.getPaddingRight(), mHeaderView.getPaddingBottom());
mHeaderView.invalidate();
return true;
}
}
return false;
}
@Override
public void onLongPress(MotionEvent e) {
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
return false;
}
@Override
public boolean onDown(MotionEvent e) {
return false;
}
};
private OnHeadVisualChangeListener headVisualChangeListener;
public void onRefreshing() {
mPullState = REFRESHING;
changeHeaderViewByState(mPullState);
}
// Call this method to refresh the pull header
private void changeHeaderViewByState(int state) {
switch (state) {
case RELEASE_To_REFRESH:
mSpinner.setVisibility(View.GONE);
mTipsText.setVisibility(View.VISIBLE);
mArrowView.setVisibility(View.VISIBLE);
mArrowView.clearAnimation();
mArrowView.startAnimation(animRotate);
mTipsText.setText(R.string.pull_to_refresh_release_label);
break;
case PULL_To_REFRESH:
mSpinner.setVisibility(View.GONE);
mTipsText.setVisibility(View.VISIBLE);
mArrowView.setVisibility(View.VISIBLE);
mArrowView.clearAnimation();
mArrowView.startAnimation(animReverseRotate);
mTipsText.setText(R.string.pull_to_refresh_pull_label);
break;
case REFRESHING:
setHeaderPaddingTop(mHeaderOrgPaddingTop);
mHeaderView.invalidate();
mSpinner.setVisibility(View.VISIBLE);
mArrowView.clearAnimation();
mArrowView.setVisibility(View.GONE);
mTipsText.setText(R.string.pull_to_refresh_refreshing_label);
break;
case DONE:
if (mHeaderViewPaddingTop - 1 < mHeaderView.getPaddingTop()) {
ResetAnimimation animation = new ResetAnimimation(mHeaderView,
mHeaderViewPaddingTop, false);
animation.setDuration(300);
mHeaderView.startAnimation(animation);
}
mSpinner.setVisibility(View.GONE);
mArrowView.clearAnimation();
mArrowView.setImageResource(R.drawable.ic_pulltorefresh_arrow);
mArrowView.setVisibility(View.VISIBLE);
mTipsText.setText(R.string.pull_to_refresh_pull_label);
break;
}
}
// 点击刷新
public void clickRefresh() {
mPullState = REFRESHING;
changeHeaderViewByState(mPullState);
onRefresh();
}
public boolean isRefreshing() {
return REFRESHING == mPullState;
}
public void setOnRefreshListener(OnRefreshListener refreshListener) {
this.refreshListener = refreshListener;
}
public void setOnLastItemVisibleListener(OnLastItemVisibleListener listener) {
this.lastItemVisibleListener = listener;
}
public void setOnHeadVisualChangeListener(OnHeadVisualChangeListener l) {
this.headVisualChangeListener = l;
}
public void onRefreshComplete(String update) {
onRefreshComplete();
}
public void onRefreshComplete() {
mPullState = DONE;
changeHeaderViewByState(mPullState);
}
private void onRefresh() {
if (refreshListener != null) {
refreshListener.onRefresh();
}
}
// 计算headView的width及height值
private void measureView(View child) {
ViewGroup.LayoutParams p = child.getLayoutParams();
if (p == null) {
p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
}
int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0, p.width);
int lpHeight = p.height;
int childHeightSpec;
if (lpHeight > 0) {
childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight,
MeasureSpec.EXACTLY);
} else {
childHeightSpec = MeasureSpec.makeMeasureSpec(0,
MeasureSpec.UNSPECIFIED);
}
child.measure(childWidthSpec, childHeightSpec);
}
public class ResetAnimimation extends Animation {
private int targetHeight;
private int originalHeight;
private int extraHeight;
private int viewPaddingBottom;
private int viewPaddingRight;
private int viewPaddingLeft;
private boolean down;
private View view;
protected ResetAnimimation(View view, int targetHeight, boolean down) {
this.view = view;
this.viewPaddingLeft = view.getPaddingLeft();
this.viewPaddingRight = view.getPaddingRight();
this.viewPaddingBottom = view.getPaddingBottom();
this.targetHeight = targetHeight;
this.down = down;
originalHeight = view.getPaddingTop();
extraHeight = this.targetHeight - originalHeight;
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
int newHeight;
newHeight = (int) (targetHeight - extraHeight * (1 - interpolatedTime));
view.setPadding(viewPaddingLeft, newHeight,
viewPaddingRight, viewPaddingBottom);
view.requestLayout();
}
@Override
public void initialize(int width, int height, int parentWidth,
int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
}
}
}