上拉載入更多,下拉重新整理的彈性ListView的實現
阿新 • • 發佈:2019-02-16
1.使用介紹
(1)首先在xml中定義
<cn.appleye.flexiblelistview.FlexibleListView
android:id="@+id/flexible_list_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
(2)在程式碼中實現回撥就可以實現上拉和下拉功能
2.具體實現 拋開程式碼細節,要實現彈性效果和上拉以及下拉功能需要了解以下幾點 (1)什麼是彈性效果?列表滑到底部或者頂部之後,還可以繼續滑動一定距離,然後再慢慢的恢復到底部或者頂部,恢復的過程有一個彈性的效果。 (2)什麼時候觸發?上面可以看到,滑到底部或者頂部之後開始觸發 (3)滑動多少距離開始恢復?定義好一個距離,合適就好 (4)恢復的過程的彈性效果怎麼實現?網上都有很多彈性公式 (5)什麼時候呼叫上拉或下拉回調?當上拉或下拉到一定距離手指離開開始呼叫 下面看一下具體程式碼怎麼實現的。mFlexibleListView = (FlexibleListView) findViewById(R.id.flexible_list_view); mFlexibleListView.setOnPullListener(new FlexibleListView.OnPullListener(){ @Override public void onPullDown() { //下拉重新整理 } @Override public void onPullUp() { //上拉載入更多 } });
程式碼不長,只有200多行,比較簡單,也不涉及資源問題。/** * 彈性ListView,實現了上拉和下拉功能 * @author newhope1106 2016-11-02 */ public class FlexibleListView extends ListView implements OnTouchListener{ /**初始可拉動Y軸方向距離*/ private static final int MAX_Y_OVER_SCROLL_DISTANCE = 100; private Context mContext; /**實際可上下拉動Y軸上的距離*/ private int mMaxYOverScrollDistance; private float mStartY = -1; /**開始計算的時候,第一個或者最後一個item是否可見的*/ private boolean mCalcOnItemVisible = false; /**是否開始計算*/ private boolean mStartCalc = false; /**使用者自定義的OnTouchListener類*/ private OnTouchListener mTouchListener; /**上拉和下拉監聽事件*/ private OnPullListener mPullListener; private int mScrollY = 0; private int mLastMotionY = 0; private int mDeltaY = 0; /**是否在進行動畫*/ private boolean mIsAnimationRunning = false; /**手指是否離開螢幕*/ private boolean mIsActionUp = false; public FlexibleListView(Context context){ super(context); mContext = context; super.setOnTouchListener(this); initBounceListView(); } public FlexibleListView(Context context, AttributeSet attrs) { super(context, attrs); mContext = context; super.setOnTouchListener(this); initBounceListView(); } public FlexibleListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); mContext = context; initBounceListView(); } private void initBounceListView(){ final DisplayMetrics metrics = mContext.getResources().getDisplayMetrics(); final float density = metrics.density; mMaxYOverScrollDistance = (int) (density * MAX_Y_OVER_SCROLL_DISTANCE); } /** * 覆蓋父類的方法,設定OnTouchListener監聽物件 * @param listener 使用者自定義的OnTouchListener監聽物件 * */ public void setOnTouchListener(OnTouchListener listener) { mTouchListener = listener; } /** * 設定上拉和下拉監聽物件 * @param listener 上拉和下拉監聽物件 * */ public void setOnPullListener(OnPullListener listener){ mPullListener = listener; } public void scrollTo(int x, int y) { super.scrollTo(x, y); mScrollY = y; } /** * 在滑動的過程中onTouch的ACTION_DOWN事件可能丟失,在這裡進行初始值設定 * */ @Override public boolean onInterceptTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mIsActionUp = false; resetStatus(); if(getFirstVisiblePosition() == 0 || (getLastVisiblePosition() == getAdapter().getCount()-1)) { mStartY = event.getY(); mStartCalc = true; mCalcOnItemVisible = true; }else{ mStartCalc = false; mCalcOnItemVisible = false; } mLastMotionY = (int)event.getY(); break; default: break; } return super.onInterceptTouchEvent(event); } @Override public boolean onTouch(View v, MotionEvent event) { /*使用者自定義的觸控監聽物件消費了事件,則不執行下面的上拉和下拉功能*/ if(mTouchListener!=null && mTouchListener.onTouch(v, event)) { return true; } /*在做動畫的時候禁止滑動列表*/ if(mIsAnimationRunning) { return true;//需要消費掉事件,否者會出現連續很快下拉或上拉無法回到初始位置的情況 } switch (event.getAction()){ case MotionEvent.ACTION_DOWN:{ mIsActionUp = false; resetStatus(); if(getFirstVisiblePosition() == 0 || (getLastVisiblePosition() == getAdapter().getCount()-1)) { mStartY = event.getY(); mStartCalc = true; mCalcOnItemVisible = true; }else{ mStartCalc = false; mCalcOnItemVisible = false; } mLastMotionY = (int)event.getY(); } case MotionEvent.ACTION_MOVE:{ if(!mStartCalc && (getFirstVisiblePosition() == 0|| (getLastVisiblePosition() == getAdapter().getCount()-1))) { mStartCalc = true; mCalcOnItemVisible = false; mStartY = event.getY(); } final int y = (int) event.getY(); mDeltaY = mLastMotionY - y; mLastMotionY = y; if(Math.abs(mScrollY) >= mMaxYOverScrollDistance) { if(mDeltaY * mScrollY > 0) { mDeltaY = 0; } } break; } case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP:{ mIsActionUp = true; float distance = event.getY() - mStartY; checkIfNeedRefresh(distance); startBoundAnimate(); } } return false; } protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) { if(mDeltaY == 0 || mIsActionUp) { return; } scrollBy(0, mDeltaY/2); } /**彈性動畫*/ private void startBoundAnimate() { mIsAnimationRunning = true; final int scrollY = mScrollY; int time = Math.abs(500*scrollY/mMaxYOverScrollDistance); ValueAnimator animator = ValueAnimator.ofInt(0,1).setDuration(time); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animator) { float fraction = animator.getAnimatedFraction(); scrollTo(0, scrollY - (int) (scrollY * fraction)); if((int)fraction == 1) { scrollTo(0, 0); resetStatus(); animator.removeUpdateListener(this); } } }); animator.start(); } private void resetStatus() { mIsAnimationRunning = false; mStartCalc = false; mCalcOnItemVisible = false; } /** * 根據滑動的距離判斷是否需要回調上拉或者下拉事件 * @param distance 滑動的距離 * */ private void checkIfNeedRefresh(float distance) { if(distance > 0 && getFirstVisiblePosition() == 0) { //下拉 View view = getChildAt(0); if(view == null) { return; } float realDistance = distance; if(!mCalcOnItemVisible) { realDistance = realDistance - view.getHeight();//第一個item的高度不計算在內容 } if(realDistance > mMaxYOverScrollDistance) { if(mPullListener != null){ mPullListener.onPullDown(); } } } else if(distance < 0 && getLastVisiblePosition() == getAdapter().getCount()-1) {//上拉 View view = getChildAt(getChildCount()-1); if(view == null) { return; } float realDistance = -distance; if(!mCalcOnItemVisible) { realDistance = realDistance - view.getHeight();//最後一個item的高度不計算在內容 } if(realDistance > mMaxYOverScrollDistance) { if(mPullListener != null){ mPullListener.onPullUp(); } } } } public interface OnPullListener{ /** * 下拉 * */ void onPullDown(); /** * 上拉 * */ void onPullUp(); } }