android ScrollView,GridView,ListView,ViewGroup均可上拉刷新的控件XScrollView(项目必用)

android ScrollView,GridView,ListView,ViewGroup均可下拉刷新的控件XScrollView(项目必用)

先上图,看看是不是你要的效果? 是,继续走!


android  ScrollView,GridView,ListView,ViewGroup均可上拉刷新的控件XScrollView(项目必用)

实现该功能需要实现注意三点:


1:下拉刷新的效果实现;
2:scrollview中图片画廊的添加(不用管你是gallery还是gridview)
3:上推刷新的时候,底部转圈等待的效果,没有数据显示加载完毕功能;

(代码在:http://download.csdn.net/my/uploads )



具体实现:

1:先说使用方法:
public class XScrollViewActivity extends Activity {
     private XScrollView mScrollView;
     private Handler mHandler;
     private Context mContext = XScrollViewActivity.this;

     /** Called when the activity is first created. */
     @Override
     public void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.main);
          mScrollView = (XScrollView) findViewById(R.id.xListView);
          mScrollView.setPullLoadEnable(true);
          mScrollView.setIXScrollViewListener(mIXScrollViewListener);
          mHandler = new Handler();
     }

     private void onLoad() {
          mScrollView.stopRefresh();
          mScrollView.stopLoadMore();
          mScrollView.setRefreshTime("刚刚");
     }

     IXScrollViewListener mIXScrollViewListener = new IXScrollViewListener() {
          @Override
          public void onRefresh() {
               mHandler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                         TextView mTextView = new TextView(mContext);
                         mTextView.setText("YAMAHA");
                         mScrollView.setView(mTextView);
                         onLoad();
                    }
               }, 2000);
          }

          @Override
          public void onLoadMore() {
               mHandler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                         TextView mTextView = new TextView(mContext);
                         mTextView.setText("YAMAHA");
                         mScrollView.setView(mTextView);
                         onLoad();
                    }
               }, 2000);
          }
     };

}

2:布局引用:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" android:background="#f0f0f0">

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello" />

<!--     <me.maxwin.view.XListView -->
<!--         android:id="@+id/xListView" -->
<!--         android:layout_width="fill_parent" -->
<!--         android:layout_height="fill_parent" android:cacheColorHint="#00000000"> -->
<!--     </me.maxwin.view.XListView> -->


    <me.maxwin.view.XScrollView
        android:id="@+id/xListView"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" android:cacheColorHint="#00000000">
    </me.maxwin.view.XScrollView>

</LinearLayout>


3:看封装的控件

public class XScrollView extends ScrollView implements OnScrollListener {

     private float mLastY = -1; // save event y

     /**
     * 这个类封装了滚动操作。 滚动的持续时间可以通过构造函数传递,并且可以指定滚动动作的持续的最长时间。
     * 经过这段时间,滚动会自动定位到最终位置,并且通过computeScrollOffset()会得到的返回值为false,表明滚动动作已经结束。
     **/
     private Scroller mScroller; // used for scroll back

     /**
     * 当屏幕停止滚动时为0; 当屏幕滚动且用户使用的触碰或手指还在屏幕上时为1; 由于用户的操作,屏幕产生惯性滑动时为2
     */
     private OnScrollListener mScrollListener; // user's scroll listener

     // the interface to trigger refresh and load more.
     private IXScrollViewListener mScorllViewListener;

     // -- header view
     private XListViewHeader mHeaderView;
     // header view content, use it to calculate the Header's height. And hide it
     // when disable pull refresh.
     private RelativeLayout mHeaderViewContent;
     private TextView mHeaderTimeView;
     private int mHeaderViewHeight; // header view's height
     private boolean mEnablePullRefresh = true;
     private boolean mPullRefreshing = false; // is refreashing.

     private LinearLayout mScrollView;
     private LinearLayout mContentView;

     // -- footer view
     private XListViewFooter mFooterView;
     private boolean mEnablePullLoad;
     private boolean mPullLoading;

     // total list items, used to detect is at the bottom of listview.
     private int mTotalItemCount;

     // for mScroller, scroll back from header or footer.
     private int mScrollBack;
     private final static int SCROLLBACK_HEADER = 0;
     private final static int SCROLLBACK_FOOTER = 1;

     private final static int SCROLL_DURATION = 400; // scroll back duration
     private final static int PULL_LOAD_MORE_DELTA = 50; // when pull up >= 50px
     // at bottom, trigger
     // load more.
     private final static float OFFSET_RADIO = 1.8f; // support iOS like pull
     // feature.

