公司項目,需要用到ScrollView的下拉刷新,一開始使用的時候PullToRefresh三方庫的下拉刷新,我比較糾結第三檔庫,很強大,但是,公司項目的需求,PullToRefresh就不能做到了,改來改去的還是自己寫一個下拉刷新比較靠譜,很多東西能夠自己去控制。效果圖就不上傳了。直接解釋關鍵代碼。
ScrollView的下拉刷新比ListView的好做多了。
因為ScroTo的性質,ScrollView的下拉刷新,需要在外部添加一個父布局,通過父布局來控制觸摸手勢時候下傳,什么時候下拉。
我自定義了一個PullScrollView
public class PullScrollView extends RelativeLayout
public PullScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
public PullScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public PullScrollView(Context context) {
super(context);
init(context);
}
private Scroller mScroller ;
private int mTouchSlop ;
private void init(Context context){
ViewConfiguration configuration = ViewConfiguration.get(getContext());
mTouchSlop = configuration.getScaledTouchSlop();
mScroller = new Scroller(context, new DecelerateInterpolator());
}
首先是初始化
mTouchSlop
是觸摸手勢滑動的最小像素值,也就是說滑動多少的距離才算是手勢滑動,這樣可以防止手勢一點點的移動就引起的滑動事件。
mScroller
是用來處理平滑滾動的。之前的博客有介紹。
第二點:
@Override
protected void onFinishInflate() {
super.onFinishInflate();
if (getChildCount() > 2) {
throw new RuntimeException("子孩子只能有兩個");
}
bottomView = (ViewGroup) getChildAt(0);
contentView = (ScrollView) getChildAt(1);
}
在布局初始化結束之后,得到布局中的兩個子孩子,為啥只能有兩個孩子那?? BottomView是用來下拉刷新展示的View contentView 就是我們的ScrollView了。如果子孩子多了,怎么知道哪個VIew是需要被隱藏的?所以只處理兩個View的情況,當然,如果還有上拉加載更多,就需要三個子孩子了。
第三:
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
bottomHeight = getBottomViewHeight() ;
Log.i("Test", l + "ceshi" + " t="+t + " r"+r + " b=" + b + " height= " + bottomHeight);
bottomView.layout(l, - bottomHeight, r, t);
contentView.layout(l, 0, r, b);
}
onLayout,做過自定義的都應該很熟悉這個方法,放置子孩子位置的一個方法,因為我們需要有一個子孩子隱藏掉,當我們需要它顯示的時候才去顯示,所以 需要手動的去將BottomView放到布局-hight到0的位置,這樣下拉的時候才能顯示出來。
第四:
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (getScrollY() < 0 ) {
return true ;
}
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
startY = (int) ev.getY();
break;
case MotionEvent.ACTION_MOVE:
int moveY = (int) ev.getY();
int delayY = moveY - startY ;
Log.i("Test", delayY + " = " + mTouchSlop) ;
if (getTopPosition() && delayY > mTouchSlop) {
ev.setAction(MotionEvent.ACTION_DOWN);
return true ;
}
break ;
case MotionEvent.ACTION_UP:
break;
}
return super.onInterceptTouchEvent(ev);
}
手勢的攔截動作,通過
getTopPosition()方式,來判斷ScrollView時候處於下拉需要顯示隱藏子View的狀態,
delayY >
mTouchSlop 是用來判斷是不是下拉的動作的。 如果符合條件 我們就需要將手勢攔截掉,return true。
為什么我覺得ScrollView做下來刷新比較好做那? 就是因為ScrollView的判斷比較好判斷。
private boolean getTopPosition(){
if (contentView.getScrollY() <= 0 ) {
return true ;
}
return false ;
}
是不是很簡單!!!!
第五:
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startY = (int) event.getY();
break;
case MotionEvent.ACTION_MOVE:
int delayY = (int) (event.getY() - startY) ;
if (getTopPosition() && getScrollY() <= 0 ) {
pullMove((int) (-delayY * 0.8)); // 跟隨手勢滑動
}
startY = (int) event.getY();
return true ;
case MotionEvent.ACTION_UP:
int scrollY = getScrollY();
if (state == PullState.ON_REFRESH && scrollY < 0 && Math.abs(scrollY) > bottomHeight) {
restView(-getScrollY() - bottomHeight); // 回彈到下拉刷新的狀態
return true ;
}else if (state == PullState.ON_REFRESH && scrollY < 0 && Math.abs(scrollY) < bottomHeight) {
return true ;
}
if (scrollY < 0 && Math.abs(scrollY) < bottomHeight ) {
returnView(); // 回彈到原來的位置。
}else if (scrollY < 0 && Math.abs(scrollY) > bottomHeight && state != PullState.ON_REFRESH) {
if (onreListener != null) {
state = PullState.ON_REFRESH ;
onreListener.refresh(); // 回調函數
}
restView(-getScrollY() - bottomHeight);
}
break;
}
return true ;
}
這個就是做移動的處理手勢的方法了。
ScrollView的下拉刷新比起Listview ,要好做的多了。哈哈………………
下面是源碼附上,用做學習之用。代碼還有不完善之處,等我項目完成之后,我會把項目中的封裝很完善的下拉刷新代碼重新打包上傳。