自定义Scrollview

简介:

Scrollview 的作用实际上就是将视图中不能完全展示的界面进行包裹然后可以实现滚动来将剩余不能展示的界面在视图中可以通过滑动来进行展示
Scrollview 默认的情况下 是 垂直来滚动的 , 如果需要水平滚动的话,需要设置滚动方式为 HorizontalScrollview 就可以了, 其用法 和垂直类似 , 在这里我值介绍 垂直的用法

首先创建一个类 继承 Scrollview , 然后重写里面的的 三个 方法

public class CustomScrollview extends ScrollView{
    private View view;
    private float y;//记录y轴坐标
    private Rect normal = new Rect(); //矩形框
    private boolean animationFinish = true;  //默认动画可以拖动

    public CustomScrollview (Context context) {
        super(context);
    }

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

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

重写完上面的三个方法的时候 要在下面重写onFinishInflate 方法 意思是
当Scrollview 绘制完毕之后,编程view对象进行操纵的时候回调的方法
可以在里面写

@Override
    protected void onFinishInflate() {
    //获取子view 如果子view 的个数 是大于0 的  就可以获取要操纵的自布局
    int childCount = getChildCount();
        if (childCount > 0) {
            view= getChildAt(0);
        }
    }

获取完子view 就要重写他的 onTouchEvent方法 ,要先判断子view是否为空,如果为空,没有子view 那么什么都不做 将原来的onTouchEvent触摸事件的方法返回回去, 如果有子view 就将自己定义的触摸事件 返回回去

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

自定义触摸事件

  private void myTouchEvent(MotionEvent ev) {
       if (animationFinish) {
            int action = ev.getAction();
            switch (action) {
                //当手指按下时候
                case MotionEvent.ACTION_DOWN:
                    y = ev.getY();//记录y轴坐标
                    break;
                //当手指移动的时候
                case MotionEvent.ACTION_MOVE:
        //按下时候得点 和最后抬起来时候的点 之间的距离}
                    float preY = y == 0 ? ev.getY() : y;
                    //滑动之后当前的位置
                    float nowY = ev.getY();
                    /**
                     * 直到最后拖动完 位置不在变化时 , 计算之后得到滑动的距离
                     */
                    int detailY = (int) (preY - nowY);
                    y = nowY; //将最后移动的点的位置 设置给 Y
                    //操作view进行拖动detailY的一半
                    if (isNeedMove()) { //判断是否需要移动
                        //布局改变位置之前,记录一下正常状态的位置
                        if (normal.isEmpty()) {//判断矩形框是否为空 ,如果矩形框为空,就记录矩形框上下左右的位置
                            normal.set(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
                        }
                        //移动时候位置的变化 (上下左右)
                        view.layout(view.getLeft(), view.getTop() - detailY / 2, view.getRight(), view.getBottom() - detailY / 2);
                    }
                    break;
                //当手指抬起的时候
                case MotionEvent.ACTION_UP:
                    y = 0; //当抬起来的时候将 Y 的值 设置为0
                    //布局回滚到原来的位置
                    if (isNeedAnimation()) { //先判断界面是否移动过, 如果没有移动过就不需要回滚, 只有移动过才回滚
                        //以动画的形式回滚到原来的位置
                        animation();
                    }
                    break;
                default:
                    break;
            }
        }
    }
    /**
     * 动画效果
     */
    private void animation() {
        TranslateAnimation ta = new TranslateAnimation(0, 0, 0, normal.top - view.getTop());
        ta.setDuration(200);
        ta.setAnimationListener(new Animation.AnimationListener() {
            //动画执行开始时调用的方法
            @Override
            public void onAnimationStart(Animation animation) {
                //在动画执行
                animationFinish = false;
            }
            //动画结束是调用的方法
            @Override
            public void onAnimationEnd(Animation animation) {
                //将之前开启的动画清除掉
                view.clearAnimation();
                //回到矩形框正常的位置
                view.layout(normal.left, normal.top, normal.right, normal.bottom);
                //清空矩形框
                normal.setEmpty();
                //将动画设置为可拖动状态
                animationFinish = true;
            }
            //动画重复时调用的方法
            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });
        //开启动画
        view.startAnimation(ta);
    }

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


当手指按下之后 是向上 或者 向下 滑动的时候 才确定是 下拉或上拉的状态
如果手指是 左右的 那么 不 不是上啦或这上啦的状态

getMeasuredHeight() 与 getHeight() 的 区别
getMeasuredHeight()是指的原始的测量高度 与屏幕无关 而
getHeight() 是在屏幕上显示的高度, 实际上当屏幕可以包裹内容的时候 他们的值是相等的

只有当 view视图 超出屏幕后 getMeasuredHeight() 就等于 getHeight()加上超出屏幕之外没有显示的高度的 和
用当前的测量的高度 减去 整个scrollview的高度 得到的是高度 的偏移量

当手指上拉拖动的时候 , 拖动之后最后停止的位置
getScrollY() 指的是 Y轴的滚动距离 当内容的高度小于 屏幕显示的高的时候
getScrollY()的值 始终是0 只有当内容的高度大于 屏幕显示的高度的时候
向上拖动 getScrollY()的值 才会变化
getScrollY() 的测量标准是 手机屏幕显示区域的左上角的坐标

//判断是否需要移动
    private boolean isNeedMove() {
        int offset = view.getMeasuredHeight() - getHeight();
        int scrollY = getScrollY();
        //scrollY只有当 等于0  或者  等于 偏移量的时候 确定是在上下滑动
        if (scrollY == 0 || scrollY == offset) {
            return true;
        }
        return false;
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值