app直播源码,列表越界后自动弹回原样的效果

app直播源码,列表越界后自动弹回原样的效果实现的相关代码

一、越界回弹效果的例子

二、效果拆分

下拉、上拉越界回弹:已到达列表顶部,执行下拉操作,此时会进行越界回弹

惯性滑动 越界回弹:快速滑动列表,列表已到达底部,但由于惯性会继续前进一部分,此时会根据速度决定越界的距离以及回弹的速度

三、下拉、上拉越界回弹实现方式及代码

在实际项目中可能在多个页面都需要回弹的效果,所以需要尽可能的减少与需要回弹view的耦合代码。所以可以自定义一个ViewGroup来包裹需要回弹的view。

 

1
public class PullOverLayout extends RelativeLayout {<br>    //允许的越界回弹的高度<br>    protected float mOverScrollHeight;<br>    //被包裹的子View<br>    private View mChildView;<br>    //~~~~~<br>    public PullOverLayout(Context context, AttributeSet attrs, int defStyleAttr) {<br>        super(context, attrs, defStyleAttr);<br>        mOverScrollHeight = DensityUtil.dp2px(context, 240);<br>    }<br>    @Override<br>    protected void onAttachedToWindow() {<br>        super.onAttachedToWindow();<br>        //获得子控件,也就是被包裹的recycleview等<br>        mChildView = getChildAt(0);<br>    }<br>}

重写事件拦截方法onInterceptTouchEvent()

通过竖直和水平方向Move的距离来判断是否拦截该事件

dy大于0为手指向下滑动,dy小于0为手指向上滑动,通过setStatePTD、setStatePBU方法记录Down或者Up

当被包裹的子view无法滑动时即发生越界时拦截并响应该Move事件

 

1
@Override<br>public boolean onInterceptTouchEvent(MotionEvent ev) {<br>    switch (ev.getAction()) {<br>        case MotionEvent.ACTION_DOWN:<br>            mTouchX = ev.getX();<br>            mTouchY = ev.getY();<br>            break;<br>        case MotionEvent.ACTION_MOVE:<br>            float dx = ev.getX() - mTouchX;<br>            float dy = ev.getY() - mTouchY;<br>            if (Math.abs(dx) <= Math.abs(dy)) {<br>                if (dy > 0 && !ScrollingUtil.canChildScrollUp(mChildView)) {<br>                    //1、下拉操作<br>                    LogUtil.d(TAG, "setStatePTD");<br>                    cp.setStatePTD();<br>                    return true;<br>                } else if (dy < 0 && !ScrollingUtil.canChildScrollDown(cp.getContent())) {<br>                    //2、上拉操作<br>                    LogUtil.d(TAG, "setStatePBU");<br>                    cp.setStatePBU();<br>                    return true;<br>                }<br>            }<br>            break;<br>    }<br>    return super.onInterceptTouchEvent(ev);<br>}

 

重写onTouchEvent()方法响应拦截的事件

onTouchEvent用来响应事件,当move和up时,做相应的“越界”操作和“回弹”操作

下拉和上拉操作分别执行对应的越界和回弹操作即可

 

1
@Override<br>public boolean onTouchEvent(MotionEvent e) {<br>    switch (e.getAction()) {<br>        //当发生越界时由viewGroup响应Move事件<br>        case MotionEvent.ACTION_MOVE:<br>            float dy = e.getY() - mTouchY;<br>            if (cp.isStatePTD()) {<br>                //1、如果是下拉操作的move事件,则执行顶部越界操作<br>                dy = Math.min(cp.getOsHeight() * 2, dy);<br>                dy = Math.max(0, dy);<br>                cp.getAnimProcessor().scrollHeadByMove(dy);<br>            } else if (cp.isStatePBU()) {<br>                //2、如果是上拉操作的move事件,则执行底部越界的操作<br>                dy = Math.min(cp.getOsHeight() * 2, Math.abs(dy));<br>                dy = Math.max(0, dy);<br>                cp.getAnimProcessor().scrollBottomByMove(dy);<br>            }<br>            return true;<br>        case MotionEvent.ACTION_CANCEL:<br>        case MotionEvent.ACTION_UP:<br>            if (cp.isStatePTD()) {<br>                //3、如果是下拉操作,当收到Up事件时,进行释放操作下拉操作,即执行下拉回弹操作<br>                cp.getAnimProcessor().dealPullDownRelease();<br>            } else if (cp.isStatePBU()) {<br>                //4、如果是上拉操作,当收到up事件时,进行释放上拉操作,执行上拉回弹操作<br>                cp.getAnimProcessor().dealPullUpRelease();<br>            }<br>            return true;<br>    }<br>    return super.onTouchEvent(e);<br>}

 

第三步onTouchEvent中的下拉越界方法scrollHeadByMove(float moveY)和下拉释放的回弹方法dealPullDownRelease()

 

1
public void scrollHeadByMove(float moveY) {<br>    float offsetY = decelerateInterpolator.getInterpolation(moveY / cp.getOsHeight() / 2) * moveY / 2;<br>    LogUtil.d(TAG, "scrollHeadByMove ", "offsetY:", String.valueOf(offsetY));<br>    cp.getHeader().setVisibility(GONE);<br>    cp.getHeader().getLayoutParams().height = (int) Math.abs(offsetY);<br>    cp.getHeader().requestLayout();<br>    cp.getContent().setTranslationY(offsetY);<br>}<br>public void dealPullDownRelease() {<br>    int start = getVisibleHeadHeight();<br>    int end = 0;<br>    LogUtil.d(TAG, "dealPullDownRelease  ", "start:", String.valueOf(start), "end:", String.valueOf(end));<br>    ValueAnimator va = ValueAnimator.ofInt(start, end);<br>    va.setInterpolator(new DecelerateInterpolator());<br>    va.addUpdateListener(animHeadUpListener);<br>    va.setDuration((int) (Math.abs(start - end) * animFraction));<br>    va.start();<br>}

 

 

四、惯性滑动 越界回弹实现方式及代码

给被包裹的子view设置监听

给定一个最低速度的阈值,当滑动速度超过这个值时根据惯性执行越界回弹操作

下方代码只实现了recycleview的越界回弹,listview类似。即被包裹的子view必须是recycleview

 

1
public void initChildViewFlingListener() {<br>    final View mChildView = cp.getContent();<br>    //1、给被包裹的子view设置监听事件,监听fling操作,并记录速度<br>    final GestureDetector gestureDetector = new GestureDetector(cp.getContext(), new GestureDetector.SimpleOnGestureListener() {<br>        @Override<br>        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {<br>            LogUtil.d(TAG, "onScroll  ", "distanceY:", String.valueOf(distanceY));<br>            return super.onScroll(e1, e2, distanceX, distanceY);<br>        }<br>        @Override<br>        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {<br>            LogUtil.d(TAG, "onFling  ", "velocityY:", String.valueOf(velocityY));<br>            mVelocityY = velocityY;<br>            return false;<br>        }<br>    });<br>    mChildView.setOnTouchListener(new View.OnTouchListener() {<br>        @Override<br>        public boolean onTouch(View v, MotionEvent event) {<br>            //手势监听的两个任务:1.监听fling动作,获取速度  2.监听滚动状态变化<br>            return gestureDetector.onTouchEvent(event);<br>        }<br>    });<br>    if (mChildView instanceof RecyclerView) {<br>        ((RecyclerView) mChildView).addOnScrollListener(new RecyclerView.OnScrollListener() {<br>            @Override<br>            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {<br>                if (newState == RecyclerView.SCROLL_STATE_IDLE) {<br>                    //2、到达越界速度阈值后,执行fling的顶部越界操作<br>                    if (mVelocityY >= OVER_SCROLL_MIN_VX && ScrollingUtil.isRecyclerViewToTop((RecyclerView) mChildView)) {<br>                        cp.getAnimProcessor().animOverScrollTop(mVelocityY, cur_delay_times);<br>                        mVelocityY = 0;<br>                        cur_delay_times = ALL_DELAY_TIMES;<br>                    }<br>                    if (mVelocityY <= -OVER_SCROLL_MIN_VX && ScrollingUtil.isRecyclerViewToBottom((RecyclerView) mChildView)) {<br>                        cp.getAnimProcessor().animOverScrollBottom(mVelocityY, cur_delay_times);<br>                        mVelocityY = 0;<br>                        cur_delay_times = ALL_DELAY_TIMES;<br>                    }<br>                }<br>                super.onScrollStateChanged(recyclerView, newState);<br>            }<br>        });<br>    }<br>}

 

执行越界动画

底部越界和顶部越界代码类似,此处只讲解顶部越界即可

 

1
<br>/**<br> * 执行顶部越界<br> * 越界高度height ∝ vy/computeTimes,此处采用的模型是height=A*(vy + B)/computeTimes<br> */<br>public void animOverScrollTop(float vy, int computeTimes) {<br>    LogUtil.d(TAG, "animOverScrollTop ", "vy:", String.valueOf(vy), "computeTimes:", String.valueOf(computeTimes));<br>    if (cp.isOsTopLocked()) return;<br>    cp.lockOsTop();<br>    cp.setStatePTD();<br>    //1、计算越界高度,最大不超过设定的越界高度<br>    int oh = (int) Math.abs(vy / computeTimes / 2);<br>    final int overHeight = Math.min(oh, cp.getOsHeight());<br>    //2、计算越界时间<br>    final int time = overHeight <= 50 ? 115 : (int) (0.3 * overHeight + 100);<br>    //3、执行越界操作<br>    animLayoutByTime(0, overHeight, time, overScrollTopUpListener, new AnimatorListenerAdapter() {<br>        @Override<br>        public void onAnimationEnd(Animator animation) {<br>            //4、执行回弹操作<br>            animLayoutByTime(overHeight, 0, 2 * time, overScrollTopUpListener, new AnimatorListenerAdapter() {<br>                @Override<br>                public void onAnimationEnd(Animator animation) {<br>                    cp.releaseOsTopLock();<br>                }<br>            });<br>        }<br>    });<br>}<br>//最终执行的就是个属性动画<br>public void animLayoutByTime(int start, int end, long time, AnimatorUpdateListener listener, AnimatorListener animatorListener) {<br>    ValueAnimator va = ValueAnimator.ofInt(start, end);<br>    va.setInterpolator(new DecelerateInterpolator());<br>    va.addUpdateListener(listener);<br>    va.addListener(animatorListener);<br>    va.setDuration(time);<br>    va.start();<br>}

 

以上就是 app直播源码,列表越界后自动弹回原样的效果实现的相关代码,更多内容欢迎关注之后的文章

 

posted @   云豹科技-苏凌霄  阅读(67)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示