     public XScrollView(Context context, AttributeSet attrs) {
          super(context, attrs);
          Log.e("LIFE", "initWithContext");
          initWithContext(context);
     }

     private void initWithContext(Context context) {
          mScrollView = (LinearLayout) View.inflate(context,
                    R.layout.xscrollview_layout, null);

          mContentView = (LinearLayout) mScrollView.findViewById(R.id.sc_layout);
          /**
          * http://www.cnblogs.com/feisky/archive/2010/01/11/1644482.html详解:
          * DecelerateInterpolator
          */
          mScroller = new Scroller(context, new DecelerateInterpolator());
          // XListView need the scroll event, and it will dispatch the event to
          // user's listener (as a proxy).
          this.setOnScrollListener(this);

          // init header view
          mHeaderView = new XListViewHeader(context);
          mHeaderViewContent = (RelativeLayout) mHeaderView
                    .findViewById(R.id.xlistview_header_content);
          mHeaderTimeView = (TextView) mHeaderView
                    .findViewById(R.id.xlistview_header_time);
          addHeaderView(mHeaderView);

          // init footer view
          mFooterView = new XListViewFooter(context);
          addFooterView(mFooterView);

          // init header height
          /**
          * 有时候需要在onCreate方法中知道某个View组件的宽度和高度等信息,而直接调用View组件的getWidth()、getHeight
          * ()、getMeasuredWidth()、getMeasuredHeight()、getTop()、getLeft()
          * 等方法是无法获取到真实值的,只会得到0。这是因为View组件布局要在onResume回调后完成。下面提供实现方法,
          * onGlobalLayout回调会在布局完成时自动调用
          */
          mHeaderView.getViewTreeObserver().addOnGlobalLayoutListener(
                    new OnGlobalLayoutListener() {
                         @Override
                         public void onGlobalLayout() {
                              mHeaderViewHeight = mHeaderViewContent.getHeight();
                              getViewTreeObserver()
                              .removeGlobalOnLayoutListener(this);
                              Log.v("onGlobalLayout",
                                        "-----------removeGlobalOnLayoutListener--------------");
                         }
                    });
          this.addView(mScrollView);
     }

     private void addHeaderView(XListViewHeader mHeaderView) {
          if (mScrollView == null) {
               return;
          }
          LinearLayout mHeadLayout = (LinearLayout) mScrollView
                    .findViewById(R.id.sc_head);
          mHeadLayout.addView(mHeaderView);
     }

     private void addFooterView(XListViewFooter mFooterView) {
          if (mScrollView == null) {
               return;
          }
          LinearLayout mFootLayout = (LinearLayout) mScrollView
                    .findViewById(R.id.sc_foot);
          mFootLayout.addView(mFooterView);
     }

     /**
     * 设置ScrollView 中间容器的View;
     * @param contentView  要添加的ViewGroup
     */
     public void setContentView(ViewGroup contentView) {
          if (mScrollView == null) {
               return;
          }
          if(mContentView == null){
               mContentView = (LinearLayout) mScrollView.findViewById(R.id.sc_layout);
          }

          if(mContentView.getChildCount() > 0){
               mContentView.removeAllViews();
          }
          mContentView.addView(contentView);
     }

     /**
     * 设置ScrollView 中间容器的View;
     * @param contentView  要添加的ViewGroup
     */
     public void setView(View contentView) {
          if (mScrollView == null) {
               return;
          }
          if(mContentView == null){
               mContentView = (LinearLayout) mScrollView.findViewById(R.id.sc_layout);
          }
          mContentView.addView(contentView);
     }
     
     
     
     /**
     * enable or disable pull down refresh feature.
     * @param enable
     */
     public void setPullRefreshEnable(boolean enable) {
          mEnablePullRefresh = enable;
          if (!mEnablePullRefresh) { // disable, hide the content
               mHeaderViewContent.setVisibility(View.INVISIBLE);
          } else {
               mHeaderViewContent.setVisibility(View.VISIBLE);
          }
     }

     /**
     * enable or disable pull up load more feature.
     * @param enable
     */
     public void setPullLoadEnable(boolean enable) {
          mEnablePullLoad = enable;
          if (!mEnablePullLoad) {
               mFooterView.hide();
               mFooterView.setOnClickListener(null);
          } else {
               mPullLoading = false;
               mFooterView.show();
               mFooterView.setState(XListViewFooter.STATE_NORMAL);
               // both "pull up" and "click" will invoke load more.
               mFooterView.setOnClickListener(new OnClickListener() {
                    @Override
                    public void onClick(View v) {
                         startLoadMore();
                    }
               });
          }
     }

