/

滑动结束 Activity(二)阴影效果和动画的实现


上一篇文章中讲了如何实现手势滑动来销毁页面,再来回顾一下实现的效果 
这里写图片描述 
具体实现请看上一篇文章 手势滑动结束 Activity(一)基本功能的实现,不过这只是实现了最基本的功能,还有很多地方需要优化和完善的,这篇文章主要是在原来实现的基础上做优化和特效; 
先来看效果:

1、效果图1:侧滑显示阴影

这里写图片描述

2、效果图2:改变滑动动画效果

这里写图片描述

先来看看效果图1的实现方式: 
根据效果图,我们应该能想到他的实现原理,就是在手势不断的滑动的同时绘制已经滑走部分的颜色,并且不断改变透明度。 
所以我们需要重写 ViewGroup 中的 drawShadow(Canvas canvas) 方法,在里面绘制我们要显示的遮罩层,

    /**
     * 绘制背景颜色,随着距离的改变而改变
     * @param canvas
     */
    private void drawBackground(Canvas canvas) {
        mShadowPaint.setAlpha((int) ((1 - mRatio) * 180));
        canvas.drawRect(-mPix, 0, 0, getHeight(), mShadowPaint);
    }12345678

这里有三个变量我们还不知道的,mRatio、mPix、mShadowPaint; 
1、mShadowPaint:是绘制阴影的画笔,这个我们可以直接在初始化的时候 new 出来,并设置画笔的颜色为黑色,画出来的图就是黑色的。

        mShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mShadowPaint.setColor(0xff000000);12

2、mRatio:滑动的梯度值,但开始滑动的时候这个值为0,而滑动到最大值的时候,也就是 Activity 页面小时不见了时,这个值为1,所以这个值的范围0.0f~1.0f。所以通过一下代码来改变画笔的透明度,使得在滑动的过程中,阴影层会逐渐变化

mShadowPaint.setAlpha((int) ((1 - mRatio) * 180));1

3、mPix:这个变量是滑动距离原始位置的距离。通俗点讲就是已经滑动了多远的距离,可以通过这个值来确定绘制阴影部分的面积

canvas.drawRect(-mPix, 0, 0, getHeight(), mShadowPaint);1
    @Override
    protected void dispatchDraw(Canvas canvas) {        super.dispatchDraw(canvas);        if (mShouldDraw){
            drawBackground(canvas);
        }
    }1234567

以上代码只是绘制了滑动部分的阴影,如果仔细看了效果图1的朋友会发现在被滑走页面的边缘也存在一条渐变的阴影,这又怎么绘制呢? 
首先,定义一个 Drawable 资源文件left_shadow.xml

<?xml version="1.0" encoding="utf-8"?>
    <!--形状为矩形--><shape android:shape="rectangle"
  xmlns:android="http://schemas.android.com/apk/res/android">
    <!--颜色渐变范围-->
    <gradient android:startColor="#00000000" android:endColor="#33000000" /></shape>12345678

接着绘制 Drawable,在绘制 Drawable 之前先要设置其大小

    /**
     * 绘制shadow阴影
     * @param canvas
     */
    private void drawShadow(Canvas canvas){        /*保存画布当前的状态,这个用法在我前面将自定义View 的时候将的很详细*/
        canvas.save() ;        /*设置 drawable 的大小范围*/
        mLeftShadow.setBounds(0, 0, mShadowWidth, getHeight());        /*让画布平移一定距离*/
        canvas.translate(-mShadowWidth,0);        /*绘制Drawable*/
        mLeftShadow.draw(canvas);        /*恢复画布的状态*/
        canvas.restore();
    }1234567891011121314151617

所以dispatchDraw(Canvas canvas)的方法实现为

    @Override
    protected void dispatchDraw(Canvas canvas) {        super.dispatchDraw(canvas);        if (mShouldDraw){            /*绘制偏移的背影颜色*/
            drawBackground(canvas);            /*绘制边缘的阴影*/
            drawShadow(canvas) ;
        }
    }12345678910

到这里就完成了效果图1的功能实现。

