前面的五篇文章已經完整的介紹了橫向ListView的整個實現過程,這篇文章介紹的是將前面所完成的橫向ListView轉換成豎向的,及一個能夠上/下拉回彈的ListView。java
這篇文章也是這一系列文章的最後一篇。android
橫向轉豎向比較容易步驟以下:算法
1.把相應的left、right、width、height的值改爲top、bottom、height、width的值spring
2.將addAndMeasureChild()方法改成如下代碼:api
private void addAndMeasureChild(View child, int viewIndex) { LayoutParams params = child.getLayoutParams(); params = params==null ? new LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.WRAP_CONTENT):params; addViewInLayout(child, viewIndex, params, true); child.measure(MeasureSpec.makeMeasureSpec(getWidth()-getPaddingLeft()-getPaddingRight(), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.UNSPECIFIED)); }
3.修改ScrollBar的調用,把mScrollBar.showHorizontal()改爲mScrollBar.showVertical()緩存
if(canShowScrollBar) { //佈局完全部的視圖以後添加上滾動條的顯示 addAndMeasureChild(mScrollBar, getChildCount()); if (adapter != null) { mScrollBar.showVertical(this, firstItemIndex, lastItemIndex, adapter.getCount(), headViewHeight, footViewHeight); } else { mScrollBar.showVertical(this, 0, 0, 0, headViewHeight, footViewHeight); } }
如下是完整代碼:ide
package com.hss.os.horizontallistview.history_version; import android.content.Context; import android.database.DataSetObserver; import android.graphics.Rect; import android.os.Build; import android.support.annotation.RequiresApi; import android.util.AttributeSet; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; import android.widget.AdapterView; import android.widget.ListAdapter; import android.widget.Scroller; import com.hss.os.horizontallistview.ScrollBar; import java.util.LinkedList; import java.util.Queue; /** * Created by sxyx on 2017/8/11. */ public class VerticalListView extends AdapterView<ListAdapter> { private Queue<View> cacheView = new LinkedList<>();//列表項緩存視圖 private ListAdapter adapter = null; private GestureDetector mGesture; private int firstItemIndex = 0;//顯示的第一個子項的下標 private int lastItemIndex = -1;//顯示的最後的一個子項的下標 private int scrollValue=0;//列表已經發生有效滾動的位移值 private int hasToScrollValue=0;//接下來列表發生滾動所要達到的位移值 private int maxScrollValue=Integer.MAX_VALUE;//列表發生滾動所能達到的最大位移值(這個由最後顯示的列表項決定) private int minScrollValue=Integer.MIN_VALUE;//列表發生滾動所能達到的最小位移值(值爲0表示不能發生相似下拉刷新的操做,負值表示能夠) private int displayOffset=0;//列表顯示的偏移值(用於矯正列表顯示的全部子項的顯示位置) private Scroller mScroller; private int firstItemTopEdge=0;//第一個子項的左邊界 private int lastItemBottomEdge=0;//最後一個子項的右邊界 private View headView; private View footView; private boolean hasHeadView=false; private boolean hasFootView=false; private ScrollBar mScrollBar; private int headViewHeight=0;//頭視圖寬度 private int footViewHeight=0;//尾視圖寬度 private boolean canShowScrollBar=true;//是否須要顯示滾動條 private boolean canPullUp=true;//能夠下拉 private boolean canPullDown=true;//能夠上拉 public VerticalListView(Context context) { super(context); init(context); } public VerticalListView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public VerticalListView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public VerticalListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(context); } private void init(Context context){ mGesture = new GestureDetector(getContext(), mOnGesture); mScroller=new Scroller(context); mScrollBar=new ScrollBar(context); } private void initParams(){ mScroller.forceFinished(true);//避免在滑動過程當中變換視圖內容時,出現列表沒法滾動的狀況 removeAllViewsInLayout(); if(adapter!=null&&lastItemIndex<adapter.getCount()) hasToScrollValue=scrollValue;//保持顯示位置不變 else hasToScrollValue=0;//滾動到列表頭 scrollValue=0;//列表已經發生有效滾動的位移值 firstItemIndex = 0;//顯示的第一個子項的下標 lastItemIndex = -1;//顯示的最後的一個子項的下標 maxScrollValue=Integer.MAX_VALUE;//列表發生滾動所能達到的最大位移值(這個由最後顯示的列表項決定) // 列表發生滾動所能達到的最小位移值(值爲0表示不能發生相似下拉刷新的操做,負值表示能夠) if(!isCanPullDown()) minScrollValue = 0; else minScrollValue = Integer.MIN_VALUE; displayOffset=0;//列表顯示的偏移值(用於矯正列表顯示的全部子項的顯示位置) firstItemTopEdge=0;//第一個子項的左邊界 lastItemBottomEdge=0;//最後一個子項的右邊界 if(hasHeadView||hasFootView) { if (hasHeadView) { scrollValue = headView.getMeasuredHeight(); headView.layout(0, 0, 0, 0); setHeadView(headView); } if (hasFootView) { footView.layout(0, 0, 0, 0); setFootView(footView); } }else requestLayout(); } private DataSetObserver mDataObserver = new DataSetObserver() { @Override public void onChanged() { //執行Adapter數據改變時的邏輯 initParams(); } @Override public void onInvalidated() { //執行Adapter數據失效時的邏輯 initParams(); } }; @Override public ListAdapter getAdapter() { return adapter; } @Override public void setAdapter(ListAdapter adapter) { if(adapter!=null){ adapter.registerDataSetObserver(mDataObserver); } if(this.adapter!=null){ this.adapter.unregisterDataSetObserver(mDataObserver); } this.adapter=adapter; requestLayout(); } @Override public View getSelectedView() { return null; } @Override public void setSelection(int i) { } private void addAndMeasureChild(View child, int viewIndex) { LayoutParams params = child.getLayoutParams(); params = params==null ? new LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.WRAP_CONTENT):params; addViewInLayout(child, viewIndex, params, true); child.measure(MeasureSpec.makeMeasureSpec(getWidth()-getPaddingLeft()-getPaddingRight(), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.UNSPECIFIED)); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); //在執行佈局以前須要先移除滾動條,以避免影響其它視圖的顯示運算 mScrollBar.remove(this); //須要先佈局列表項再根據餘下的空間佈局列表頭尾 //佈局列表項 /* 1.計算這一次總體滾動偏移量 2.根據偏移量提取須要緩存視圖 3.根據偏移量顯示新的列表項 4.根據總體偏移值整頓全部列表項位置 5.計算最大滾動位移值,記錄已經發生有效滾動的位移值 6.根據顯示的最終效果,判斷是否要居中顯示 */ int dx=calculateScrollValue(); removeNonVisibleItems(dx); showListItem(dx); adjustItems(); //佈局列表頭、尾 adjustHeadAndFootView(dx); calculateMaxScrollValue(); if(canShowScrollBar) { //佈局完全部的視圖以後添加上滾動條的顯示 addAndMeasureChild(mScrollBar, getChildCount()); if (adapter != null) { mScrollBar.showVertical(this, firstItemIndex, lastItemIndex, adapter.getCount(), headViewHeight, footViewHeight); } else { mScrollBar.showVertical(this, 0, 0, 0, headViewHeight, footViewHeight); } } //繼續滾動 if(!mScroller.isFinished()){ //在onLayout中requestLayout()方法必須按如下形式調用,不然沒有效果 post(new Runnable(){ @Override public void run() { requestLayout(); } }); }else if(isRemoveFootView){//表示全部的計算都已經完成 isRemoveFootView=false; //這是修補因移除尾視圖而出現列表頭空白的bug int startIndex=0; if(canShowScrollBar) startIndex=1; if(getChildCount()>startIndex){ if(getChildAt(0).getTop()>getShowStartEdge()){ //表示列表頭有出現空白 hasToScrollValue += getChildAt(0).getTop()-getShowStartEdge(); //在onLayout中requestLayout()方法必須按如下形式調用,不然沒有效果 post(new Runnable(){ @Override public void run() { requestLayout(); } }); } } } } /** * 計算這一次總體滾動偏移量 * @return */ private int calculateScrollValue(){ int dx=0; if(mScroller.computeScrollOffset()){ hasToScrollValue = mScroller.getCurrY(); } if(hasToScrollValue <= minScrollValue){ hasToScrollValue = minScrollValue; mScroller.forceFinished(true); } if(hasToScrollValue >= maxScrollValue) { hasToScrollValue = maxScrollValue; mScroller.forceFinished(true); } dx=hasToScrollValue-scrollValue; scrollValue=hasToScrollValue; return -dx; } /** * 計算最大滾動值 */ private void calculateMaxScrollValue(){ if(getListItemCount()>0) { if(lastItemIndex==adapter.getCount()-1) {//已經顯示了最後一項 if(getChildAt(getChildCount() - 1).getBottom()>=getShowEndEdge()) { setMaxScrollValue(scrollValue + getChildAt(getChildCount() - 1).getBottom() - getShowEndEdge()); }else{ setMaxScrollValue(0); } }else{ setMaxScrollValue(Integer.MAX_VALUE); } }else{ if(adapter!=null&&adapter.getCount()>0){ // 修補bug:當尾視圖足夠寬可以佔據全部可視區域時, // maxScrollValue的值沒有改變,而致使滾動出現空白區域 setMaxScrollValue(scrollValue + getChildAt(getChildCount() - 1).getBottom() - getShowEndEdge()); }else { if (getChildCount() > 0 && getChildAt(getChildCount() - 1).getBottom() >= getShowEndEdge()) { setMaxScrollValue(scrollValue + getChildAt(getChildCount() - 1).getBottom() - getShowEndEdge()); }else{ setMaxScrollValue(0); } } } } /** * 根據偏移量提取須要緩存視圖 * @param dx */ private void removeNonVisibleItems(int dx) { if(getListItemCount()>0) { //移除列表頭 View child = getChildAt(getStartItemIndex()); while (getListItemCount()>0&&child != null && child.getBottom() + dx <= getShowStartEdge()) { displayOffset += child.getMeasuredHeight(); cacheView.offer(child); removeViewInLayout(child); firstItemIndex++; child = getChildAt(getStartItemIndex()); } //移除列表尾 child = getChildAt(getEndItemIndex()); while (getListItemCount()>0&&child != null && child.getTop() + dx >= getShowEndEdge()) { cacheView.offer(child); removeViewInLayout(child); lastItemIndex--; child = getChildAt(getEndItemIndex()); } } } /** * 根據偏移量顯示新的列表項 * @param dx */ private void showListItem(int dx) { if(adapter==null)return; int firstItemEdge = getFirstItemTopEdge()+dx; int lastItemEdge = getLastItemBottomEdge()+dx; displayOffset+=dx;//計算偏移量 //顯示列表頭視圖 while(firstItemEdge > getShowStartEdge() && firstItemIndex-1 >= 0) { firstItemIndex--;//往前顯示一個列表項 View child = adapter.getView(firstItemIndex, cacheView.poll(), this); addAndMeasureChild(child, getStartItemIndex()); firstItemEdge -= child.getMeasuredHeight(); displayOffset -= child.getMeasuredHeight(); } //顯示列表未視圖 while(lastItemEdge < getShowEndEdge() && lastItemIndex+1 < adapter.getCount()) { lastItemIndex++;//日後顯示一個列表項 View child = adapter.getView(lastItemIndex, cacheView.poll(), this); addAndMeasureChild(child, getEndItemIndex()+1); lastItemEdge += child.getMeasuredHeight(); } } /** * 調整各個item的位置 */ private void adjustItems() { if(getListItemCount() > 0){ int top = displayOffset+getShowStartEdge(); int left = getPaddingLeft(); int endIndex = getEndItemIndex(); int startIndex = getStartItemIndex(); int childWidth,childHeight; for(int i=startIndex;i<=endIndex;i++){ View child = getChildAt(i); childWidth = child.getMeasuredWidth(); childHeight = child.getMeasuredHeight(); child.layout(left, top, left + childWidth, top + childHeight); top += childHeight; } firstItemTopEdge=getChildAt(getStartItemIndex()).getTop(); lastItemBottomEdge=getChildAt(getEndItemIndex()).getBottom(); } } /** * 調整列表頭、尾 */ private void adjustHeadAndFootView(int dx){ headViewHeight=footViewHeight=0; if(hasHeadView){ int top,bottom; if(getListItemCount()>0){ bottom=firstItemTopEdge; }else{ bottom=headView.getBottom()+dx; } top=bottom-headView.getMeasuredHeight(); headView.layout(getPaddingLeft(), top, headView.getMeasuredWidth()+getPaddingLeft(), bottom); headViewHeight=headView.getMeasuredHeight(); } if(hasFootView){ int top,bottom; if(getListItemCount()>0){ top=getChildAt(getEndItemIndex()).getBottom(); }else{ //添加headView.getRight()>=getShowStartEdge()條件用於修補原先的bug if(hasHeadView&&headView.getBottom()>=getShowStartEdge()) top=headView.getBottom(); else { if(footView.getTop()==0&&dx==0){//第一次賦值 top=getShowStartEdge(); }else{ top=footView.getTop()+dx; } //調整以修補顯示bug if(hasHeadView) {//從新調整headView的位置 headView.layout(getPaddingLeft(),top-headView.getMeasuredWidth(), headView.getMeasuredWidth() + getPaddingLeft(),top); } } } bottom=top+footView.getMeasuredHeight(); footView.layout(getPaddingLeft(), top, footView.getMeasuredWidth()+getPaddingLeft(),bottom); footViewHeight=footView.getMeasuredHeight(); } } //如下八個方法爲概念性封裝方法,有助於日後的擴展和維護 /** * 得到列表視圖中item View的總數 * @return */ private int getListItemCount(){ int itemCount=getChildCount(); if(hasHeadView)itemCount-=1; if(hasFootView)itemCount-=1; return itemCount; } /** * 得到列表視圖中第一個item View下標 * @return */ private int getStartItemIndex(){ if(hasHeadView) return 1; return 0; } /** * 得到列表視圖中最後一個item View下標 * @return */ private int getEndItemIndex(){ if(hasFootView) return getChildCount()-2; return getChildCount()-1; } /** * 得到列表視圖中第一個item View上邊界值 * @return */ private int getFirstItemTopEdge(){ if(getListItemCount()>0) { return firstItemTopEdge; }else{ if(hasHeadView) return headView.getBottom(); else return getShowStartEdge(); } } /** * 得到列表視圖中最後一個item View下邊界值 * @return */ private int getLastItemBottomEdge(){ if(getListItemCount()>0) { return lastItemBottomEdge; }else{ if(hasFootView) return footView.getTop(); else return getShowStartEdge(); } } /** * 取得視圖可見區域的左邊界 * @return */ private int getShowStartEdge(){ return getPaddingTop(); } /** * 取得視圖可見區域的右邊界 * @return */ private int getShowEndEdge(){ return getHeight()-getPaddingBottom(); } /** * 取得視圖可見區域的高度 * @return */ private int getShowHeight(){ return getHeight()-getPaddingTop()-getPaddingBottom(); } public void setMaxScrollValue(int maxScrollValue) { if(isCanPullUp()&&maxScrollValue!=Integer.MAX_VALUE) { if(isSpringBack){//在執行回彈操做 this.maxScrollValue = Integer.MAX_VALUE; }else if(mScroller.isFinished()){//手動執行滾動操做 this.maxScrollValue = Integer.MAX_VALUE; }else{ if(maxScrollValue<0)this.maxScrollValue=0; else this.maxScrollValue = maxScrollValue; } }else{ if(maxScrollValue<0)this.maxScrollValue=0; else this.maxScrollValue = maxScrollValue; } } public void setHeadView(View view){ if(view!=null) { int headBottom=-1; int height=0; if (hasHeadView&&headView!=null) { headBottom=headView.getBottom(); height=headView.getHeight(); removeViewInLayout(headView); } hasHeadView = true; headView=view; addAndMeasureChild(headView, 0); if(getListItemCount()>0) {//有列表內容 if (headBottom == -1) { //新增列表頭 if (firstItemIndex == 0) {//第一個顯示的是第一個列表項 //滾動整個列表,讓其顯示完整列表頭(讓列表往回滾) scrollValue = headView.getMeasuredHeight() + getShowStartEdge() - firstItemTopEdge; hasToScrollValue=0; } else {//不是顯示第一個列表項 //不滾動列表項,增長曆史滾動值 hasToScrollValue += headView.getMeasuredHeight(); scrollValue = hasToScrollValue; } } else { //替換列表頭 hasToScrollValue += headView.getMeasuredHeight()-height; } } setMaxScrollValue(Integer.MAX_VALUE); requestLayout(); } } public void removeHeadView(){ if(hasHeadView&&headView!=null){ hasHeadView=false; int top=headView.getTop(); int height=headView.getMeasuredHeight(); removeViewInLayout(headView); if(headView.getBottom()>=getShowStartEdge()) {//列表頭有顯示 scrollValue = -(height+top-getShowStartEdge()); hasToScrollValue=0; }else{ scrollValue-=height; hasToScrollValue-=height; } requestLayout(); }else{ hasHeadView=false; } } public void setFootView(View view){ if(view!=null) { if (hasFootView&&footView!=null) { removeViewInLayout(footView); } hasFootView=true; footView=view; addAndMeasureChild(footView, -1); requestLayout(); } } private boolean isRemoveFootView=false; public void removeFootView(){ if(hasFootView&&footView!=null){ hasFootView=false; int top=footView.getTop(); mScroller.forceFinished(true);//用於修補因爲快速華東的過程當中移除尾視圖而出現bug removeViewInLayout(footView); if(top<getShowEndEdge()) {//表示須要回退 hasToScrollValue -= getShowEndEdge()-top; isRemoveFootView=true; } requestLayout(); }else{ hasFootView=false; } } /** * 在onTouchEvent處理事件,讓子視圖優先消費事件 * @param event * @return */ @Override public boolean onTouchEvent(MotionEvent event) { if(dealTouchEvent(event)) return true; return mGesture.onTouchEvent(event); } private GestureDetector.OnGestureListener mOnGesture = new GestureDetector.SimpleOnGestureListener() { @Override public boolean onDown(MotionEvent e) { mScroller.forceFinished(true);//點擊時中止滾動 return true; } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { minScrollValue=0;//在快速滑動時,不能出現相似下拉刷新的操做 mScroller.fling(0, scrollValue, 0, (int)-velocityY, 0, 0, 0, maxScrollValue); requestLayout(); return true; } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { if(isCanPullDown()) { minScrollValue = Integer.MIN_VALUE;//手動滑動時能夠出現相似下拉刷新的操做 }else{ minScrollValue = 0; } synchronized(VerticalListView.this){ hasToScrollValue += (int)calDistanceVal(distanceY); } requestLayout(); return true; } @Override public boolean onSingleTapConfirmed(MotionEvent e) { int startIndex=0; if(canShowScrollBar) startIndex=1; for(int i=0;i<getChildCount();i++){ View child = getChildAt(i); if(child==mScrollBar) break; if (isEventWithinView(e, child)) { if(hasHeadView&&i==0){ //點擊列表頭 if(OnHeadViewClickListener!=null) OnHeadViewClickListener.onClick(child); }else if(hasFootView&&i==getChildCount()-(1+startIndex)){ //點擊列表尾 if(OnFootViewClickListener!=null) OnFootViewClickListener.onClick(child); }else { int position=firstItemIndex + i; if(hasHeadView) position--; if (getOnItemClickListener() != null) { getOnItemClickListener().onItemClick(VerticalListView.this, child, position, adapter.getItemId(position)); } if (getOnItemSelectedListener() != null) { getOnItemSelectedListener().onItemSelected(VerticalListView.this, child, position, adapter.getItemId(position)); } } break; } } return true; } @Override public void onLongPress(MotionEvent e) { int startIndex=0;//這個值用於處理滾動條的影響 if(canShowScrollBar) startIndex=1; int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View child = getChildAt(i); if(child==mScrollBar) return; if (isEventWithinView(e, child)) { if(hasHeadView&&i==0){ //長按列表頭 if(OnHeadViewLongClickListener!=null) OnHeadViewLongClickListener.onLongClick(child); }else if(hasFootView&&i==getChildCount()-(1+startIndex)){ //長按列表尾 if(OnFootViewLongClickListener!=null) OnFootViewLongClickListener.onLongClick(child); } else { int position=firstItemIndex + i; if(hasHeadView) position--; if (getOnItemLongClickListener() != null) { getOnItemLongClickListener().onItemLongClick(VerticalListView.this, child, position, adapter.getItemId(position)); } } break; } } } private boolean isEventWithinView(MotionEvent e, View child) { Rect viewRect = new Rect(); int[] childPosition = new int[2]; child.getLocationOnScreen(childPosition); int left = childPosition[0]; int right = left + child.getWidth(); int top = childPosition[1]; int bottom = top + child.getHeight(); viewRect.set(left, top, right, bottom); return viewRect.contains((int) e.getRawX(), (int) e.getRawY()); } }; private boolean isEventDownIntercept=false; private boolean isSpringBack=false; private boolean isCanPullDownInthis=true; /** * 在手勢解析器以前攔截事件 * @param event * @return */ private boolean dealTouchEvent(MotionEvent event){ switch (event.getAction()){ case MotionEvent.ACTION_DOWN: isCanPullDownInthis=true; int startIndex=0; if(canShowScrollBar) startIndex=1; if(getChildCount()==startIndex){ isCanPullDownInthis=false; }else{ if(getChildAt(getChildCount()-(1+startIndex)).getBottom()<getShowEndEdge()){ isCanPullDownInthis=false; } } isEventDownIntercept=springBack(); if (isEventDownIntercept) return true; break; case MotionEvent.ACTION_MOVE: return isEventDownIntercept; case MotionEvent.ACTION_UP: return springBack(); } return false; } /** * 採用阻尼式算法,從新提取移動值 * @param distanceVal * @return */ private float calDistanceVal(float distanceVal){ if(!isCanPullDownInthis&&distanceVal>0){ return 0; } int startIndex=0;//這個值用於處理滾動條的影響 if(canShowScrollBar) startIndex=1; if(getChildCount()>startIndex){ int dis=getChildAt(0).getTop()-getShowStartEdge(); if(dis>0){//執行頭部拉伸阻尼式計算 if(distanceVal>=0) distanceVal = distanceVal/2; else { float sc = 1 - dis / (getShowHeight() * 0.4f); sc = sc < 0 ? 0 : sc; distanceVal = distanceVal * sc; } if(onPullDownListener!=null) onPullDownListener.onScroll(distanceVal); }else { dis = getShowEndEdge() - getChildAt(getChildCount() - (1 + startIndex)).getBottom(); if (dis > 0 && getFirstItemTopEdge() < getShowStartEdge()) { //執行尾部拉伸阻尼式計算 if (distanceVal <= 0) distanceVal = distanceVal / 2; else { float sc = 1 - dis / (getShowHeight() * 0.4f); sc = sc < 0 ? 0 : sc; distanceVal = distanceVal * sc; } if (onPullUpListener != null) onPullUpListener.onScroll(distanceVal); } } } return distanceVal; } /** * 執行回滾操做 * @return */ private boolean springBack(){ isSpringBack=false; int startIndex=0; if(canShowScrollBar) startIndex=1; if(getChildCount()>startIndex){ int dis=getChildAt(0).getTop()-getShowStartEdge(); if(dis>0){//執行頭部回滾操做 mScroller.startScroll(0, scrollValue, 0, dis, 500); isSpringBack=true; requestLayout(); if(onPullDownListener!=null &&(dis>getShowHeight()/5||dis>120)) onPullDownListener.onPullEvent(); return true; } dis=getShowEndEdge()-getChildAt(getChildCount()-(1+startIndex)).getBottom(); if(dis>0&&getFirstItemTopEdge()<getShowStartEdge()){ //執行尾部回滾操做 mScroller.startScroll(0, scrollValue, 0, -dis, 500); isSpringBack=true; requestLayout(); if(onPullUpListener!=null &&(dis>getShowHeight()/5||dis>120)) onPullUpListener.onPullEvent(); return true; } } return false; } private OnPullListener onPullDownListener = null; private OnPullListener onPullUpListener = null; private OnClickListener OnHeadViewClickListener; private OnClickListener OnFootViewClickListener; private OnLongClickListener OnHeadViewLongClickListener; private OnLongClickListener OnFootViewLongClickListener; public void setOnPullDownListener(OnPullListener onPullDownListener) { this.onPullDownListener = onPullDownListener; } public void setOnPullUpListener(OnPullListener onPullUpListener) { this.onPullUpListener = onPullUpListener; } public void setOnHeadViewClickListener(OnClickListener onHeadViewClickListener) { OnHeadViewClickListener = onHeadViewClickListener; } public void setOnFootViewClickListener(OnClickListener onFootViewClickListener) { OnFootViewClickListener = onFootViewClickListener; } public void setOnHeadViewLongClickListener(OnLongClickListener onHeadViewLongClickListener) { OnHeadViewLongClickListener = onHeadViewLongClickListener; } public void setOnFootViewLongClickListener(OnLongClickListener onFootViewLongClickListener) { OnFootViewLongClickListener = onFootViewLongClickListener; } public interface OnPullListener{ /** * 滾動時執行 * @param dis 移動的位移值 */ void onScroll(float dis); /** * 事件確認(相似下拉刷新事件觸發) */ void onPullEvent(); } public synchronized void scrollTo(int x) { mScroller.startScroll(hasToScrollValue, 0, x - hasToScrollValue, 0); requestLayout(); } public boolean isCanShowScrollBar() { return canShowScrollBar; } public void setCanShowScrollBar(boolean canShowScrollBar) { this.canShowScrollBar = canShowScrollBar; } public boolean isCanPullDown() { return canPullDown; } public void setCanPullDown(boolean canPullDown) { this.canPullDown = canPullDown; } public boolean isCanPullUp() { return canPullUp; } public void setCanPullUp(boolean canPullUp) { this.canPullUp = canPullUp; } }