Android-SwipeToDismiss 源码分析

一、Android-SwipeToDismiss简介

(一)github地址

    https://github.com/romannurik/Android-SwipeToDismiss

(二)效果

这里写图片描述

解释一下github提供的demo,左边的是一列button,右边的是一个listview,当点击button或者listview的item时就会toast出“xxx被点击”之类的信息;但左滑或者右滑button或者listview的item时,该button或者item就会以动画的形式消失。

二、代码分析

(一)项目目录

这里写图片描述

可以见到,只有三个文件,非常简单,SwipeDismissListViewTouchListener.java是实现右边listView滑动使item消失的功能;
SwipeDismissTouchListener.java是实现左边button滑动消失的功能;

(二)代码详解

(1)SwipeDismissTouchListener.java


 public SwipeDismissTouchListener(View view, Object token, DismissCallbacks callbacks) {
        ViewConfiguration vc = ViewConfiguration.get(view.getContext());
        mSlop = vc.getScaledTouchSlop();
        mMinFlingVelocity = vc.getScaledMinimumFlingVelocity() * 16;
        mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
        mAnimationTime = view.getContext().getResources().getInteger(
                android.R.integer.config_shortAnimTime);
        mView = view;
        mToken = token;
        mCallbacks = callbacks;
    }

一个类我们从构造器看起,在构造器中进行一系列的初始化:
mslop是判断是否产生了move的最小距离;
mMinFlingVelocity和 mMaxFlingVelocity分别是判断是否发生了fling(手指离开屏幕但是继续惯性滑动)的上下限;
mView是SwipeDismissTouchListener监听的主体,可以看到在MAinActivity中,button是作为参数被传进来后赋值给mView;
mCallback是实现了内部接口DismissCallbacks的实例,代码如下:

public interface DismissCallbacks {
        /**
         * Called to determine whether the view can be dismissed.
         */
        boolean canDismiss(Object token);

        /**
         * Called when the user has indicated they she would like to dismiss the view.
         *
         * @param view  The originating {@link View} to be dismissed.
         * @param token The optional token passed to this object's constructor.
         */
        void onDismiss(View view, Object token);
    }

它的作用就是在成功去除button后进行通知用户。后面能看到具体的运用。


既然SwipeDismissTouchListener implements View.OnTouchListener,必然要重写

public boolean onTouch(View view, MotionEvent motionEvent)

motionEvent.offsetLocation(mTranslationX, 0);//因为手指在

接下来会进入一个switch-case结构,至于motionevent.getAction()与motionEvent.getActionMask()的区别可以从链接中找到答案:
http://my.oschina.net/banxi/blog/56421

