用途

我们经常会看到scrollview带下拉上拉功能,提供页面刷新。现在我们总结以下。

效果与原理

我们知道,scrollView可以提供滚动功能,如果超出屏幕就可以滚动显示,当滚动到顶部或者底部的时候切换拖动。

当滚动到底部时,又有两种情况,一种是手没有离开,在拖动,这样的话就是一般的拖动方法。另外一种是滑动之后松手,屏幕开始滚动,滚动到边界,这种情况不是正常的上拉下拉,但是我们可以给一个弹性,给人一种惯性弹出屏幕的感觉。安卓的scrollview自带效果是滚动到底端时,会闪出一道神奇的蓝光,然并卵,所以我们自己加了这个惯性弹出效果。

  • 拖动效果
  • android 切换下排序方式的下拉 安卓下拉屏幕怎么设置_自定义控件

  • 滚动效果
  • android 切换下排序方式的下拉 安卓下拉屏幕怎么设置_Android控件_02

实现流程

在以下两种情况下出现弹性效果

  • 1.自然滚动超越屏幕
    复写函数:onOverScrolled
    STATUS_SCROLLOVER
    if 手离开屏幕 && Y轴有滚动
    ——–只做滚出然后弹回的动画效果autoScrollAnimation
  • 2.拖动超越屏幕(或者外层容器)
    复写函数:onTouchEvent
    if 滚动距离+scrollview外边框高度==内部控件的实际高度(即滚动到最底下时拖动) || y轴没有滚动(长度不够滚动时拖动)
    ——–上拉 STATUS_DRAG
    —————拉动+松手回调+弹回动画reductAnimation
    ——–下拉 STATUS_PULL
    —————拉动+松手回调+弹回动画reductAnimation
  • 动画完毕恢复状态STATUS_NORMAL
关键问题
  • 1.关于控件移动的方法
本例程控件移动使用控件的setTranslationY方法
  • 2.判断由滚动切换到拖动需要的数据
获取滚动距离           getScrollY()
   scrollview外边框高度   getHeight()         此处需要区别
   scrollview的高度是屏幕内可见的范围
  内部控件的实际高度     内部控件.getMeasuredHeight();         
  scrollview中真正长的其实是scrollview内部的控件
关键代码

复写onOverScrolled监听滑动过界

/**
     * 自然滚动超越屏幕
     * @param scrollX
     * @param scrollY
     * @param clampedX
     * @param clampedY
     */
    @Override
    protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
        super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);

        Log.i("ElasticScrollView1", "scrollY " + scrollY + "  clampedY " + clampedY + "  " + mIsTouch);
        //1.手未接触,滚动到底
        if(!mIsTouch && clampedY){

            Log.i("ElasticScrollView1", "scroll over!!!");

            mCurrentStatus = STATUS_SCROLLOVER;
            //向下滚动距离
            int dataLength = Math.abs(mVelocityY/50);
            //向下滚动时间
            int dataTime = Math.abs(mVelocityY/50);

            if(scrollY < 0 && mPullDownEnable){            //向下滚动  滑到顶端
                autoScrollAnimation(mChildView, dataLength, dataTime);
            }else if(scrollY > 0 && mDragUpEnable){     //向上滚动  滑到底端
                autoScrollAnimation(mChildView, -dataLength, dataTime);
            }
        }
        mScrollViewMeasuedHeight = mChildView.getMeasuredHeight();
    }

复写onTouchEvent监听拖动事件

@Override
    public boolean onTouchEvent(MotionEvent ev) {

        int action = ev.getAction();

        mYScrollLengh = this.getScrollY();
        switch (action){
            case MotionEvent.ACTION_DOWN:

                mIsTouch = true;                     //触摸标志位置位
                mDownY = (int) ev.getY();             //按下Y轴基准
                mViewHeight = this.getHeight();      //获取当前ScrollView的高度
                mScrollViewMeasuedHeight = mChildView.getMeasuredHeight();//获取内层控件的高度

                mCurrentStatus = STATUS_NORMAL;
                mYDrahLengh = 0;                     //Y轴拖动距离复位

                break;
            case MotionEvent.ACTION_MOVE:
                mMoveY = (int) ev.getY();

                mYDrahLengh = mDownY - mMoveY;   //获取Y轴真实拖动距离
                mYDrahLengh = mYDrahLengh/2;     //转换为比例拖动距离
                //上拉
                if(mYDrahLengh > 0 && mDragUpEnable) {
                    //两种情况,切换到拉动
                    //1.mYScrollLengh == 0  scrollview中内容较短,没有滑动起来 时拉动
                    //2.mYScrollLengh + mViewHeight == mScrollViewMeasuedHeight    
                    //滚动,并且滚动到头 时拉动
                    //当滑动距离+scrollView == scrollView内部控件高度,说明滚动到头,切换为
                    拉动
                    if (mYScrollLengh + mViewHeight == mScrollViewMeasuedHeight ||
                     (mYScrollLengh == 0)) { //上拉
                        if(mCurrentStatus == STATUS_NORMAL){
                            mStartMoveY = mMoveY;
                        }
                        mCurrentStatus = STATUS_DRAG;
                        //根据拖到距离设置控件实际移动距离
                        mMoveLength = (mStartMoveY - mMoveY)/2;
                        //移动控件
                        mChildView.setTranslationY(-mMoveLength);
                    }
                }else if(mYDrahLengh < 0 && mPullDownEnable){     //下拉

                    //如果滚动距离为0  则切换为下拉状态
                    if(mYScrollLengh == 0) {
                        if(mCurrentStatus == STATUS_NORMAL){
                            mStartMoveY = mMoveY;
                        }
                        mCurrentStatus = STATUS_PULL;

                        mMoveLength = (mStartMoveY - mMoveY)/2;
                        mChildView.setTranslationY(-mMoveLength);
                    }
                }
                break;
            case MotionEvent.ACTION_UP:

                if(mCurrentStatus == STATUS_DRAG){
                    reductAnimation(mChildView, mMoveLength);
                }else if(mCurrentStatus == STATUS_PULL){
                    reductAnimation(mChildView, mMoveLength);
                }

                mIsTouch = false;
                break;
        }
        return super.onTouchEvent(ev);
    }