滑动动画的实现: 
在实现可滑动的自定义 ViewGroup 的时候,我把滑动的距离做了一个接口回调出来,类似 ViewPage控件的 PageChange 监听事件,这里也对页面滑动做了滑动监听,虽然只有一页,但是有了这个监听事件,对做很多操作都十分方便,比如我们前面讲到的 mRatio、mPix 都可以直接通过监听滑动事件来获取,接口定义如下:

    public interface OnPageChangeListener {        /*滑动页面滑动状态,当前页和页面的偏移梯度,页面的偏移位置*/
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels);        /*当前页面*/
        public void onPageSelected(int position);
    }1234567

接口调用通过重写scrollTo(int x, int y)方法来获取滑动监听的参数

    @Override
    public void scrollTo(int x, int y) {        super.scrollTo(x, y);        /*设置回调事件的值*/
        pageScrolled(x);
    }123456
    private void pageScrolled(int xpos) {        final int widthWithMargin = getWidth();        /*获取当前页面*/
        int position = Math.abs(xpos) / widthWithMargin;        /*获取当前滑动的距离*/
        final int offsetPixels = Math.abs(xpos) % widthWithMargin;        /*通过滑动的距离来获取梯度值*/
        final float offset = (float) offsetPixels / widthWithMargin;        /*这里需要做特殊处理,因为只有一个页面*/
        position = mIsBeingDragged ? 0 : position;
        onPageScrolled(position, offset, offsetPixels);
    }123456789101112
    protected void onPageScrolled(int position, float offset, int offsetPixels) {        if (mListener != null) {
            mListener.onPageScrolled(position, offset, offsetPixels);
        }
        mInternalPageChangeListener.onPageScrolled(position, offset, offsetPixels);
    }123456

动画效果的实现我这里做了处理,放在了 SlidingLayout类中实现

/**
 * Created by moon.zhong on 2015/3/13.
 */public class SlidingLayout extends FrameLayout {

    /*可根据手势滑动的View*/
    private SlidingView mSlidingView ;    /*需要绑定的Activity*/
    private Activity mActivity ;    /*滑动View 的滑动监听*/
    private SlidingView.OnPageChangeListener mPageChangeListener ;    /*这个是当Activity 滑动到可以结束的时候用到的常量*/
    private final int POSITION_FINISH = 1 ;    /*页面的View*/
    private View mContextView  ;    /*用于定制自己的动画效果的接口*/
    private OnAnimListener mAnimListener ;    public SlidingLayout(Context context) {        this(context, null);
    }    public SlidingLayout(Context context, AttributeSet attrs) {        this(context, attrs, 0);
    }    public SlidingLayout(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        /*初始化可滑动的View*/
        mSlidingView = new SlidingView(context) ;        /*吧可滑动的View 添加到当前Layout 中*/
        addView(mSlidingView);        /*设置滑动监听*/
        mPageChangeListener = new SlidingOnPageChangeListener() ;
        mSlidingView.setOnPageChangeListener(mPageChangeListener);
        mActivity = (Activity) context;        /*绑定Activity 到可滑动的View 上面*/
        bindActivity(mActivity) ;
    }    /**
     * 侧滑View 和Activity 绑定
     * @param activity
     */
    private void bindActivity(Activity activity){        /*获取Activity 的最顶级ViewGroup*/
        ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();        /*获取Activity 显示内容区域的ViewGroup,包行ActionBar*/
        ViewGroup child = (ViewGroup) decorView.getChildAt(0);
        decorView.removeView(child);
        setContentView(child) ;
        decorView.addView(this);
    }    private void setContentView(View view){
        mContextView = view ;
        mSlidingView.setContent(view);

    }    private class SlidingOnPageChangeListener implements SlidingView.OnPageChangeListener{
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {            /*到达了结束Activity 的条件*/
            if (position == POSITION_FINISH){                /*结束当前 */
                mActivity.finish();
            }            if (mAnimListener != null){
                mAnimListener.onAnimationSet(mContextView,positionOffset,positionOffsetPixels);
            }
        }        @Override
        public void onPageSelected(int position) {

        }
    }    /**
     * 设置自己定义的动画
     * @param listener
     */
    public void setOnAnimListener(OnAnimListener listener){
        mAnimListener = listener ;
        mSlidingView.setShouldDraw(false);
    }    /**
     * 定制动画的接口
     */
    public interface OnAnimListener {
        /**
         * 重写这个方法来定制动画
         * @param view
         * @param offSet
         * @param offSetPix
         */
        void onAnimationSet(View view, float offSet,int offSetPix) ;
    }    /**
     * 默认的动画效果
     */
    public static class SimpleAnimImpl implements OnAnimListener{
        private final int MAX_ANGLE = 25 ;        @Override
        public void onAnimationSet(View view, float offSet,int offSetPix) {
            view.setPivotX(view.getWidth()/2.0F);
            view.setPivotY(view.getHeight());
            View.ROTATION.set(view,MAX_ANGLE*offSet);
        }
    }

}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114