     /**
     * stop refresh, reset header view.
     */
     public void stopRefresh() {
          if (mPullRefreshing == true) {
               mPullRefreshing = false;
               resetHeaderHeight();
          }
     }

     /**
     * stop load more, reset footer view.
     */
     public void stopLoadMore() {
          if (mPullLoading == true) {
               mPullLoading = false;
               mFooterView.setState(XListViewFooter.STATE_NORMAL);
          }
     }

     /**
     * set last refresh time
     * 
     * @param time
     */
     public void setRefreshTime(String time) {
          mHeaderTimeView.setText(time);
     }

     private void invokeOnScrolling() {
          if (mScrollListener instanceof OnXScrollListener) {
               OnXScrollListener l = (OnXScrollListener) mScrollListener;
               l.onXScrolling(this);
          }
     }

     private void updateHeaderHeight(float delta) {
          mHeaderView.setVisiableHeight((int) delta
                    + mHeaderView.getVisiableHeight());
          if (mEnablePullRefresh && !mPullRefreshing) { // 未处于刷新状态,更新箭头
               if (mHeaderView.getVisiableHeight() > mHeaderViewHeight) {
                    mHeaderView.setState(XListViewHeader.STATE_READY);
               } else {
                    mHeaderView.setState(XListViewHeader.STATE_NORMAL);
               }
          }
          // setSelection(0); // scroll to top each time
     }

     /**
     * reset header view's height.
     */
     private void resetHeaderHeight() {
          int height = mHeaderView.getVisiableHeight();
          if (height == 0) // not visible.
               return;
          // refreshing and header isn't shown fully. do nothing.
          if (mPullRefreshing && height <= mHeaderViewHeight) {
               return;
          }
          int finalHeight = 0; // default: scroll back to dismiss header.
          // is refreshing, just scroll back to show all the header.
          if (mPullRefreshing && height > mHeaderViewHeight) {
               finalHeight = mHeaderViewHeight;
          }
          mScrollBack = SCROLLBACK_HEADER;
          mScroller.startScroll(0, height, 0, finalHeight - height,
                    SCROLL_DURATION);
          // trigger computeScroll
          invalidate();
     }

     private void updateFooterHeight(float delta) {
          int height = mFooterView.getBottomMargin() + (int) delta;
          if (mEnablePullLoad && !mPullLoading) {
               if (height > PULL_LOAD_MORE_DELTA) { // height enough to invoke load
                    // more.
                    mFooterView.setState(XListViewFooter.STATE_READY);
               } else {
                    mFooterView.setState(XListViewFooter.STATE_NORMAL);
               }
          }
          mFooterView.setBottomMargin(height);

          // setSelection(mTotalItemCount - 1); // scroll to bottom
     }

     private void resetFooterHeight() {
          int bottomMargin = mFooterView.getBottomMargin();
          if (bottomMargin > 0) {
               mScrollBack = SCROLLBACK_FOOTER;
               mScroller.startScroll(0, bottomMargin, 0, -bottomMargin,
                         SCROLL_DURATION);
               invalidate();
          }
     }

     private void startLoadMore() {
          mPullLoading = true;
          mFooterView.setState(XListViewFooter.STATE_LOADING);
          if (mScorllViewListener != null) {
               mScorllViewListener.onLoadMore();
          }
     }

     @Override
     public boolean onTouchEvent(MotionEvent ev) {
          Log.e("LIFE", "onTouchEvent(MotionEvent ev)");
          if (mLastY == -1) {
               /**
               * getX()是表示Widget相对于自身左上角的x坐标,而getRawX()是表示相对于屏幕左上角的x坐标值
               * (注意:这个屏幕左上角是手机屏幕左上角,不管activity是否有titleBar或是否全屏幕)
               */
               mLastY = ev.getRawY();
          }

          switch (ev.getAction()) {
          case MotionEvent.ACTION_DOWN:
               mLastY = ev.getRawY();
               break;
          case MotionEvent.ACTION_MOVE:
               final float deltaY = ev.getRawY() - mLastY;
               mLastY = ev.getRawY();
               if (getFirstVisiblePosition() == 0
                         && (mHeaderView.getVisiableHeight() > 0 || deltaY > 0)) {
                    // the first item is showing, header has shown or pull down.
                    updateHeaderHeight(deltaY / OFFSET_RADIO);
                    invokeOnScrolling();
               } else if (getLastVisiblePosition() == mTotalItemCount - 1
                         && (mFooterView.getBottomMargin() > 0 || deltaY < 0)) {
                    // last item, already pulled up or want to pull up.
                    updateFooterHeight(-deltaY / OFFSET_RADIO);
               }
               break;
          default:
               mLastY = -1; // reset
               if (getFirstVisiblePosition() == 0) {
                    // invoke refresh
                    if (mEnablePullRefresh
                              && mHeaderView.getVisiableHeight() > mHeaderViewHeight) {
                         mPullRefreshing = true;
                         mHeaderView.setState(XListViewHeader.STATE_REFRESHING);
                         if (mScorllViewListener != null) {
                              mScorllViewListener.onRefresh();
                         }
                    }
                    resetHeaderHeight();
               } else if (getLastVisiblePosition() == mTotalItemCount - 1) {
                    // invoke load more.
                    if (mEnablePullLoad
                              && mFooterView.getBottomMargin() > PULL_LOAD_MORE_DELTA) {
                         startLoadMore();
                    }
                    resetFooterHeight();
               }
               break;
          }
          return super.onTouchEvent(ev);
     }

