Android中listView的底部拖拉翻页的实现

 在twitter微博中的, 往下拉列表的话, 顶部会自动load, 而且有反弹的效果,非常cool。 但是它的自动load顶部的内部, 我现在需要拖拉listview到最后的时候load。这个组件不知道方向的选择, 所以自己就动手改了下,实现从底部load。 (今天发现新浪微博也实现了同样的功能。)  。

Java代码  
  1. package com.markupartist.android.widget;   
  2.   
  3.   
  4. import java.lang.reflect.InvocationTargetException;   
  5. import java.lang.reflect.Method;   
  6.   
  7. import android.content.Context;   
  8. import android.util.AttributeSet;   
  9. import android.util.Log;   
  10. import android.view.LayoutInflater;   
  11. import android.view.MotionEvent;   
  12. import android.view.View;   
  13. import android.view.ViewGroup;   
  14. import android.view.animation.LinearInterpolator;   
  15. import android.view.animation.RotateAnimation;   
  16. import android.widget.AbsListView;   
  17. import android.widget.ImageView;   
  18. import android.widget.RelativeLayout;   
  19. import android.widget.ListAdapter;   
  20. import android.widget.ListView;   
  21. import android.widget.ProgressBar;   
  22. import android.widget.TextView;   
  23. import android.widget.AbsListView.OnScrollListener;   
  24.   
  25. import com.markupartist.android.widget.pulltorefresh.R;   
  26.   
  27. public class PullToRefreshListView extends ListView implements OnScrollListener {   
  28.   
  29.     private static final int TAP_TO_REFRESH = 1;   
  30.     private static final int PULL_TO_REFRESH = 2;   
  31.     private static final int RELEASE_TO_REFRESH = 3;   
  32.     private static final int REFRESHING = 4;   
  33.   
  34.     private static final String TAG = "PullToRefreshListView";   
  35.   
  36.     private OnRefreshListener mOnRefreshListener;   
  37.   
  38.     /**  
  39.      * Listener that will receive notifications every time the list scrolls. 
  40.      */  
  41.     private OnScrollListener mOnScrollListener;   
  42.     private LayoutInflater mInflater;   
  43.   
  44.     private RelativeLayout mRefreshView;   
  45.     private TextView mRefreshViewText;   
  46.     private ImageView mRefreshViewImage;   
  47.     private ProgressBar mRefreshViewProgress;   
  48.     private TextView mRefreshViewLastUpdated;   
  49.   
  50.     private int mCurrentScrollState;   
  51.     private int mRefreshState;   
  52.   
  53.     private RotateAnimation mFlipAnimation;   
  54.     private RotateAnimation mReverseFlipAnimation;   
  55.   
  56.     private int mRefreshViewHeight;   
  57.     private int mRefreshOriginalTopPadding;   
  58.     private int mRefreshOriginalBottomPadding;   
  59.     private int mLastMotionY;   
  60.   
  61.     public PullToRefreshListView(Context context) {   
  62.         super(context);   
  63.         init(context);   
  64.     }   
  65.   
  66.     public PullToRefreshListView(Context context, AttributeSet attrs) {   
  67.         super(context, attrs);   
  68.         init(context);   
  69.     }   
  70.   
  71.     public PullToRefreshListView(Context context, AttributeSet attrs, int defStyle) {   
  72.         super(context, attrs, defStyle);   
  73.         init(context);   
  74.     }   
  75.   
  76.     private void init(Context context) {   
  77.         // Load all of the animations we need in code rather than through XML  
  78.         mFlipAnimation = new RotateAnimation(0, -180,   
  79.                 RotateAnimation.RELATIVE_TO_SELF, 0.5f,   
  80.                 RotateAnimation.RELATIVE_TO_SELF, 0.5f);   
  81.         mFlipAnimation.setInterpolator(new LinearInterpolator());   
  82.         mFlipAnimation.setDuration(250);   
  83.         mFlipAnimation.setFillAfter(true);   
  84.         mReverseFlipAnimation = new RotateAnimation(-1800,   
  85.                 RotateAnimation.RELATIVE_TO_SELF, 0.5f,   
  86.                 RotateAnimation.RELATIVE_TO_SELF, 0.5f);   
  87.         mReverseFlipAnimation.setInterpolator(new LinearInterpolator());   
  88.         mReverseFlipAnimation.setDuration(250);   
  89.         mReverseFlipAnimation.setFillAfter(true);   
  90.   
  91.         mInflater = (LayoutInflater) context.getSystemService(   
  92.                 Context.LAYOUT_INFLATER_SERVICE);   
  93.   
  94.         mRefreshView = (RelativeLayout) mInflater.inflate(   
  95.                 R.layout.pull_to_refresh_header, thisfalse);   
  96.         mRefreshViewText =   
  97.             (TextView) mRefreshView.findViewById(R.id.pull_to_refresh_text);   
  98.         mRefreshViewImage =   
  99.             (ImageView) mRefreshView.findViewById(R.id.pull_to_refresh_image);   
  100.         mRefreshViewProgress =   
  101.             (ProgressBar) mRefreshView.findViewById(R.id.pull_to_refresh_progress);   
  102.         mRefreshViewLastUpdated =   
  103.             (TextView) mRefreshView.findViewById(R.id.pull_to_refresh_updated_at);   
  104.   
  105.         mRefreshViewImage.setMinimumHeight(50);   
  106.         mRefreshView.setOnClickListener(new OnClickRefreshListener());   
  107.         mRefreshOriginalTopPadding = mRefreshView.getPaddingTop();   
  108.         mRefreshOriginalBottomPadding = mRefreshView.getPaddingBottom();   
  109.   
  110.         mRefreshState = TAP_TO_REFRESH;   
  111.   
  112. //        addHeaderView(mRefreshView);   
  113.         addFooterView(mRefreshView);   
  114.   
  115.         super.setOnScrollListener(this);   
  116.   
  117.         measureView(mRefreshView);   
  118.         mRefreshViewHeight = mRefreshView.getMeasuredHeight();   
  119.     }   
  120.   
  121.     @Override  
  122.     protected void onAttachedToWindow() {   
  123.         //setSelection(1);   
  124.     }   
  125.   
  126.     @Override  
  127.     public void setAdapter(ListAdapter adapter) {   
  128.         super.setAdapter(adapter);   
  129.   
  130.         //setSelection(1);   
  131.     }   
  132.   
  133.     /**  
  134.      * Set the listener that will receive notifications every time the list 
  135.      * scrolls.  
  136.      *   
  137.      * @param l The scroll listener.   
  138.      */  
  139.     @Override  
  140.     public void setOnScrollListener(AbsListView.OnScrollListener l) {   
  141.         mOnScrollListener = l;   
  142.     }   
  143.   
  144.     /**  
  145.      * Register a callback to be invoked when this list should be refreshed. 
  146.      *   
  147.      * @param onRefreshListener The callback to run. 
  148.      */  
  149.     public void setOnRefreshListener(OnRefreshListener onRefreshListener) {   
  150.         mOnRefreshListener = onRefreshListener;   
  151.     }   
  152.   
  153.     /**  
  154.      * Set a text to represent when the list was last updated.  
  155.      * @param lastUpdated Last updated at.  
  156.      */  
  157.     public void setLastUpdated(CharSequence lastUpdated) {   
  158.         if (lastUpdated != null) {   
  159.             mRefreshViewLastUpdated.setVisibility(View.VISIBLE);   
  160.             mRefreshViewLastUpdated.setText(lastUpdated);   
  161.         } else {   
  162.             mRefreshViewLastUpdated.setVisibility(View.GONE);   
  163.         }   
  164.     }   
  165.   
  166.     @Override  
  167.     public boolean onTouchEvent(MotionEvent event) {   
  168.         final int y = (int) event.getY();   
  169.         Log.i("PullToRefreshListView""Height:" + getMeasuredHeight());   
  170.         Log.i("PullToRefreshListView""MotionEvent:" + event.getAction());   
  171.         Log.i("PullToRefreshListView""mRefreshState:" + mRefreshState);   
  172.         Log.i("PullToRefreshListView""FirstVisiblePosition:" + getFirstVisiblePosition());   
  173.         Log.i("PullToRefreshListView""LastVisiblePosition:" + getLastVisiblePosition());   
  174.         Log.i("PullToRefreshListView""ChildCount:" + getAdapter().getCount());   
  175.         Log.i("PullToRefreshListView""RefreshViewBottom:" + mRefreshView.getBottom());   
  176.         Log.i("PullToRefreshListView""RefreshViewTop:" + mRefreshView.getTop());   
  177.         Log.i("PullToRefreshListView""mRefreshViewHeight:" + mRefreshViewHeight);   
  178.         Log.i("PullToRefreshListView""  ");   
  179.         Log.i("PullToRefreshListView""  ");   
  180.         Log.i("PullToRefreshListView""  ");   
  181.   
  182.   
  183.         switch (event.getAction()) {   
  184.             case MotionEvent.ACTION_UP:   
  185.   
  186.                 if (!isVerticalScrollBarEnabled()) {   
  187.                     setVerticalScrollBarEnabled(true);   
  188.                 }   
  189.                 if (getLastVisiblePosition() == getAdapter().getCount() - 1  
  190.                         && mRefreshState != REFRESHING) {   
  191.                     if ((   
  192.                             mRefreshView.getTop() <= getMeasuredHeight() - mRefreshViewHeight)   
  193.                             && mRefreshState == RELEASE_TO_REFRESH) {   
  194. //                        Log.i("PullToRefreshListView", "Fire...");  
  195. //                        Log.i("PullToRefreshListView", "  ");  
  196. //                        Log.i("PullToRefreshListView", "  ");  
  197. //                        Log.i("PullToRefreshListView", "  ");  
  198.                         // Initiate the refresh  
  199.                         mRefreshState = REFRESHING;   
  200.                         prepareForRefresh();   
  201.                         onRefresh();   
  202.                     } else if (   
  203.                             mRefreshView.getTop() > getMeasuredHeight() - mRefreshViewHeight) {   
  204.                         // Abort refresh and scroll down below the refresh view  
  205.                         resetHeader();   
  206.                         //setSelection(1);  
  207. //                        Log.v("PPMacbook", "pi:" + (getSelectedItemPosition() - 1));  
  208. //                        setSelectionAfterHeaderView();  
  209. //                        setSelection(getSelectedItemPosition() - 1);  
  210. //                        System.out.println("PPMacbook:" + (getFirstVisiblePosition() - 1));  
  211.                         setSelection((getFirstVisiblePosition() - 1));   
  212.                     }   
  213.                 }   
  214.                 break;   
  215.   
  216.   
  217.             case MotionEvent.ACTION_DOWN:   
  218.                 mLastMotionY = y;   
  219.                 break;   
  220.   
  221.             case MotionEvent.ACTION_MOVE:   
  222.                 applyHeaderPadding(event);   
  223.                 break;   
  224.         }   
  225.         return super.onTouchEvent(event);   
  226.     }   
  227.   
  228.     private void applyHeaderPadding(MotionEvent ev) {   
  229.         final int historySize = ev.getHistorySize();   
  230.   
  231.         // Workaround for getPointerCount() which is unavailable in 1.5  
  232.         // (it's always 1 in 1.5)   
  233.         int pointerCount = 1;   
  234.         try {   
  235.             Method method = MotionEvent.class.getMethod("getPointerCount");   
  236.             pointerCount = (Integer)method.invoke(ev);   
  237.         } catch (NoSuchMethodException e) {   
  238.             pointerCount = 1;   
  239.         } catch (IllegalArgumentException e) {   
  240.             throw e;   
  241.         } catch (IllegalAccessException e) {   
  242.             System.err.println("unexpected " + e);   
  243.         } catch (InvocationTargetException e) {   
  244.             System.err.println("unexpected " + e);   
  245.         }   
  246.   
  247. //        Log.i("PullToRefreshListView", "historySize:" + historySize);  
  248. //        Log.i("PullToRefreshListView", "pointerCount:" + pointerCount);  
  249. //        Log.i("PullToRefreshListView", "  ");  
  250. //        Log.i("PullToRefreshListView", "  ");  
  251. //        Log.i("PullToRefreshListView", "  ");  
  252.   
  253.         for (int h = 0; h < historySize; h++) {   
  254.             for (int p = 0; p < pointerCount; p++) {   
  255.                 if (mRefreshState == RELEASE_TO_REFRESH) {   
  256.                     if (isVerticalFadingEdgeEnabled()) {   
  257.                         setVerticalScrollBarEnabled(false);   
  258.                     }   
  259.   
  260.                     int historicalY = 0;   
  261.                     try {   
  262.                         // For Android > 2.0  
  263.                         Method method = MotionEvent.class.getMethod(   
  264.                                 "getHistoricalY", Integer.TYPE, Integer.TYPE);   
  265.                         historicalY = ((Float) method.invoke(ev, p, h)).intValue();   
  266.                     } catch (NoSuchMethodException e) {   
  267.                         // For Android < 2.0  
  268.                         historicalY = (int) (ev.getHistoricalY(h));   
  269.                     } catch (IllegalArgumentException e) {   
  270.                         throw e;   
  271.                     } catch (IllegalAccessException e) {   
  272.                         System.err.println("unexpected " + e);   
  273.                     } catch (InvocationTargetException e) {   
  274.                         System.err.println("unexpected " + e);   
  275.                     }   
  276.   
  277.                     // Calculate the padding to apply, we divide by 1.7 to  
  278.                     // simulate a more resistant effect during pull.  
  279.                     int topPadding = (int) (((historicalY + mLastMotionY)   
  280.                             + mRefreshViewHeight) / 1.7);   
  281.   
  282.                     mRefreshView.setPadding(   
  283.                             mRefreshView.getPaddingLeft(),   
  284.                             mRefreshView.getPaddingTop(),   
  285.                             mRefreshView.getPaddingRight(),   
  286.                             topPadding);   
  287.                 }   
  288.             }   
  289.         }   
  290.     }   
  291.   
  292.     /**  
  293.      * Sets the header padding back to original size. 
  294.      */  
  295.     private void resetHeaderPadding() {   
  296.         mRefreshView.setPadding(   
  297.                 mRefreshView.getPaddingLeft(),   
  298.                 mRefreshView.getPaddingTop(),   
  299.                 mRefreshView.getPaddingRight(),   
  300.                 mRefreshOriginalBottomPadding);   
  301.     }   
  302.   
  303.     /**  
  304.      * Resets the header to the original state.  
  305.      */  
  306.     private void resetHeader() {   
  307.         if (mRefreshState != TAP_TO_REFRESH) {   
  308.             mRefreshState = TAP_TO_REFRESH;   
  309.   
  310.             resetHeaderPadding();   
  311.   
  312.             // Set refresh view text to the pull label  
  313.             mRefreshViewText.setText(R.string.pull_to_refresh_tap_label);   
  314.             // Replace refresh drawable with arrow drawable  
  315.             mRefreshViewImage.setImageResource(R.drawable.ic_pulltorefresh_arrow);   
  316.             // Clear the full rotation animation  
  317.             mRefreshViewImage.clearAnimation();   
  318.             // Hide progress bar and arrow.  
  319.             mRefreshViewImage.setVisibility(View.GONE);   
  320.             mRefreshViewProgress.setVisibility(View.GONE);   
  321.         }   
  322.     }   
  323.   
  324.     private void measureView(View child) {   
  325.         ViewGroup.LayoutParams p = child.getLayoutParams();   
  326.         if (p == null) {   
  327.             p = new ViewGroup.LayoutParams(   
  328.                     ViewGroup.LayoutParams.FILL_PARENT,   
  329.                     ViewGroup.LayoutParams.WRAP_CONTENT);   
  330.         }   
  331.   
  332.         int childWidthSpec = ViewGroup.getChildMeasureSpec(0,   
  333.                 0 + 0, p.width);   
  334.         int lpHeight = p.height;   
  335.         int childHeightSpec;   
  336.         if (lpHeight > 0) {   
  337.             childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);   
  338.         } else {   
  339.             childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);   
  340.         }   
  341.         child.measure(childWidthSpec, childHeightSpec);   
  342.     }   
  343.   
  344.     @Override  
  345.     public void onScroll(AbsListView view, int firstVisibleItem,   
  346.             int visibleItemCount, int totalItemCount) {   
  347. //        Log.i("PullToRefreshListView", "firstVisibleItem:" + firstVisibleItem);  
  348. //        Log.i("PullToRefreshListView", "visibleItemCount:" + visibleItemCount);  
  349. //        Log.i("PullToRefreshListView", "totalItemCount:" + totalItemCount);  
  350. //        Log.i("PullToRefreshListView", "");  
  351. //        Log.i("PullToRefreshListView", "");  
  352. //        Log.i("PullToRefreshListView", "");  
  353.         // When the refresh view is completely visible, change the text to say  
  354.         // "Release to refresh..." and flip the arrow drawable.  
  355.         if (mCurrentScrollState == SCROLL_STATE_TOUCH_SCROLL   
  356.                 && mRefreshState != REFRESHING) {   
  357.             if (firstVisibleItem + visibleItemCount == totalItemCount) {   
  358.                 mRefreshViewImage.setVisibility(View.VISIBLE);   
  359.                 if ((   
  360.                         mRefreshView.getTop() <= getMeasuredHeight() - mRefreshViewHeight)   
  361.                         && mRefreshState != RELEASE_TO_REFRESH) {   
  362.                     mRefreshViewText.setText(R.string.pull_to_refresh_release_label);   
  363.                     mRefreshViewImage.clearAnimation();   
  364.                     mRefreshViewImage.startAnimation(mFlipAnimation);   
  365.                     mRefreshState = RELEASE_TO_REFRESH;   
  366.                 } else if (   
  367.                         mRefreshView.getTop() >getMeasuredHeight() - 20 - mRefreshViewHeight   
  368.                         && mRefreshState != PULL_TO_REFRESH) {   
  369.                     mRefreshViewText.setText(R.string.pull_to_refresh_pull_label);   
  370.                     if (mRefreshState != TAP_TO_REFRESH) {   
  371.                         mRefreshViewImage.clearAnimation();   
  372.                         mRefreshViewImage.startAnimation(mReverseFlipAnimation);   
  373.                     }   
  374.                     mRefreshState = PULL_TO_REFRESH;   
  375.                 }   
  376.             } else {   
  377.                 mRefreshViewImage.setVisibility(View.GONE);   
  378.                 resetHeader();   
  379.             }   
  380.         } else if (mCurrentScrollState == SCROLL_STATE_FLING   
  381.                 && firstVisibleItem + visibleItemCount == totalItemCount   
  382.                 && mRefreshState != REFRESHING) {   
  383.             //setSelection(1);   
  384. //            Log.i("PullToRefreshListView", "Selection:" + (totalItemCount - visibleItemCount));  
  385.             setSelection(getSelectedItemPosition() - 2);  
  386. //   
  387.             setSelection((getFirstVisiblePosition() - 1));   
  388.         }   
  389.   
  390.         if (mOnScrollListener != null) {   
  391.             mOnScrollListener.onScroll(view, firstVisibleItem,   
  392.                     visibleItemCount, totalItemCount);   
  393.         }   
  394.     }   
  395.   
  396.     @Override  
  397.     public void onScrollStateChanged(AbsListView view, int scrollState) {   
  398.         mCurrentScrollState = scrollState;   
  399.   
  400.         if (mOnScrollListener != null) {   
  401.             mOnScrollListener.onScrollStateChanged(view, scrollState);   
  402.         }   
  403.     }   
  404.   
  405.     public void prepareForRefresh() {   
  406.         resetHeaderPadding();   
  407.   
  408.         mRefreshViewImage.setVisibility(View.GONE);   
  409.         // We need this hack, otherwise it will keep the previous drawable.  
  410.         mRefreshViewImage.setImageDrawable(null);   
  411.         mRefreshViewProgress.setVisibility(View.VISIBLE);   
  412.   
  413.         // Set refresh view text to the refreshing label  
  414.         mRefreshViewText.setText(R.string.pull_to_refresh_refreshing_label);   
  415.   
  416.         mRefreshState = REFRESHING;   
  417.     }   
  418.   
  419.     public void onRefresh() {   
  420.         Log.d(TAG, "onRefresh");   
  421.   
  422.         if (mOnRefreshListener != null) {   
  423.             mOnRefreshListener.onRefresh();   
  424.         }   
  425.     }   
  426.   
  427.     /**  
  428.      * Resets the list to a normal state after a refresh. 
  429.      * @param lastUpdated Last updated at.  
  430.      */  
  431.     public void onRefreshComplete(CharSequence lastUpdated) {   
  432.         setLastUpdated(lastUpdated);   
  433.         onRefreshComplete();   
  434.     }   
  435.   
  436.     /**  
  437.      * Resets the list to a normal state after a refresh. 
  438.      */  
  439.     public void onRefreshComplete() {           
  440.         Log.d(TAG, "onRefreshComplete");   
  441.   
  442.         resetHeader();   
  443.   
  444.         // If refresh view is visible when loading completes, scroll down to  
  445.         // the next item.   
  446.         if (mRefreshView.getBottom() > 0) {   
  447.             invalidateViews();   
  448.             //setSelection(1);   
  449.         }   
  450.     }   
  451.   
  452.     /**  
  453.      * Invoked when the refresh view is clicked on. This is mainly used when 
  454.      * there's only a few items in the list and it's not possible to drag the 
  455.      * list.  
  456.      */  
  457.     private class OnClickRefreshListener implements OnClickListener {   
  458.   
  459.         @Override  
  460.         public void onClick(View v) {   
  461.             if (mRefreshState != REFRESHING) {   
  462.                 prepareForRefresh();   
  463.                 onRefresh();   
  464.             }   
  465.         }   
  466.   
  467.     }   
  468.   
  469.     /**  
  470.      * Interface definition for a callback to be invoked when list should be 
  471.      * refreshed.  
  472.      */  
  473.     public interface OnRefreshListener {   
  474.         /**  
  475.          * Called when the list should be refreshed. 
  476.          * <p>  
  477.          * A call to {@link PullToRefreshListView #onRefreshComplete()} is 
  478.          * expected to indicate that the refresh has completed. 
  479.          */  
  480.         public void onRefresh();   
  481.     }   
  482. }  
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值