Back to project page XPullToRefreshListView.
The source code is released under:
MIT License
If you think the Android project XPullToRefreshListView listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
package com.custom.widget; //www . j a v a2s .co m import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.animation.LinearInterpolator; import android.view.animation.RotateAnimation; import android.widget.AbsListView; import android.widget.ImageView; import android.widget.ListAdapter; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.AbsListView.OnScrollListener; public class PullToRefreshListView extends ListView implements OnScrollListener { private static final int TAP_TO_REFRESH = 1; private static final int PULL_TO_REFRESH = 2; private static final int RELEASE_TO_REFRESH = 3; protected static final int REFRESHING = 4; /** manual control whether albe to refresh or not */ private boolean canRefresh = true; /** * use it with `canRefresh` when you don't wan't to show headview when it * can't refresh */ private boolean ableToShowHeadView = true; private OnRefreshListener mOnRefreshListener; /** * Listener that will receive notifications every time the list scrolls. */ private OnScrollListener mOnScrollListener; protected LayoutInflater mInflater; // header private RelativeLayout mRefreshView; private TextView mRefreshViewText; private ImageView mRefreshViewImage; private ProgressBar mRefreshViewProgress; private TextView mRefreshViewLastUpdated; protected int mCurrentScrollState; protected int mRefreshState; private RotateAnimation mFlipAnimation; private RotateAnimation mReverseFlipAnimation; private int mRefreshViewHeight; private int mRefreshOriginalTopPadding; private int mLastMotionY; private boolean mBounceHack; public PullToRefreshListView(Context context) { super(context); init(context); } public PullToRefreshListView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public PullToRefreshListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context); } protected void init(Context context) { // Load all of the animations we need in code rather than through XML mFlipAnimation = new RotateAnimation(0, -180, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f); mFlipAnimation.setInterpolator(new LinearInterpolator()); mFlipAnimation.setDuration(250); mFlipAnimation.setFillAfter(true); mReverseFlipAnimation = new RotateAnimation(-180, 0, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f); mReverseFlipAnimation.setInterpolator(new LinearInterpolator()); mReverseFlipAnimation.setDuration(250); mReverseFlipAnimation.setFillAfter(true); mInflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE); // header mRefreshView = (RelativeLayout) mInflater.inflate( R.layout.pull_to_refresh_header, this, false); mRefreshViewText = (TextView) mRefreshView .findViewById(R.id.pull_to_refresh_text); mRefreshViewImage = (ImageView) mRefreshView .findViewById(R.id.pull_to_refresh_image); mRefreshViewProgress = (ProgressBar) mRefreshView .findViewById(R.id.pull_to_refresh_progress); mRefreshViewLastUpdated = (TextView) mRefreshView .findViewById(R.id.pull_to_refresh_updated_at); mRefreshViewImage.setMinimumHeight(50); mRefreshView.setOnClickListener(new OnClickRefreshListener()); mRefreshOriginalTopPadding = mRefreshView.getPaddingTop(); mRefreshState = TAP_TO_REFRESH; addHeaderView(mRefreshView); super.setOnScrollListener(this); measureView(mRefreshView); mRefreshViewHeight = mRefreshView.getMeasuredHeight(); } @Override protected void onAttachedToWindow() { // have to ask super to attach to window, otherwise it won't scroll in // jelly bean. super.onAttachedToWindow(); setSelection(1); } @Override public void setAdapter(ListAdapter adapter) { super.setAdapter(adapter); setSelection(1); } /** * Set the listener that will receive notifications every time the list * scrolls. * * @param l * The scroll listener. */ @Override public void setOnScrollListener(AbsListView.OnScrollListener l) { mOnScrollListener = l; } /** * Register a callback to be invoked when this list should be refreshed. * * @param onRefreshListener * The callback to run. */ public void setOnRefreshListener(OnRefreshListener onRefreshListener) { mOnRefreshListener = onRefreshListener; } /** * Set a text to represent when the list was last updated. * * @param lastUpdated * Last updated at. */ public void setLastUpdated(CharSequence lastUpdated) { if (lastUpdated != null) { mRefreshViewLastUpdated.setVisibility(View.VISIBLE); mRefreshViewLastUpdated.setText(lastUpdated); } else { mRefreshViewLastUpdated.setVisibility(View.GONE); } } @Override public boolean onTouchEvent(MotionEvent event) { final int y = (int) event.getY(); mBounceHack = false; switch (event.getAction()) { case MotionEvent.ACTION_UP: if (!isVerticalScrollBarEnabled()) { setVerticalScrollBarEnabled(true); } if (getFirstVisiblePosition() == 0 && mRefreshState != REFRESHING) { if ((mRefreshView.getBottom() >= mRefreshViewHeight || mRefreshView .getTop() >= 0) && mRefreshState == RELEASE_TO_REFRESH) { // Initiate the refresh mRefreshState = REFRESHING; prepareForRefresh(); onRefresh(); } else if (mRefreshView.getBottom() < mRefreshViewHeight || mRefreshView.getTop() <= 0) { // Abort refresh and scroll down below the refresh view resetHeader(); setSelection(1); } } break; case MotionEvent.ACTION_DOWN: mLastMotionY = y; break; case MotionEvent.ACTION_MOVE: applyHeaderPadding(event); break; } return super.onTouchEvent(event); } private void applyHeaderPadding(MotionEvent ev) { // getHistorySize has been available since API 1 int pointerCount = ev.getHistorySize(); for (int p = 0; p < pointerCount; p++) { if (mRefreshState == RELEASE_TO_REFRESH) { if (isVerticalFadingEdgeEnabled()) { setVerticalScrollBarEnabled(false); } int historicalY = (int) ev.getHistoricalY(p); // Calculate the padding to apply, we divide by 1.7 to // simulate a more resistant effect during pull. int topPadding = (int) (((historicalY - mLastMotionY) - mRefreshViewHeight) / 1.7); mRefreshView.setPadding(mRefreshView.getPaddingLeft(), topPadding, mRefreshView.getPaddingRight(), mRefreshView.getPaddingBottom()); } } } /** * Sets the header padding back to original size. */ private void resetHeaderPadding() { mRefreshView.setPadding(mRefreshView.getPaddingLeft(), mRefreshOriginalTopPadding, mRefreshView.getPaddingRight(), mRefreshView.getPaddingBottom()); } /** * Resets the header to the original state. */ private void resetHeader() { if (mRefreshState != TAP_TO_REFRESH) { mRefreshState = TAP_TO_REFRESH; resetHeaderPadding(); // Set refresh view text to the pull label mRefreshViewText.setText(R.string.xpull_to_refresh_tap_label); // Replace refresh drawable with arrow drawable mRefreshViewImage .setImageResource(R.drawable.ic_pulltorefresh_arrow); // Clear the full rotation animation mRefreshViewImage.clearAnimation(); // Hide progress bar and arrow. mRefreshViewImage.setVisibility(View.GONE); mRefreshViewProgress.setVisibility(View.GONE); } } private void measureView(View child) { ViewGroup.LayoutParams p = child.getLayoutParams(); if (p == null) { p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_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 void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { // if canRefresh == false don't handler scroll if (!canRefresh) return; // When the refresh view is completely visible, change the text to say // "Release to refresh..." and flip the arrow drawable. if (mCurrentScrollState == SCROLL_STATE_TOUCH_SCROLL && mRefreshState != REFRESHING) { if (firstVisibleItem == 0) { mRefreshViewImage.setVisibility(View.VISIBLE); if ((mRefreshView.getBottom() >= mRefreshViewHeight + 20 || mRefreshView .getTop() >= 0) && mRefreshState != RELEASE_TO_REFRESH) { mRefreshViewText .setText(R.string.xpull_to_refresh_release_label); mRefreshViewImage.clearAnimation(); mRefreshViewImage.startAnimation(mFlipAnimation); mRefreshState = RELEASE_TO_REFRESH; } else if (mRefreshView.getBottom() < mRefreshViewHeight + 20 && mRefreshState != PULL_TO_REFRESH) { mRefreshViewText .setText(R.string.xpull_to_refresh_pull_label); if (mRefreshState != TAP_TO_REFRESH) { mRefreshViewImage.clearAnimation(); mRefreshViewImage.startAnimation(mReverseFlipAnimation); } mRefreshState = PULL_TO_REFRESH; } } else { mRefreshViewImage.setVisibility(View.GONE); resetHeader(); } } else if (mCurrentScrollState == SCROLL_STATE_FLING && firstVisibleItem == 0 && mRefreshState != REFRESHING) { setSelection(1); mBounceHack = true; } else if (mBounceHack && mCurrentScrollState == SCROLL_STATE_FLING) { setSelection(1); } if (mOnScrollListener != null) { mOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount); } } public void onScrollStateChanged(AbsListView view, int scrollState) { mCurrentScrollState = scrollState; if (mCurrentScrollState == SCROLL_STATE_IDLE) { mBounceHack = false; } if (mOnScrollListener != null) { mOnScrollListener.onScrollStateChanged(view, scrollState); } } public void prepareForRefresh() { resetHeaderPadding(); mRefreshViewImage.setVisibility(View.GONE); // We need this hack, otherwise it will keep the previous drawable. mRefreshViewImage.setImageDrawable(null); mRefreshViewProgress.setVisibility(View.VISIBLE); // Set refresh view text to the refreshing label mRefreshViewText.setText(R.string.xpull_to_refresh_refreshing_label); mRefreshState = REFRESHING; } public void onRefresh() { if (mOnRefreshListener != null) { mOnRefreshListener.onRefresh(); } } /** * manually refresh */ public void toRefresh() { if (canRefresh && mRefreshState != REFRESHING) { prepareForRefresh(); onRefresh(); } } /** * set whether it's able to refresh with text to tell the user it's able to * refresh * * @param canRefresh * able to refresh * @param text * word to setText() to tell the user if it's able to refresh<br> * */ public void setCanRefresh(boolean canRefresh, String text) { this.canRefresh = canRefresh; if (text != null) mRefreshViewText.setText(text); else mRefreshViewText .setText(R.string.xpull_to_refresh_have_no_more_to_refresh); } /** * set whether it's able to refresh<br> * you <strong>have to</strong> called this method <strong>after</strong> * {@link#onRefreshComplete()} * * @param canRefrsh * able to refresh */ public void setCanRefresh(boolean canRefrsh) { setCanRefresh(canRefrsh, null); } /** * <li>set whether it's able to refresh<br> <li>set whether it's able to * show the headView when it can't refresh * * @see {@link#onRefreshComplete()} {@link#setCanRefresh()} * @param canRefresh * @param ableToShowHeadView */ public void setCanRefresh(boolean canRefresh, boolean ableToShowHeadView) { setCanRefresh(canRefresh, null); this.ableToShowHeadView = ableToShowHeadView; } /** * Resets the list to a normal state after a refresh. * * @param lastUpdated * Last updated at. */ public void onRefreshComplete(CharSequence lastUpdated) { setLastUpdated(lastUpdated); onRefreshComplete(); } /** * Resets the list to a normal state after a refresh. */ public void onRefreshComplete() { resetHeader(); // If refresh view is visible when loading completes, scroll down to // the next item. if (mRefreshView.getBottom() > 0) { invalidateViews(); setSelection(1); } // if it can't reRefresh the remove this head view // but if you want to make it work again,called `reInit()` if (!canRefresh && !ableToShowHeadView) { mRefreshView.setPadding(mRefreshView.getPaddingLeft(), -1 * mRefreshViewHeight, mRefreshView.getPaddingRight(), mRefreshView.getPaddingBottom()); } } /** * Invoked when the refresh view is clicked on. This is mainly used when * there's only a few items in the list and it's not possible to drag the * list. */ private class OnClickRefreshListener implements OnClickListener { public void onClick(View v) { toRefresh(); } } /** * Interface definition for a callback to be invoked when list should be * refreshed. */ public interface OnRefreshListener { /** * Called when the list should be refreshed. * <p> * A call to {@link PullToRefreshListView #onRefreshComplete()} is * expected to indicate that the refresh has completed. */ public void onRefresh(); } }