     private int getFirstVisiblePosition() {
          return 0;
     }

     private int getLastVisiblePosition() {
          return 0;
     }

     /** 由父视图调用用来请求子视图根据偏移值 mScrollX,mScrollY重新绘制 **/
     @Override
     public void computeScroll() {
          Log.e("LIFE", "computeScroll() ");
          /**
          * 当startScroll执行过程中即在duration时间内,computeScrollOffset
          * 方法会一直返回false,但当动画执行完成后会返回返加true.
          */
          if (mScroller.computeScrollOffset()) {
               if (mScrollBack == SCROLLBACK_HEADER) {
                    mHeaderView.setVisiableHeight(mScroller.getCurrY());
               } else {
                    mFooterView.setBottomMargin(mScroller.getCurrY());
               }
               postInvalidate();
               invokeOnScrolling();
          }
          super.computeScroll();
     }

     public void setOnScrollListener(OnScrollListener l) {
          Log.e("LIFE", "setOnScrollListener");
          mScrollListener = l;
     }

     /**
     * 正在滚动时回调,回调2-3次,手指没抛则回调2次。scrollState = 2的这次不回调 //回调顺序如下 //第1次:scrollState
     * = SCROLL_STATE_TOUCH_SCROLL(1) 正在滚动 //第2次:scrollState =
     * SCROLL_STATE_FLING(2) 手指做了抛的动作(手指离开屏幕前,用力滑了一下) //第3次:scrollState =
     * SCROLL_STATE_IDLE(0) 停止滚动 //当屏幕停止滚动时为0;当屏幕滚动且用户使用的触碰或手指还在屏幕上时为1;
     * //由于用户的操作,屏幕产生惯性滑动时为2 //当滚到最后一行且停止滚动时,执行加载
     **/

     @Override
     public void onScrollStateChanged(AbsListView view, int scrollState) {
          Log.e("LIFE", "onScrollStateChanged");
          if (scrollState == SCROLL_STATE_TOUCH_SCROLL) {
               Log.e("scrollState", "正在滚动 ");
          }
          if (scrollState == SCROLL_STATE_FLING) {
               Log.e("scrollState", " 手指做了抛的动作(手指离开屏幕前,用力滑了一下) ");
          }

          if (scrollState == SCROLL_STATE_IDLE) {
               Log.e("scrollState", "停止滚动     ");
          }
          if (mScrollListener != null) {
               mScrollListener.onScrollStateChanged(view, scrollState);
          }
     }

     /**
     * 滚动时一直回调,直到停止滚动时才停止回调。单击时回调一次。 firstVisibleItem:当前能看见的第一个列表项ID(从0开始)
     * visibleItemCount:当前能看见的列表项个数(小半个也算) totalItemCount:列表项共数
     */
     @Override
     public void onScroll(AbsListView view, int firstVisibleItem,
               int visibleItemCount, int totalItemCount) {
          Log.e("LIFE", "onScroll");
          // send to user's listener
          mTotalItemCount = totalItemCount;
          if (mScrollListener != null) {
               mScrollListener.onScroll(view, firstVisibleItem, visibleItemCount,
                         totalItemCount);
          }
     }

     public void setIXScrollViewListener(IXScrollViewListener l) {
          mScorllViewListener = l;
     }

     /**
     * you can listen ListView.OnScrollListener or this one. it will invoke
     * onXScrolling when header/footer scroll back.
     */
     public interface OnXScrollListener extends OnScrollListener {
          public void onXScrolling(View view);
     }

     /**
     * implements this interface to get refresh/load more event.
     */
     public interface IXScrollViewListener {
          public void onRefresh();

          public void onLoadMore();
     }
}


以上是根据XListView-Android-master 例子的改装,解决在scrollview中可以添加任何你想加的控件来进行下拉刷新功能;
有不足之处请指正!