Activity 中应用:

public class SecondActivity extends ActionBarActivity {;

    SlidingLayout mSlidingLayout ;    @Override
    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        mSlidingLayout = new SlidingLayout(this);        /*设置默认的动画效果*/
        mSlidingLayout.setOnAnimListener(new SlidingLayout.SimpleAnimImpl());
    }

}12345678910111213

运行就是我们看到的效果图2的动画效果;

如果想要自定义动画该怎么做呢,只要在 Activity 中设置

mSlidingLayout.setOnAnimListener(OnAnimListener anim)1

anim需要自己实现,来看看。

public class SecondActivity extends ActionBarActivity {;

    SlidingLayout mSlidingLayout ;    @Override
    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        mSlidingLayout = new SlidingLayout(this) ;        /*设置自己定义的动画效果*/
        mSlidingLayout.setOnAnimListener(new SlidingLayout.OnAnimListener() {            final int MAX_DEGREE = 180 ;            @Override
            public void onAnimationSet(View view, float offSet, int offSetPix) {
                view.setPivotX(view.getWidth()/2.0F);
                view.setPivotY(view.getHeight() / 2.0F);
                View.TRANSLATION_X.set(view, (float) -offSetPix);
                View.ROTATION_Y.set(view,MAX_DEGREE*offSet);
                view.setAlpha((1-offSet));
            }
        });

    }

}12345678910111213141516171819202122232425

运行效果 
这里写图片描述

下面把可滑动的自定义 ViewGroup 的代码贴出来

