Scroller的学习

前言

一直对Scroller这个类不太熟悉,之前老是在网上找着看,但是过不了多长时间后就忘记了,今天来整理一下
先看一下Scroller里面的方法:
http://api.apkbus.com/reference/android/widget/Scroller.html

这里写图片描述

说明

为了理解方便,拿SlideView来做说明,关于SlideView的demo网上有很多,这里为了讲解主要贴出SlideView:

public class SlideView extends LinearLayout {
      private Context mContext;

        private LinearLayout mViewContent;

        private LinearLayout mHolder;

        private TextView tv_delete;

        // 弹性滑动对象,实现View平滑滚动的一个帮助类
        private Scroller mScroller;

        // 滑动回调接口,用来向上层通知滑动事件
        private OnSlideListener mOnSlideListener;

        private int mHolderWidth = 100;

        private int mLastX = 0;

        private int mLastY = 0;

        private static final int TAN = 2;


        public SlideView(Context context) {
            super(context);
            initView();
        }

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


        private void initView(){
            mContext = getContext();
            mScroller = new Scroller(mContext);
            setOrientation(LinearLayout.HORIZONTAL);
            setGravity(Gravity.CENTER_VERTICAL);
            //将R.layout.slide_view 添加到this, View view=View.inflate(this,R.layout.*,null);是生成一个新的View
            View.inflate(mContext, R.layout.slide_view, this);
            mViewContent = (LinearLayout)findViewById(R.id.view_content);
            mHolder = (LinearLayout)findViewById(R.id.holder);

            tv_delete = (TextView)findViewById(R.id.delete);

        }


        public void setButtonText(CharSequence text){
            tv_delete.setText(text);
        }


        public void setContentView(View view){
            mViewContent.addView(view);
        }

        public void onRequireTouchEvent(MotionEvent event){
                // 获取点击的坐标
                int x = (int)event.getX();
                int y = (int)event.getY();
                //当前view的左上角相对于母视图的左上角的X轴偏移量。
                int scrollX = getScrollX();
                switch (event.getAction()){
                    case MotionEvent.ACTION_DOWN:
                        if(!mScroller.isFinished()){
                            mScroller.abortAnimation();
                        }
                        if(mOnSlideListener != null){
                            mOnSlideListener.onSlide(this, OnSlideListener.SLIDE_STATUS_START_SCROLL);
                        }

                        break;

                    case MotionEvent.ACTION_MOVE:
                        //增量
                        int deltaX = x - mLastX;
                        int deltaY = y - mLastY;
                        if(Math.abs(deltaX) < Math.abs(deltaY)*TAN){
                            // 滑动不满足条件 不做横向滑动
                            break;
                        }
                            /**
     * 1. 这个SlideView相对于上次偏移的距离减去手指这次move的相对于上次move的增量(相对偏移量?)
     *
     * 2. 举个例子:如果现在手指滑动,ACTION_DOWN的坐标为(100,0),现在开始MOVE滑动(ACTION_MOVE)第一个MOVE的坐标为(50,0)
     *
     *    即deltaX=50-100,第一个MOVE时scrollX = 0,newScrollX = scrollX - deltaX;newScrollX = 50
     *    this.scrollTo(newScrollX, 0);view是偏移50,
     *
     *    第二个MOVE的到达坐标为(10,0),而mLastX是第一个MOVE的x坐标(mLastX = 50)
     *    deltaX=10-50 ,deltaX = -40 ;scrollX = 50  newScrollX = scrollX - deltaX;newScrollX = 90
     *    this.scrollTo(newScrollX, 0);view是偏移90,
     *
     */
                        int newScrollX = scrollX - deltaX;
                        if(deltaX != 0){
                            if(newScrollX < 0){
                                newScrollX = 0;
                            }else if(newScrollX > mHolderWidth){
                                newScrollX = mHolderWidth;
                            }
                            //会触发computeScroll()
                            this.scrollTo(newScrollX, 0);
                        }

                        break;

                    case MotionEvent.ACTION_UP:
                        int newScrollx = 0;
                        //如果SlideView的偏移量大于默认要滑动距离的3/4,newScrollx=mHolderWidth,否则 newScrollx = 0;
                        if(scrollX - mHolderWidth*0.75 > 0){
                            newScrollx = mHolderWidth;
                        }

                        this.smoothScrollTo(newScrollx, 0);
                        // 通知上层滑动事件
                        if(mOnSlideListener != null){
                            mOnSlideListener.onSlide(this, newScrollx == 0 ? OnSlideListener.SLIDE_STATUS_OFF
                                    : OnSlideListener.SLIDE_STATUS_ON);
                        }

                        break;

                    default:
                        break;
                }

                mLastX = x;
                mLastY = y;
        }


