SoFunction
Updated on 2025-04-10

Android custom view achieves vertical sliding rebound effect

This example shares the specific code for Android custom view to achieve sliding rebound for your reference. The specific content is as follows

Preface

The rebound effect of Android page sliding

1. Key code

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) ((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) (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();
        ();
    }
 
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        (w, h, oldw, oldh);
        viewHeight = h;
        VIEW_SCROLL_MAX = h * 1 / 3;
    }
 
    private boolean isTouch = true;
 
    public void setTouch(boolean isTouch) {
         = isTouch;
    }
 
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (!isTouch) {
            return true;
        } else {
            try {
                if (isPullEnable()) {
                    if (() == MotionEvent.ACTION_MOVE) {
                        if ((() - tmpY) < touchSlop) {
                            return (ev);
                        } else {
                            tmpY = Integer.MIN_VALUE;
                        }
                    }
                    return takeEvent(ev);
                }
            } catch (IllegalArgumentException | IllegalStateException e) {
                ();
            }
            if (getVisibility() != ) {
                return true;
            }
            return (ev);
        }
    }
 
    private boolean takeEvent(MotionEvent ev) {
        int action = ();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                mPointerId = (0);
                downY = ();
                tmpY = downY;
                (());
                setScrollY(());
                ();
                pullPaused = true;
                isNotJustInClickMode = false;
                moveDelta = 0;
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                pullPaused = false;
                smoothScrollTo(0);
                if (isNotJustInClickMode) {
                    (MotionEvent.ACTION_CANCEL);
                }
                postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        if (getScrollY() == 0 && onBounceStateListener != null) {
                            ();
                        }
                    }
                }, 200);
                break;
            case MotionEvent.ACTION_MOVE:
                lastPointerIndex = (mPointerId);
                lastDownY = (lastPointerIndex);
                moveDiffY = ((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 ((scroll) > VIEW_SCROLL_MAX) {
                    return true;
                }
                if ((canStart && total < 0) || (canEnd && total > 0)) {
                    if (moveDelta < touchSlop) {
                        moveDelta += (moveDiffY);
                    } else {
                        isNotJustInClickMode = true;
                    }
                    if (onBounceStateListener != null) {
                        ();
                    }
                    scrollBy(0, (int) -moveDiffY);
                    return true;
                }
//                else if ((total > 0 && canStart) || (total < 0 && canEnd)) {
//                    if (moveDelta < touchSlop) {
//                        moveDelta += (moveDiffY);
//                    } else {
//                        isNotJustInClickMode = true;
//                    }
//                    scrollBy(0, -scroll);
//                    return true;
//                }
                break;
            case MotionEvent.ACTION_POINTER_UP:
                handlePointerUp(ev, 1);
                break;
            default:
                break;
        }
        return (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 += (moveDiffY);
                return true;
            }
            return false;
        }
        if (((scroll == 0 && canStart && moveDiffY < 0) || (scroll == 0 && canEnd && moveDiffY > 0) || (!canStart && !canEnd))) {
            return true;
        }
        if (moveDelta < touchSlop) {
            moveDelta += (moveDiffY);
            return true;
        } else {
            isNotJustInClickMode = true;
        }
        return false;
    }
 
    private void handleInternal() {
 
    }
 
    private void handlePointerUp(MotionEvent event, int type) {
        int pointerIndexLeave = ();
        int pointerIdLeave = (pointerIndexLeave);
        if (mPointerId == pointerIdLeave) {
            int reIndex = pointerIndexLeave == 0 ? 1 : 0;
            mPointerId = (reIndex);
            // Adjust the touch position to prevent jumps            downY = (reIndex);
        }
    }
 
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return (event);
    }
 
    private void smoothScrollTo(int value) {
        int scroll = getScrollY();
        (0, scroll, 0, value - scroll, SCROLL_DURATION);
        postInvalidate();
    }
 
    @Override
    public void computeScroll() {
        ();
        if (!pullPaused && ()) {
            scrollTo((), ());
            postInvalidate();
        }
    }
 
    private float getScrollFraction() {
        float ratio = (getScrollY()) / VIEW_SCROLL_MAX;
        ratio = ratio < 1 ? ratio : 1;
        float fraction = (float) (-2 * ((ratio + 1) * ) / 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 !(-1);
        }
        if (child instanceof AbsListView) {
            AbsListView lv = (AbsListView) child;
            return !(-1);
        }
        if (child instanceof RelativeLayout
                || child instanceof FrameLayout
                || child instanceof LinearLayout
                || child instanceof WebView
                || child instanceof View) {
            return () == 0;
        }
        return false;
    }
 
    @Override
    public boolean isCanPullEnd() {
        if (child instanceof RecyclerView) {
            RecyclerView recyclerView = (RecyclerView) child;
            return !(1);
        }
        if (child instanceof AbsListView) {
            AbsListView lv = (AbsListView) child;
            int first = ();
            int last = ();
            View view = (last - first);
            if (null == view) {
                return false;
            } else {
                return (() - 1 == last) &&
                        (() <= ());
            }
        }
        if (child instanceof ScrollView) {
            View v = ((ScrollView) child).getChildAt(0);
            if (null == v) {
                return true;
            } else {
                return () >= () - ();
            }
        }
        if (child instanceof NestedScrollView) {
            View v = ((NestedScrollView) child).getChildAt(0);
            if (null == v) {
                return true;
            } else {
                return () >= () - ();
            }
        }
        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 (() == 0);
        }
        return false;
    }
 
    /**
 * Achieve effect rebound effect through addView
      *
 * @param replaceChildView View that needs to be replaced
 */
    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) {
         = onBounceStateListener;
    }
 
    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        try {
            return (event);
        } catch (IllegalArgumentException | IllegalStateException e) {
            ();
        }
        return false;
    }
 
    @Override
    public void dispatchWindowFocusChanged(boolean hasFocus) {
        try {
            (hasFocus);
        } catch (IllegalArgumentException | IllegalStateException e) {
            ();
        }
    }
 
 
}

2. Pay attention to the key points

Prevent animation shaking when the slide ends

private void handlePointerUp(MotionEvent event, int type) {
        int pointerIndexLeave = ();
        int pointerIdLeave = (pointerIndexLeave);
        if (mPointerId == pointerIdLeave) {
            int reIndex = pointerIndexLeave == 0 ? 1 : 0;
            mPointerId = (reIndex);
            // Adjust the touch position to prevent jumps            downY = (reIndex);
        }
    } 

Summarize

The above is the main content of the article, which achieves the effect of vertical sliding rebound.

The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.