/**
 * Created by moon.zhong on 2015/3/13.
 */public class SlidingView extends ViewGroup {

    /*无效的点*/
    private static final int INVALID_POINTER = -1;    /*滑动动画执行的时间*/
    private static final int MAX_SETTLE_DURATION = 600; // ms
    /*最小滑动距离,结合加速度来判断需要滑动的方向*/
    private static final int MIN_DISTANCE_FOR_FLING = 25; // dips
    /*定义了一个时间插值器,根据ViewPage控件来定义的*/
    private static final Interpolator sInterpolator = new Interpolator() {        public float getInterpolation(float t) {
            t -= 1.0f;            return t * t * t * t * t + 1.0f;
        }
    };    /*内容View*/
    private View mContent;    /*是否开始滑动*/
    private boolean mIsBeingDragged;    /*是否停止滑动*/
    private boolean mIsUnableToDrag;    /*自动滚动工具*/
    private Scroller mScroller;    /*判断是否已经在滚动*/
    private boolean mScrolling;    /*共外面调用的监听事件*/
    private OnPageChangeListener mListener;    /*内部监听事件*/
    private OnPageChangeListener mInternalPageChangeListener;    /*记录上一次手指触摸的点*/
    private float mLastMotionX;    private float mLastMotionY;    /*记录最初触摸的点*/
    private float mInitialMotionX;    /*当前活动的点Id,有效的点的Id*/
    protected int mActivePointerId = INVALID_POINTER;    /*加速度工具类*/
    protected VelocityTracker mVelocityTracker;    /*最大加速度的值*/
    private int mMinMunVelocity;    /*最小加速度的值*/
    private int mMaxMunVelocity;    /*滑动的距离*/
    private int mFlingDistance;    /*开始滑动的标志距离*/
    private int mTouchSlop;    /*是否可以触摸滑动的标志*/
    private boolean isEnable = true;    /*没有滑动,正常显示*/
    private int mCurItem = 0;    /*用于绘制阴影时的梯度变化*/
    private float mRatio;    /*页面滑动的距离*/
    private int mPix;    /*绘制阴影背景的画笔*/
    private Paint mShadowPaint;    /*页面边缘的阴影图*/
    private Drawable mLeftShadow ;    /*页面边缘阴影的宽度默认值*/
    private final int SHADOW_WIDTH = 6 ;    /*页面边缘阴影的宽度*/
    private int mShadowWidth ;    /*这个标志是用来判断是否需要绘制阴影效果*/
    private boolean mShouldDraw = true;    public SlidingView(Context context) {        this(context, null);
    }    public SlidingView(Context context, AttributeSet attrs) {        this(context, attrs, 0);
    }    public SlidingView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);
        initCustomView(context);
    }    private void initCustomView(Context context) {
        setWillNotDraw(false);
        mScroller = new Scroller(context, sInterpolator);
        mInternalPageChangeListener = new InternalPageChangeListener();
        mShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mLeftShadow = getResources().getDrawable(R.drawable.left_shadow) ;

        mShadowPaint.setColor(0xff000000);        final ViewConfiguration configuration = ViewConfiguration.get(context);
        mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);
        mMinMunVelocity = configuration.getScaledMinimumFlingVelocity();
        mMaxMunVelocity = configuration.getScaledMaximumFlingVelocity();        final float density = context.getResources().getDisplayMetrics().density;
        mFlingDistance = (int) (MIN_DISTANCE_FOR_FLING * density);
        mShadowWidth = (int) (SHADOW_WIDTH*density);
    }    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {        final int width = r - l;        final int height = b - t;
        mContent.layout(0, 0, width, height);
    }    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {        if (!isEnable) {            return false;
        }        final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP
                || action != MotionEvent.ACTION_DOWN && mIsUnableToDrag) {
            endToDrag();            return false;
        }        switch (action) {            case MotionEvent.ACTION_DOWN:                /*计算 x,y 的距离*/
                int index = MotionEventCompat.getActionIndex(ev);
                mActivePointerId = MotionEventCompat.getPointerId(ev, index);                if (mActivePointerId == INVALID_POINTER)                    break;
                mLastMotionX = mInitialMotionX = MotionEventCompat.getX(ev, index);
                mLastMotionY = MotionEventCompat.getY(ev, index);                /*这里判读,如果这个触摸区域是允许滑动拦截的,则拦截事件*/
                if (thisTouchAllowed(ev)) {
                    mIsBeingDragged = false;
                    mIsUnableToDrag = false;
                } else {
                    mIsUnableToDrag = true;
                }                if (!mScroller.isFinished()) {
                    startDrag();
                }                break;            case MotionEvent.ACTION_MOVE:               /*继续判断是否需要拦截*/
                determineDrag(ev);                break;            case MotionEvent.ACTION_UP:                break;            case MotionEvent.ACTION_POINTER_UP:                /*这里做了对多点触摸的处理,当有多个手指触摸的时候依然能正确的滑动*/
                onSecondaryPointerUp(ev);                break;

        }        if (!mIsBeingDragged) {            if (mVelocityTracker == null) {
                mVelocityTracker = VelocityTracker.obtain();
            }
            mVelocityTracker.addMovement(ev);
        }        return mIsBeingDragged;
    }    @Override
    public boolean onTouchEvent(MotionEvent event) {        if (!isEnable) {            return false;
        }        if (!mIsBeingDragged && !thisTouchAllowed(event))            return false;        final int action = event.getAction();        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain();
        }
        mVelocityTracker.addMovement(event);        switch (action & MotionEventCompat.ACTION_MASK) {            case MotionEvent.ACTION_DOWN:                /*按下则结束滚动*/
                completeScroll();                int index = MotionEventCompat.getActionIndex(event);
                mActivePointerId = MotionEventCompat.getPointerId(event, index);
                mLastMotionX = mInitialMotionX = event.getX();                break;            case MotionEventCompat.ACTION_POINTER_DOWN: {                /*有多个点按下的时候,取最后一个按下的点为有效点*/
                final int indexx = MotionEventCompat.getActionIndex(event);
                mLastMotionX = MotionEventCompat.getX(event, indexx);
                mActivePointerId = MotionEventCompat.getPointerId(event, indexx);                break;

            }            case MotionEvent.ACTION_MOVE:                if (!mIsBeingDragged) {
                    determineDrag(event);                    if (mIsUnableToDrag)                        return false;
                }                /*如果已经是滑动状态,则根据手势滑动,而改变View 的位置*/
                if (mIsBeingDragged) {                    // 以下代码用来判断和执行View 的滑动
                    final int activePointerIndex = getPointerIndex(event, mActivePointerId);                    if (mActivePointerId == INVALID_POINTER)                        break;                    final float x = MotionEventCompat.getX(event, activePointerIndex);                    final float deltaX = mLastMotionX - x;
                    mLastMotionX = x;                    float oldScrollX = getScrollX();                    float scrollX = oldScrollX + deltaX;                    final float leftBound = getLeftBound();                    final float rightBound = getRightBound();                    if (scrollX < leftBound) {
                        scrollX = leftBound;
                    } else if (scrollX > rightBound) {
                        scrollX = rightBound;
                    }

                    mLastMotionX += scrollX - (int) scrollX;
                    scrollTo((int) scrollX, getScrollY());

                }                break;            case MotionEvent.ACTION_UP:                /*如果已经是滑动状态,抬起手指,需要判断滚动的位置*/
                if (mIsBeingDragged) {
                    mIsBeingDragged = false;                    final VelocityTracker velocityTracker = mVelocityTracker;
                    velocityTracker.computeCurrentVelocity(1000, mMaxMunVelocity);                    int initialVelocity = (int) VelocityTrackerCompat.getXVelocity(
                            velocityTracker, mActivePointerId);                    final int scrollX = getScrollX();                    final float pageOffset = (float) (-scrollX) / getContentWidth();                    final int activePointerIndex = getPointerIndex(event, mActivePointerId);                    if (mActivePointerId != INVALID_POINTER) {                        final float x = MotionEventCompat.getX(event, activePointerIndex);                        final int totalDelta = (int) (x - mInitialMotionX);                         /*这里判断是否滚动到下一页,还是滚回原位置*/
                        int nextPage = determineTargetPage(pageOffset, initialVelocity, totalDelta);
                        setCurrentItemInternal(nextPage, true, initialVelocity);
                    } else {
                        setCurrentItemInternal(mCurItem, true, initialVelocity);
                    }
                    mActivePointerId = INVALID_POINTER;
                    endToDrag();
                } else {//                    setCurrentItemInternal(0, true, 0);
                    scrollTo(getScrollX(), getScrollY());
                    endToDrag();
                }                break;            case MotionEventCompat.ACTION_POINTER_UP:
                onSecondaryPointerUp(event);                int pointerIndex = getPointerIndex(event, mActivePointerId);                if (mActivePointerId == INVALID_POINTER)                    break;
                mLastMotionX = MotionEventCompat.getX(event, pointerIndex);                break;
        }        return true;
    }    public void setOnPageChangeListener(OnPageChangeListener listener) {
        mListener = listener;
    }    private float getLeftBound() {        return -mContent.getWidth();
    }    private float getRightBound() {        return 0;
    }    private int getContentWidth() {        return mContent.getWidth();
    }    public void setEnable(boolean isEnable) {        this.isEnable = isEnable;
    }    /**
     * 通过事件和点的 id 来获取点的索引
     * @param ev
     * @param id
     * @return
     */
    private int getPointerIndex(MotionEvent ev, int id) {        int activePointerIndex = MotionEventCompat.findPointerIndex(ev, id);        if (activePointerIndex == -1)
            mActivePointerId = INVALID_POINTER;        return activePointerIndex;
    }    /**
     * 结束拖拽
     */
    private void endToDrag() {
        mIsBeingDragged = false;
        mIsUnableToDrag = false;
        mActivePointerId = INVALID_POINTER;        if (mVelocityTracker != null) {
            mVelocityTracker.recycle();
            mVelocityTracker = null;
        }
    }    /**
     * 可以拖拽
     */
    private void startDrag() {
        mIsBeingDragged = true;
    }    /**
     * 这里是多多点触控的控制
     * @param ev
     */
    private void onSecondaryPointerUp(MotionEvent ev) {        final int pointerIndex = MotionEventCompat.getActionIndex(ev);        final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);        if (pointerId == mActivePointerId) {            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
            mLastMotionX = MotionEventCompat.getX(ev, newPointerIndex);
            mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);            if (mVelocityTracker != null) {
                mVelocityTracker.clear();
            }
        }
    }    /**
     * 决定是否可以拖拽
     * @param event
     */
    private void determineDrag(MotionEvent event) {        /*这么一大串代码只有一个目的,就是用来获取和判断手指触摸的位置*/
        int pointIndex = MotionEventCompat.getActionIndex(event);        int pointId = MotionEventCompat.getPointerId(event, pointIndex);        if (pointId == INVALID_POINTER) {            return;
        }        final float x = MotionEventCompat.getX(event, pointIndex);        final float y = MotionEventCompat.getY(event, pointIndex);        final float dx = x - mLastMotionX;        final float dy = y - mLastMotionY;        final float xDiff = Math.abs(dx);        final float yDiff = Math.abs(dy);        /*如果滑动的距离大于我们规定的默认位置,并且水平滑动的幅度大于垂直滑动的幅度,则说明可以滑动此View*/
        if (xDiff > mTouchSlop && xDiff > yDiff && thisSlideAllowed(dx)) {
            startDrag();
            mLastMotionX = x;
            mLastMotionY = y;
        } else if (xDiff > mTouchSlop) {
            mIsUnableToDrag = true;
        }
    }    /**
     * 这个方法是待扩展用的,主要是想要来过滤某些特殊情况
     * @param ev
     * @return
     */
    private boolean thisTouchAllowed(MotionEvent ev) {        return true;
    }    /**
     * 如果手势是向有滑动返回为 true
     * @param dx
     * @return
     */
    private boolean thisSlideAllowed(float dx) {        return dx > 0;
    }    @Override
    public void computeScroll() {        if (!mScroller.isFinished()) {            if (mScroller.computeScrollOffset()) {                int oldX = getScrollX();                int oldY = getScrollY();                int x = mScroller.getCurrX();                int y = mScroller.getCurrY();                if (oldX != x || oldY != y) {
                    scrollTo(x, y);//                    pageScrolled(x);
                }


                invalidate();                return;
            }
        }


        completeScroll();
    }    private void pageScrolled(int xpos) {        final int widthWithMargin = getWidth();        /*获取当前页面*/
        int position = Math.abs(xpos) / widthWithMargin;        /*获取当前滑动的距离*/
        final int offsetPixels = Math.abs(xpos) % widthWithMargin;        /*通过滑动的距离来获取梯度值*/
        final float offset = (float) offsetPixels / widthWithMargin;        /*这里需要做特殊处理,因为只有一个页面*/
        position = mIsBeingDragged ? 0 : position;
        onPageScrolled(position, offset, offsetPixels);
    }    protected void onPageScrolled(int position, float offset, int offsetPixels) {        if (mListener != null) {
            mListener.onPageScrolled(position, offset, offsetPixels);
        }
        mInternalPageChangeListener.onPageScrolled(position, offset, offsetPixels);
    }     private void setCurrentItemInternal(int item, boolean smoothScroll, int velocity) {
        mCurItem = item;        final int destX = getDestScrollX(item);        if (mListener != null) {
            mListener.onPageSelected(mCurItem);
        }
        mInternalPageChangeListener.onPageSelected(mCurItem);        if (smoothScroll) {            /*执行滑动滚动*/
            smoothScrollTo(destX, 0, velocity);
        } else {            /*结束滑动滚动*/
            completeScroll();            /*直接滚动到指定位置*/
            scrollTo(destX, 0);
        }
    }    /**
     * 根据当前页面来获取需要滚动的目的位置
     * @param page
     * @return
     */
    public int getDestScrollX(int page) {        switch (page) {            case 0:                return mContent.getLeft();            case 1:                return -mContent.getRight();
        }        return 0;
    }    /**
     * 通过偏移位置和加速度来确定需要滚动的页
     * @param pageOffset
     * @param velocity
     * @param deltaX
     * @return
     */
    private int determineTargetPage(float pageOffset, int velocity, int deltaX) {        int targetPage = 0;        /*这里判断是否需要滚动到下一页*/
        if (Math.abs(deltaX) > mFlingDistance && Math.abs(velocity) > mMinMunVelocity) {            if (velocity > 0 && deltaX > 0) {
                targetPage = 1;
            } else if (velocity < 0 && deltaX < 0) {
                targetPage = 0;
            }
        } else {            /*根据距离来判断滚动的页码*/
            targetPage = (int) Math.round(targetPage + pageOffset);
        }        return targetPage;
    }    private void completeScroll() {        boolean needPopulate = mScrolling;        if (needPopulate) {
            mScroller.abortAnimation();            int oldX = getScrollX();            int oldY = getScrollY();            int x = mScroller.getCurrX();            int y = mScroller.getCurrY();            if (oldX != x || oldY != y) {//                scrollTo(x, y);
            }
        }

        mScrolling = false;
    }    @Override
    public void scrollTo(int x, int y) {        super.scrollTo(x, y);        /*设置回调事件的值*/
        pageScrolled(x);
    }    private void smoothScrollTo(int x, int y, int velocity) {        if (getChildCount() == 0) {            return;
        }        int sx = getScrollX();        int sy = getScrollY();        int dx = x - sx;        int dy = y - sy;        if (dx == 0 && dy == 0) {
            completeScroll();            /*这里为了解决一个bug,当用手指触摸滑到看不见的时候再用力滑动,如果不做此操作,那么不会回调position = 1*/
            scrollTo(sx,sy);            return;
        }

        mScrolling = true;        final int width = getContentWidth();        final int halfWidth = width / 2;        final float distanceRatio = Math.min(1f, 1.0f * Math.abs(dx) / width);        final float distance = halfWidth + halfWidth *
                distanceInfluenceForSnapDuration(distanceRatio);        int duration = 0;
        velocity = Math.abs(velocity);        if (velocity > 0) {
            duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
        } else {            final float pageDelta = (float) Math.abs(dx) / width;
            duration = (int) ((pageDelta + 1) * 100);
            duration = MAX_SETTLE_DURATION;
        }
        duration = Math.min(duration, MAX_SETTLE_DURATION);        /*开始自定滚动到指定的位置*/
        mScroller.startScroll(sx, sy, dx, dy, duration);
        invalidate();
    }    float distanceInfluenceForSnapDuration(float f) {
        f -= 0.5f;
        f *= 0.3f * Math.PI / 2.0f;        return (float) FloatMath.sin(f);
    }    public void setContent(View v) {        if (mContent != null)            this.removeView(mContent);
        mContent = v;
        addView(mContent);
    }    public View getContentView(){        return mContent ;
    }    public void setShouldDraw(boolean shouldDraw){
        mShouldDraw = shouldDraw ;
    }    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        /*获取默认的宽度*/
        int width = getDefaultSize(0, widthMeasureSpec);        /*获取默认的高度*/
        int height = getDefaultSize(0, heightMeasureSpec);        /*设置ViewGroup 的宽高*/
        setMeasuredDimension(width, height);        /*获取子 View 的宽度*/
        final int contentWidth = getChildMeasureSpec(widthMeasureSpec, 0, width);        /*获取子View 的高度*/
        final int contentHeight = getChildMeasureSpec(heightMeasureSpec, 0, height);        /*设置子View 的大小*/
        mContent.measure(contentWidth, contentHeight);
    }    @Override
    protected void dispatchDraw(Canvas canvas) {        super.dispatchDraw(canvas);        if (mShouldDraw){            /*绘制偏移的背影颜色*/
            drawBackground(canvas);            /*绘制边缘的阴影*/
            drawShadow(canvas) ;
        }
    }    /**
     * 绘制背景颜色,随着距离的改变而改变
     * @param canvas
     */
    private void drawBackground(Canvas canvas) {
        mShadowPaint.setAlpha((int) ((1 - mRatio) * 180));
        canvas.drawRect(-mPix, 0, 0, getHeight(), mShadowPaint);
    }    /**
     * 绘制shadow阴影
     * @param canvas
     */
    private void drawShadow(Canvas canvas){        /*保存画布当前的状态,这个用法在我前面将自定义View 的时候将的很详细*/
        canvas.save() ;        /*设置 drawable 的大小范围*/
        mLeftShadow.setBounds(0, 0, mShadowWidth, getHeight());        /*让画布平移一定距离*/
        canvas.translate(-mShadowWidth,0);        /*绘制Drawable*/
        mLeftShadow.draw(canvas);        /*恢复画布的状态*/
        canvas.restore();
    }    private class InternalPageChangeListener implements OnPageChangeListener {
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {            if (mShouldDraw){
                mRatio = positionOffset;
                mPix = positionOffsetPixels;
                invalidate();
            }
        }        @Override
        public void onPageSelected(int position) {

        }
    }    public interface OnPageChangeListener {

        /*滑动页面滑动状态,当前页和页面的偏移梯度,页面的偏移位置*/
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels);        /*当前页面*/
        public void onPageSelected(int position);
    }
}


发布评论

热门评论区: