Android自定义View实现竖向滑动回弹效果

Android自定义View实现竖向滑动回弹效果

本文实例为大家分享了Android自定义View实现滑动回弹的具体代码,供大家参考,具体内容如下

前言

Android 页面滑动的时候的回弹效果

一、关键代码

public class UniversalBounceView extends FrameLayout implements IPull {     private static final String TAG = "UniversalBounceView";     //default.     private static final int SCROLL_DURATION = 200;     private static final float SCROLL_FRACTION = 0.4f;     private static final int VIEW_TYPE_NORMAL = 0;     private static final int VIEW_TYPE_ABSLISTVIEW = 1;     private static final int VIEW_TYPE_SCROLLVIEW = 2;     private static float VIEW_SCROLL_MAX = 720;     private int viewHeight;     private AbsListView alv;     private OnBounceStateListener onBounceStateListener;     private View child;     private Scroller scroller;     private boolean pullEnabled = true;     private boolean pullPaused;     private int touchSlop = 8;     private int mPointerId;     private float downY, lastDownY, tmpY;     private int lastPointerIndex;     private float moveDiffY;     private boolean isNotJustInClickMode;     private int moveDelta;     private int viewType = VIEW_TYPE_NORMAL;     public UniversalBounceView(Context context) {         super(context);         init(context);     }     public UniversalBounceView(Context context, AttributeSet attrs) {         super(context, attrs);         init(context);     }     public UniversalBounceView(Context context, AttributeSet attrs, int defStyleAttr) {         super(context, attrs, defStyleAttr);         init(context);     }     private void init(Context context) {         scroller = new Scroller(context, new CustomDecInterpolator());         touchSlop = (int) (ViewConfiguration.get(context).getScaledTouchSlop() * 1.5);     }     class CustomDecInterpolator extends DecelerateInterpolator {         public CustomDecInterpolator() {             super();         }         public CustomDecInterpolator(float factor) {             super(factor);         }         public CustomDecInterpolator(Context context, AttributeSet attrs) {             super(context, attrs);         }         @Override         public float getInterpolation(float input) {             return (float) Math.pow(input, 6.0 / 12);         }     }     private void checkCld() {         int cnt = getChildCount();         if (1 <= cnt) {             child = getChildAt(0);         } else if (0 == cnt) {             pullEnabled = false;             child = new View(getContext());         } else {             throw new ArrayIndexOutOfBoundsException("child count can not be less than 0.");         }     }     @Override     protected void onFinishInflate() {         checkCld();         super.onFinishInflate();     }     @Override     protected void onSizeChanged(int w, int h, int oldw, int oldh) {         super.onSizeChanged(w, h, oldw, oldh);         viewHeight = h;         VIEW_SCROLL_MAX = h * 1 / 3;     }     private boolean isTouch = true;     public void setTouch(boolean isTouch) {         this.isTouch = isTouch;     }     @Override     public boolean dispatchTouchEvent(MotionEvent ev) {         if (!isTouch) {             return true;         } else {             try {                 if (isPullEnable()) {                     if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) {                         if (Math.abs(ev.getY() - tmpY) < touchSlop) {                             return super.dispatchTouchEvent(ev);                         } else {                             tmpY = Integer.MIN_VALUE;                         }                     }                     return takeEvent(ev);                 }             } catch (IllegalArgumentException | IllegalStateException e) {                 e.printStackTrace();             }             if (getVisibility() != View.VISIBLE) {                 return true;             }             return super.dispatchTouchEvent(ev);         }     }     private boolean takeEvent(MotionEvent ev) {         int action = ev.getActionMasked();         switch (action) {             case MotionEvent.ACTION_DOWN:                 mPointerId = ev.getPointerId(0);                 downY = ev.getY();                 tmpY = downY;                 scroller.setFinalY(scroller.getCurrY());                 setScrollY(scroller.getCurrY());                 scroller.abortAnimation();                 pullPaused = true;                 isNotJustInClickMode = false;                 moveDelta = 0;                 break;             case MotionEvent.ACTION_UP:             case MotionEvent.ACTION_CANCEL:                 pullPaused = false;                 smoothScrollTo(0);                 if (isNotJustInClickMode) {                     ev.setAction(MotionEvent.ACTION_CANCEL);                 }                 postDelayed(new Runnable() {                     @Override                     public void run() {                         if (getScrollY() == 0 && onBounceStateListener != null) {                             onBounceStateListener.overBounce();                         }                     }                 }, 200);                 break;             case MotionEvent.ACTION_MOVE:                 lastPointerIndex = ev.findPointerIndex(mPointerId);                 lastDownY = ev.getY(lastPointerIndex);                 moveDiffY = Math.round((lastDownY - downY) * getScrollFraction());                 downY = lastDownY;                 boolean canStart = isCanPullStart();                 boolean canEnd = isCanPullEnd();                 int scroll = getScrollY();                 float total = scroll - moveDiffY;                 if (canScrollInternal(scroll, canStart, canEnd)) {                     handleInternal();                     break;                 }                 if (Math.abs(scroll) > VIEW_SCROLL_MAX) {                     return true;                 }                 if ((canStart && total < 0) || (canEnd && total > 0)) {                     if (moveDelta < touchSlop) {                         moveDelta += Math.abs(moveDiffY);                     } else {                         isNotJustInClickMode = true;                     }                     if (onBounceStateListener != null) {                         onBounceStateListener.onBounce();                     }                     scrollBy(0, (int) -moveDiffY);                     return true;                 } //                else if ((total > 0 && canStart) || (total < 0 && canEnd)) { //                    if (moveDelta < touchSlop) { //                        moveDelta += Math.abs(moveDiffY); //                    } else { //                        isNotJustInClickMode = true; //                    } //                    scrollBy(0, -scroll); //                    return true; //                }                 break;             case MotionEvent.ACTION_POINTER_UP:                 handlePointerUp(ev, 1);                 break;             default:                 break;         }         return super.dispatchTouchEvent(ev);     }     private boolean canScrollInternal(int scroll, boolean canStart, boolean canEnd) {         boolean result = false;         if ((child instanceof RecyclerView) || (child instanceof AbsListView) || child instanceof ScrollView) {             viewType = VIEW_TYPE_ABSLISTVIEW;             result = canStart && canEnd;         } else if (child instanceof ScrollView || child instanceof NestedScrollView) {             viewType = VIEW_TYPE_SCROLLVIEW;         } else {             return false;         }         if (result) {             isNotJustInClickMode = true;             if (moveDelta < touchSlop) {                 moveDelta += Math.abs(moveDiffY);                 return true;             }             return false;         }         if (((scroll == 0 && canStart && moveDiffY < 0) || (scroll == 0 && canEnd && moveDiffY > 0) || (!canStart && !canEnd))) {             return true;         }         if (moveDelta < touchSlop) {             moveDelta += Math.abs(moveDiffY);             return true;         } else {             isNotJustInClickMode = true;         }         return false;     }     private void handleInternal() {     }     private void handlePointerUp(MotionEvent event, int type) {         int pointerIndexLeave = event.getActionIndex();         int pointerIdLeave = event.getPointerId(pointerIndexLeave);         if (mPointerId == pointerIdLeave) {             int reIndex = pointerIndexLeave == 0 ? 1 : 0;             mPointerId = event.getPointerId(reIndex);             // 调整触摸位置,防止出现跳动             downY = event.getY(reIndex);         }     }     @Override     public boolean onTouchEvent(MotionEvent event) {         return super.onTouchEvent(event);     }     private void smoothScrollTo(int value) {         int scroll = getScrollY();         scroller.startScroll(0, scroll, 0, value - scroll, SCROLL_DURATION);         postInvalidate();     }     @Override     public void computeScroll() {         super.computeScroll();         if (!pullPaused && scroller.computeScrollOffset()) {             scrollTo(scroller.getCurrX(), scroller.getCurrY());             postInvalidate();         }     }     private float getScrollFraction() {         float ratio = Math.abs(getScrollY()) / VIEW_SCROLL_MAX;         ratio = ratio < 1 ? ratio : 1;         float fraction = (float) (-2 * Math.cos((ratio + 1) * Math.PI) / 5.0f) + 0.1f;         return fraction < 0.10f ? 0.10f : fraction;     }     @Override     public boolean isPullEnable() {         return pullEnabled;     }     @Override     public boolean isCanPullStart() {         if (child instanceof RecyclerView) {             RecyclerView recyclerView = (RecyclerView) child;             return !recyclerView.canScrollVertically(-1);         }         if (child instanceof AbsListView) {             AbsListView lv = (AbsListView) child;             return !lv.canScrollVertically(-1);         }         if (child instanceof RelativeLayout                 || child instanceof FrameLayout                 || child instanceof LinearLayout                 || child instanceof WebView                 || child instanceof View) {             return child.getScrollY() == 0;         }         return false;     }     @Override     public boolean isCanPullEnd() {         if (child instanceof RecyclerView) {             RecyclerView recyclerView = (RecyclerView) child;             return !recyclerView.canScrollVertically(1);         }         if (child instanceof AbsListView) {             AbsListView lv = (AbsListView) child;             int first = lv.getFirstVisiblePosition();             int last = lv.getLastVisiblePosition();             View view = lv.getChildAt(last - first);             if (null == view) {                 return false;             } else {                 return (lv.getCount() - 1 == last) &&                         (view.getBottom() <= lv.getHeight());             }         }         if (child instanceof ScrollView) {             View v = ((ScrollView) child).getChildAt(0);             if (null == v) {                 return true;             } else {                 return child.getScrollY() >= v.getHeight() - child.getHeight();             }         }         if (child instanceof NestedScrollView) {             View v = ((NestedScrollView) child).getChildAt(0);             if (null == v) {                 return true;             } else {                 return child.getScrollY() >= v.getHeight() - child.getHeight();             }         }         if (child instanceof WebView) {             return (((WebView) child).getContentHeight() * ((WebView) child).getScale()) - (((WebView) child).getHeight() + ((WebView) child).getScrollY()) <= 10;         }         if (child instanceof RelativeLayout                 || child instanceof FrameLayout                 || child instanceof LinearLayout                 || child instanceof View) {             return (child.getScrollY() == 0);         }         return false;     }     /**      * 通过addView实现效果回弹效果      *      * @param replaceChildView 需要替换的View      */     public void replaceAddChildView(View replaceChildView) {         if (replaceChildView != null) {             removeAllViews();             child = replaceChildView;             addView(replaceChildView);         }     }     public void setPullEnabled(boolean enable) {         pullEnabled = enable;     }     public interface OnBounceStateListener {         public void onBounce();         public void overBounce();     }     public void setOnBounceStateListener(OnBounceStateListener onBounceStateListener) {         this.onBounceStateListener = onBounceStateListener;     }     @Override     public boolean dispatchKeyEvent(KeyEvent event) {         try {             return super.dispatchKeyEvent(event);         } catch (IllegalArgumentException | IllegalStateException e) {             e.printStackTrace();         }         return false;     }     @Override     public void dispatchWindowFocusChanged(boolean hasFocus) {         try {             super.dispatchWindowFocusChanged(hasFocus);         } catch (IllegalArgumentException | IllegalStateException e) {             e.printStackTrace();         }     } }

二、注意要点

滑动结束的时候要防止动画抖动

private void handlePointerUp(MotionEvent event, int type) {         int pointerIndexLeave = event.getActionIndex();         int pointerIdLeave = event.getPointerId(pointerIndexLeave);         if (mPointerId == pointerIdLeave) {             int reIndex = pointerIndexLeave == 0 ? 1 : 0;             mPointerId = event.getPointerId(reIndex);             // 调整触摸位置,防止出现跳动             downY = event.getY(reIndex);         }     } 

总结

以上就是文章的主要内容,实现了竖向滑动回弹的效果。

推荐阅读