——>首先看ACTION_DOWN:
在这里面先获得点击的坐标,然后进行一次if判断,这里的mCallbacks.canDismiss(mToken)的返回值能在MainActivity中找到答案:

 new SwipeDismissListViewTouchListener.DismissCallbacks() {
                            @Override
                            public boolean canDismiss(int position) {
                                return true;
                            }

                            @Override
                            public void onDismiss(ListView listView, int[] reverseSortedPositions) {
                                for (int position : reverseSortedPositions) {
                                    mAdapter.remove(mAdapter.getItem(position));
                                }
                                mAdapter.notifyDataSetChanged();
                            }

可以看到总是返回true,所以就进入if体内;接下的

 mVelocityTracker.addMovement(motionEvent);

作用是告诉mVelocityTracker去跟进这个motionEvent,相当于一个登记的操作。

—–>接着我们看ACTION_MOVE: 注意看注释

            case MotionEvent.ACTION_MOVE: {
                if (mVelocityTracker == null) {
                    break;
                }

                mVelocityTracker.addMovement(motionEvent);
                float deltaX = motionEvent.getRawX() - mDownX;//找出x方向上每次的移动距离
                float deltaY = motionEvent.getRawY() - mDownY;//找出y方向上每次的移动距离

                if (Math.abs(deltaX) > mSlop && Math.abs(deltaY) < Math.abs(deltaX) / 2) {
                //如果x方向上的距离能被判断为移动且x方向上的移动远大于y方向上的移动,就认定为水平方向上的滑动
                    mSwiping = true;//正在发生滑动
                    mSwipingSlop = (deltaX > 0 ? mSlop : -mSlop);
                    mView.getParent().requestDisallowInterceptTouchEvent(true);
//请求父容器不要拦截事件
                    // Cancel listview's touch
                    MotionEvent cancelEvent = MotionEvent.obtain(motionEvent);
                    cancelEvent.setAction(MotionEvent.ACTION_CANCEL |
                            (motionEvent.getActionIndex() <<
                                    MotionEvent.ACTION_POINTER_INDEX_SHIFT));
                    mView.onTouchEvent(cancelEvent);//触碰mVeiw时,除了SwipeDismissTouchListener的onTouch方法会被调用,mView本身的onTouchEvent也会被调用,所以要防止onTouchEvent操作的干扰,便发送一个cancelEvent事件过去

                    cancelEvent.recycle();
                }

                if (mSwiping) {
                    mTranslationX = deltaX;
                    mView.setTranslationX(deltaX - mSwipingSlop);
                    // TODO: use an ease-out interpolator or such
                    mView.setAlpha(Math.max(0f, Math.min(1f,
                            1f - 2f * Math.abs(deltaX) / mViewWidth)));
                    return true;
                }
                break;
            }

注意的是,其中的这段代码:

                if (mSwiping) {
                    mTranslationX = deltaX;
                    mView.setTranslationX(deltaX - mSwipingSlop);
                    // TODO: use an ease-out interpolator or such
                    mView.setAlpha(Math.max(0f, Math.min(1f,
                            1f - 2f * Math.abs(deltaX) / mViewWidth)));
                    return true;
                }

实现的动画是发生在手指还在屏幕上拖动时,所以mView会跟着持续移动,并且逐渐透明。

—–>ACTION_UP:
当手指抬起之时,可能会发生以下的情况:

第一种:Math.abs(deltaX) > mViewWidth / 2 && mSwiping:
判断mView是否已经移动距离超过自身宽度的一半且此时正在滑动,如果为真,认为用户打算dimiss,则dimiss=true;

第二种:mMinFlingVelocity <= absVelocityX && absVelocityX <= mMaxFlingVelocity
&& absVelocityY < absVelocityX
&& absVelocityY < absVelocityX && mSwiping:
判断水平方向上的速度是否在fling的范围中且x轴方向速度要快于y轴方向且此时正在滑动,如果为真, 也可认为用户打算dimiss,则dismiss = (velocityX < 0) == (deltaX < 0); // dismiss only if flinging in the same direction as dragging

第三种:前两种情况都为false,则发生这种情况就是cancel的情况:

if (mSwiping) {
                    // cancel
                    mView.animate()
                            .translationX(0)
                            .alpha(1)
                            .setDuration(mAnimationTime)
                            .setListener(null);
                }

这时候就mView复位;

如果前两种情况为真,则

                if (dismiss) {
                    // dismiss
                    mView.animate()
                            .translationX(dismissRight ? mViewWidth : -mViewWidth)
                            .alpha(0)
                            .setDuration(mAnimationTime)
                            .setListener(new AnimatorListenerAdapter() {
                                @Override
                                public void onAnimationEnd(Animator animation) {
                                    performDismiss();
                                }
                            });
                } 

则继续启动后续的动画,完成dimiss操作,并在动画完成后调用了 performDismiss();

那么,这个 performDismiss();是干什么的呢?

    private void performDismiss() {
        // Animate the dismissed view to zero-height and then fire the dismiss callback.
        // This triggers layout on each animation frame; in the future we may want to do something
        // smarter and more performant.

        final ViewGroup.LayoutParams lp = mView.getLayoutParams();
        final int originalHeight = mView.getHeight();

        ValueAnimator animator = ValueAnimator.ofInt(originalHeight, 1).setDuration(mAnimationTime);

        animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                mCallbacks.onDismiss(mView, mToken);
                // Reset view presentation
                mView.setAlpha(1f);
                mView.setTranslationX(0);
                lp.height = originalHeight;
                mView.setLayoutParams(lp);
            }
        });

        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                lp.height = (Integer) valueAnimator.getAnimatedValue();
                mView.setLayoutParams(lp);
            }
        });

        animator.start();
    }
}

明显,他是在dimiss的动画完成后,后面的item会向上挪一个位置(除了最后一个),这个方法就是实现这个动画的。

(2)SwipeDismissListViewTouchListener
SwipeDismissListViewTouchListener的实现大体相同,大家可以对照着来学习

谢谢大家!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值