        /**
         * 调用此方法滚动到目标位置
         * @param fx  目标x坐标
         * @param fy  目标Y坐标
         */
        private void smoothScrollTo(int fx, int fy){
            int scrollX = getScrollX();
            int dx = fx - scrollX;

            int scrollY = getScrollY();
            int dy = fy - scrollY;
            //手指up之后从现在的位置偏移到scrollX+dx位置,dx是增量,触发computeScroll
            mScroller.startScroll(scrollX, scrollY, dx, dy, Math.abs((dx)*3));

            invalidate();
        }


        /**
         * 由mScroller记录/计算好View滚动的位置后,最后由View的computeScroll(),完成实际的滚动
         */
        @Override
        public void computeScroll() {
            //先判断mScroller滚动是否完成
            if(mScroller.computeScrollOffset()){
                //这里调用View的scrollTo()完成实际的滚动
                scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
                //必须调用该方法,否则不一定能看到滚动效果
                postInvalidate();
            }
            super.computeScroll();
        }


        /**
         * 设置滑动回调
         * @param onSlideListener
         */
        public void setOnSlideListener(OnSlideListener onSlideListener){
            this.mOnSlideListener = onSlideListener;
        }


        public interface OnSlideListener {
            public static final int SLIDE_STATUS_OFF = 0;
            public static final int SLIDE_STATUS_START_SCROLL = 1;
            public static final int SLIDE_STATUS_ON = 2;

            public void onSlide(View view, int status);
        }

}

先说一下实现的具体思路,SlideView是一个横向LinearLayout,作为父布局,它里面有两个child(当然可以更多),第一个child设置宽度充满整个父布局,这样的第二个child就会被挤出屏幕外面,scrollTo()和scrollBy()方法移动的是View的content,即让View的内容移动,如果在ViewGroup中使用scrollTo()和scrollBy()方法,那么移动的将是所有的子View,如果在View中使用,那么移动的将是View的内容。例如,TextView,content就是它的文本,ImageView,content就是它的drawable对象。

具体讲解一下:
1.ACTION_DOWN:mScroller停止动画,这个没什么说的

2.ACTION_MOVE:举个例子:SlideView已经偏移了scrollX,scrollTo是相对于原点偏移,getScrollX却是向对于开始时的位置

3.ACTION_UP:在UP之前可以看到mScroller没有做任何关于滚动的的动作,up的时候调用了
private void smoothScrollTo(int fx, int fy){
int scrollX = getScrollX();
int dx = fx - scrollX;

        int scrollY = getScrollY();
        int dy = fy - scrollY;
        //手指up之后从现在的位置偏移到scrollX+dx位置,dx是增量,触发computeScroll
        mScroller.startScroll(scrollX, scrollY, dx, dy, Math.abs((dx)*3));

        invalidate();
    }

触发了computeScroll()
@Override
public void computeScroll() {
//先判断mScroller滚动是否完成
if(mScroller.computeScrollOffset()){
//这里调用View的scrollTo()完成实际的滚动
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
//必须调用该方法,否则不一定能看到滚动效果
postInvalidate();
}
super.computeScroll();
}
computeScroll里面做了什么??先mScroller.computeScrollOffset()判断mScroller滚动是否完成,computeScrollOffset做了什么呢?
public boolean computeScrollOffset() {
if (mFinished) {
return false;
}

    int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);

    if (timePassed < mDuration) {
        switch (mMode) {
        case SCROLL_MODE:
            float x = timePassed * mDurationReciprocal;

            if (mInterpolator == null)
                x = viscousFluid(x); 
            else
                x = mInterpolator.getInterpolation(x);

            mCurrX = mStartX + Math.round(x * mDeltaX);
            mCurrY = mStartY + Math.round(x * mDeltaY);
            break;

}

public void startScroll(int startX, int startY, int dx, int dy, int duration) {
    mMode = SCROLL_MODE;
    mFinished = false;
    mDuration = duration;
    mStartTime = AnimationUtils.currentAnimationTimeMillis();
    mStartX = startX;
    mStartY = startY;
    mFinalX = startX + dx;
    mFinalY = startY + dy;
    mDeltaX = dx;
    mDeltaY = dy;
    mDurationReciprocal = 1.0f / (float) mDuration;
}

可以看出int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);mStartTime = AnimationUtils.currentAnimationTimeMillis();获取过了多长时间,
然后if (timePassed < mDuration) ,看一下有没有到达设置的时间,
然后一通计算mCurrX = mStartX + Math.round(x * mDeltaX);(计算是为了在mDuration时间内完成滚动dx)得到mCurrX
,然后回到computeScroll(), scrollTo(mScroller.getCurrX(), mScroller.getCurrY());在次偏移mCurrX ,
然后回调computeScroll,直到滚动结束

注意:scrollTo(),如果第一次你在x轴向左上偏移了10,下次再向左偏移10的话,相当于较之原点你已经移到了20,所以说getscrollx也是基于开始位置的偏移,现在getscrollx是20,scrollBy()是相对于你上次移动

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值