Scrollview回弹效果自定义控件

滚动回弹效果分析:

首先,创建一个类,继承scrollview,重写ontouch事件,实现伸缩回弹效果。

scroollview节点下只能有一个子节点,这个子节点就是我们要移动的view布局。

第一步:获取要操作的子view布局

第二步:重写onTouch事件监听

分析具体事件:

观察分析得出结论:

让布局移动每一次拉动的Y轴一半的距离,然后松手滚动[携带动画]回到原来的位置。

下拉或者上拉的时候,记录按下时的Y轴位置

action_down

移动过程中的处理:

计算上一次与本次的Y轴(拉动距离)[而不是按下时候的Y值,和现在移动到的Y值,是每上一次和本次的Y值比较

判断是否需要移动布局的情况:Y轴的一个距离偏移

//2种情况,随着布局的拖动, inner.getMeasuredHeight()的值是变化的

//inner.getMeasuredHeight()与getHeight()的区别:

当屏幕可以包裹内容的时候,他们的值相等

当view的高度超出屏幕时,getMeasuredHeight()是实际View的大小,与屏幕无关,getHeight的大小此时则是屏幕的大小。

此时,getMeasuredHeight() = getHeight+超出部分。

抬起的处理:布局回滚到正常位置

移动动画回滚到正常位置(*:动画执行期间,不允许拖拉操作)   

距离:-的滚动距离

public class MyScrollview extends ScrollView {

    //要操作的布局
    private View innerView;
    private float y;
    private Rect normal = new Rect();
    private boolean animationFinish = true;

    public MyScrollview(Context context) {
        super(context, null);
    }

    public MyScrollview(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MyScrollview(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onFinishInflate() {
        int childCount = getChildCount();
        if (childCount > 0) {
            innerView = getChildAt(0);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (innerView == null) {
            return super.onTouchEvent(ev);
        } else {
            commonTouchEvent(ev);
        }
        return super.onTouchEvent(ev);
    }

    /**
     * 自定义touch事件处理
     *
     * @param ev
     */
    private void commonTouchEvent(MotionEvent ev) {
        if (animationFinish) {
            int action = ev.getAction();
            switch (action) {
                case MotionEvent.ACTION_DOWN:
                    y = ev.getY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    float preY = y == 0 ? ev.getY() : y;
                    float nowY = ev.getY();
                    int detailY = (int) (preY - nowY);
                    y = nowY;
                    //操作view进行拖动detailY的一半
                    if (isNeedMove()) {
                        //布局改变位置之前,记录一下正常状态的位置
                        if (normal.isEmpty()) {
                            normal.set(innerView.getLeft(), innerView.getTop(), innerView.getRight(), innerView.getBottom());
                        }
                        innerView.layout(innerView.getLeft(), innerView.getTop() - detailY / 2, innerView.getRight(), innerView.getBottom() - detailY / 2);
                    }
                    break;
                case MotionEvent.ACTION_UP:
                    y = 0;
                    //布局回滚到原来的位置
                    if (isNeedAnimation()) {
                        animation();
                    }
                    break;
            }
        }
    }

    private void animation() {
        TranslateAnimation ta = new TranslateAnimation(0, 0, 0, normal.top - innerView.getTop());
        ta.setDuration(200);
        ta.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                animationFinish = false;
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                innerView.clearAnimation();
                innerView.layout(normal.left, normal.top, normal.right, normal.bottom);
                normal.setEmpty();
                animationFinish = true;
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });
        innerView.startAnimation(ta);
    }

    /**
     * 判断是否需要回滚
     *
     * @return
     */
    private boolean isNeedAnimation() {
        return !normal.isEmpty();
    }

    /**
     * 判断是否需要移动
     *
     * @return
     */
    private boolean isNeedMove() {
        int offset = innerView.getMeasuredHeight() - getHeight();
        int scrollY = getScrollY();
        Log.e("zoubo", "getMeasuredHeight:" + innerView.getMeasuredHeight() + "----getHeight:" + getHeight());
        Log.e("zoubo", "offset:" + offset + "----scrollY:" + scrollY);
        if (scrollY == 0 || scrollY == offset) {
            return true;
        }
        return false;
    }
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏向治洪

listview滑动删除

今天还是给大家带来自定义控件的编写,自定义一个ListView的左右滑动删除Item的效果,这个效果之前已经实现过了,有兴趣的可以看下Android 使用Scr...

2477
来自专栏向治洪

SliferMenu详解

SlidingMenu简介: SlidingMenu的是一种比较新的设置界面或配置界面效果,在主界面左滑或者右滑出现设置界面,能方便的进行各种操作.目前有大...

1985
来自专栏Android知识点总结

2-VIII--ViewPager滑动监听与自定义滑动特效

1171
来自专栏Android源码框架分析

三句代码创建全屏Dialog或者DialogFragment:带你从源码角度实现全屏Dialog

Dialog是APP开发中常用的控件,同Activity类似,拥有独立的Window窗口,但是Dialog跟Activity还是有一定区别的,最明显的就是:默认...

3354
来自专栏向治洪

滑动开关按钮SlideSwich

iphone上有开关控件,很漂亮,其实android4.0以后也有switch控件,但是只能用在4.0以后的系统中,这就失去了其使用价值,而且我觉得它的界面也...

34910
来自专栏郭霖

Android滑动菜单框架完全解析,教你如何一分钟实现滑动菜单特效

之前我向大家介绍了史上最简单的滑动菜单的实现方式,相信大家都还记得。如果忘记了其中的实现原理或者还没看过的朋友,请先去看一遍之前的文章 Android滑动菜单特...

3236
来自专栏向治洪

android 仿音悦台页面交互效果

概述 新版的音悦台 APP 播放页面交互非常有意思,可以把播放器往下拖动,然后在底部悬浮一个小框,还可以左右拖动,然后回弹的时候也会有相应的效果,这种交互效果在...

2507
来自专栏Android开发经验

ScrollView里面基于某个View弹出PopupWindow,PopupWindow不会跟着View滚动?

1702
来自专栏程序员叨叨叨

听说你想用ViewPager实现这样的效果?

此图盗于https://github.com/smallnew/FuCardPager

983
来自专栏向治洪

仿淘宝购买详情页购买缩小动画

偶尔一个时候,我们产品的详情页面也做的和淘宝神识,为了加强 的体验,我们加了一些动画,下面说说淘宝详情的缩放详情页的动画怎么做的吧。 先上两张图, ? ? 其实...

2088

扫码关注云+社区

领取腾讯云代金券