你见过微信侧滑返回的联动效果,但开门效果、百叶窗效果见过吗?

SmartSwipe是一个Android侧滑处理框架,它封装了对控件侧滑事件(上/下/左/右4个方向滑动的手势事件)的捕获、分发及多点交替滑动的处理,基于SmartSwipe咱们能够为控件添加各类你想要的侧滑效果。html

先来看看它能作些什么吧!java

若是已经了解SmartSwipe的功能,只是想了解他的实现原理
可跳过第一节,直接看第二节的原理介绍
复制代码

1、 用法及演示

1.1 一行代码实现全局侧滑返回

//仿手机QQ的手势滑动返回
SmartSwipeBack.activityStayBack(application, null);		
//仿微信带联动效果的透明侧滑返回
SmartSwipeBack.activitySlidingBack(application, null);	
//侧滑开门样式关闭activity
SmartSwipeBack.activityDoorBack(application, null);		
//侧滑百叶窗样式关闭activity
SmartSwipeBack.activityShuttersBack(application, null);	
//仿小米MIUI系统的贝塞尔曲线返回效果
SmartSwipeBack.activityBezierBack(application, null);
复制代码

侧滑返回的更多用法请戳 这里android

效果图:git

侧滑返回效果

1.2 一行代码让页面动起来

//为控件添加仿iOS的弹性留白效果:
//当纵向不能滚动(或滚动到顶/底)时,若继续拖动,则UI呈现弹性留白效果,释放后平滑恢复
SmartSwipe.wrap(view)
    .addConsumer(new SpaceConsumer())
    .enableVertical();
复制代码

效果图:github

弹性留白效果

1.3 一行代码让页面具备弹性

//为控件添加仿MIUI的弹性拉伸效果:
//当纵向不能滚动(或滚动到顶/底)时,若继续拖动,则UI呈现弹性拉伸效果,释放后平滑恢复
SmartSwipe.wrap(view)
    .addConsumer(new StretchConsumer())
    .enableVertical();
复制代码

效果图:bash

弹性拉伸效果

1.4 一行代添加下拉刷新

//xxxMode第二个参数为false,表示工做方向为纵向:下拉刷新&上拉加载更多
//若是第二个参数设置为true,则表示工做方向为横向:右拉刷新&左拉加载更多
SmartSwipeRefresh.drawerMode(view, false).setDataLoader(loader);
SmartSwipeRefresh.behindMode(view, false).setDataLoader(loader);
SmartSwipeRefresh.scaleMode(view, false).setDataLoader(loader);
SmartSwipeRefresh.translateMode(view, false).setDataLoader(loader);
复制代码

下拉刷新的更多用法请戳 这里微信

样式 效果图
drawerMode
drawerMode
behindMode
behindMode
scaleMode
scaleMode
translateMode
translateMode

1.5 一行代码添加滑动菜单

SmartSwipe.wrap(view)
    //添加抽屉效果,其效果与DrawerLayout类似
    // DrawerLayout只支持左右2个方向,而DrawerConsumer支持上下左右4个方向
    .addConsumer(new DrawerConsumer())	
    //设置横向(左右两侧)的抽屉为同一个view(常见的侧滑显示删除按钮的功能)
    .setHorizontalDrawerView(buttonsViewGroup) 
    .setScrimColor(0x2F000000) //设置遮罩的颜色
    .setShadowColor(0x80000000)	//设置边缘的阴影颜色
    ;
复制代码

效果图:app

滑动菜单

1.6 一行代码添加具备联动效果的滑动菜单

SmartSwipe.wrap(view)
    .addConsumer(new SlidingConsumer())
    .setRelativeMoveFactor(0.3F) //联动系数
    .setHorizontalDrawerView(buttonsView)
    .setScrimColor(0x2F000000)
    ;
复制代码

效果图:框架

可联动滑动菜单

1.7 炫酷的封面

SmartSwipe.wrap(coverView)
    .addConsumer(new ShuttersConsumer()) //百叶窗效果
    .setScrimColor(0xAF000000)
    .enableAllDirections()
    .addListener(new SimpleSwipeListener() {
        @Override
        public void onSwipeOpened(SmartSwipeWrapper wrapper, SwipeConsumer consumer, int direction) {
            //封面打开后自动隐藏或移除
            wrapper.setVisibility(View.GONE);
        }
    });
复制代码

效果图:ide

百叶窗封面

SmartSwipe.wrap(coverView)
    .addConsumer(new DoorConsumer()) //开门效果
    .setScrimColor(0xAF000000)
    .enableAllDirections()
    .addListener(new SimpleSwipeListener() {
        @Override
        public void onSwipeOpened(SmartSwipeWrapper wrapper, SwipeConsumer consumer, int direction) {
            //封面打开后自动隐藏或移除
            wrapper.setVisibility(View.GONE);
        }
    });
复制代码

效果图:

开门封面

关于封面的更多设置请参考: Demo

2、实现原理

2.1 先介绍一下ViewDragHelper

ViewDragHelper是Android官方支持库中有一个工具类。它能够帮助咱们处理控件的拖拽:先建立一个自定义ViewGroup,将被拖动的控件添加到这个自定义ViewGroup中,并用ViewDragHelper来处理控件的拖拽。

ViewDragHelper的主要做用是:拦截父容器的touch事件,捕获一个子控件来进行拖拽,经过改变这个子控件的left和top来将其在父容器中从新定位,从而达到拖拽的效果。

在官方支持库中,滑动抽屉相关的SlidingPaneLayout和DrawerLayout,以及CoordinatorLayout布局相关的BottomSheetBehavior和SwipeDismissBehavior,都能看到ViewDragHelper的身影。

可是,ViewDragHelper的名称也代表它就是用来处理拖拽的,拖拽的对象必须是一个子View,在拖拽的过程当中须要改变子控件的left和top,对于一些没有子View被拖拽的侧滑效果(例如:MIUI系统的贝塞尔曲线侧滑返回效果、手机QQ的侧滑返回效果及MIUI官方app中的广泛使用了的弹性拉伸效果等等),却有点力有不逮。

2.2 借鉴ViewDragHelper实现侧滑处理

针对侧滑这个手势,咱们能不能将它的概念抽象一下,到底侧滑指的是什么呢?

  • 狭义侧滑:从屏幕的某个边缘开始向着远离该边缘的方向滑动
  • 广义侧滑:手指在屏幕上按下以后向着某个方向滑动

个人理解是,广义侧滑包含狭义侧滑,只不过是触发区域是否在屏幕边缘的区别罢了。

既然侧滑手势能被明确地抽象出来,那么咱们是否能够借鉴ViewDragHelper的事件拦截思路将它作这样的封装?

对被侧滑控件的touch事件进行拦截分析,确认是否将其捕获做为侧滑手势
而后计算好侧滑的实时位移(手指滑动的位移,而不是不依赖于View的left与top)
再经过策略模式(Strategy Pattern)使用不一样的策略不断消费侧滑的位移来进行侧滑效果的UI呈现。
复制代码

答案是确定的!

2.3 SmartSwipe的实现原理

SmartSwipe在ViewDragHelper的基础上,将它对子View的捕获及移动处理改形成对父View自身触摸事件的定性(可否及是否捕获)、定向(捕获的事件所触发的侧滑方向)及定位(事件捕获以后在侧滑方向上移动的距离),并将侧滑距离交由SwipeConsumer来消费,SwipeConsumer根据侧滑距离的变化对控件布局进行相应的改变。

SmartSwipe的封装思路以下:

  • 用一个ViewGroup将须要处理侧滑事件的控件View包裹起来(被包裹起来的控件做为它的contentView
  • 能够为这个ViewGroup添加一些附属控件(如:滑动抽屉
  • 拦截这个ViewGroup的touch事件,并将touch事件转换为侧滑距离交给SwipeConsumer进行消费
  • SwipeConsumer根据侧滑距离的变化对控件布局进行相应的改变
  • 经过继承SwipeConsumer,用不一样的方式来改变控件布局(例如:对contentView及附属控件的位置、缩放、透明等进行改变),从而实现各类侧滑的效果。

因而,侧滑的手势事件识别及滑动距离计算的工做在框架内部就统一完成了,至于根据侧滑距离来实现各类不一样的UI呈现效果,就能够很方便地经过继承SwipeConsumer来实现了。

2.4 如何建立自定义SwipeConsumer?

以框架内置的仿MIUI系统应用中弹性拉伸效果的实现为例

根据侧滑距离,对contentView进行缩放和平移,从而实现弹性拉伸效果
复制代码

代码以下:

public class StretchConsumer extends SwipeConsumer {
    @Override
    public void onDetachFromWrapper() {
        super.onDetachFromWrapper();
        View contentView = mWrapper.getContentView();
        if (contentView != null) {
            contentView.setScaleX(1);
            contentView.setScaleY(1);
            contentView.setTranslationX(0);
            contentView.setTranslationY(0);
        }
    }

    @Override
    public void onDisplayDistanceChanged(int distanceXToDisplay, int distanceYToDisplay, int dx, int dy) {
        View contentView = mWrapper.getContentView();
        if (contentView != null) {
            if (distanceXToDisplay >= 0 && isLeftEnable() || distanceXToDisplay <= 0 && isRightEnable()) {
                contentView.setScaleX(1 + Math.abs((float) distanceXToDisplay) / mWidth);
                contentView.setTranslationX(distanceXToDisplay / 2F);
            }
            if (distanceYToDisplay >= 0 && isTopEnable() || distanceYToDisplay <= 0 && isBottomEnable()) {
                contentView.setScaleY(1 + Math.abs((float) distanceYToDisplay) / mHeight);
                contentView.setTranslationY(distanceYToDisplay / 2F);
            }
        }
    }
}
复制代码

以上就是实现弹性拉伸效果的所有代码,很简单,不是吗?

它的使用方式一样简单:

SmartSwipe.wrap(view) //指定目标控件
    .addConsumer(new StretchConsumer()) //添加弹性拉伸效果
    .enableVertical(); //指定工做方向为:上、下2个方向
复制代码

再来看看仿手机QQ侧滑返回的效果如何实现

手机QQ侧滑时UI没有任何变化
在手指释放时,根据滑动的方向和速率来决定是否finish当前Activity
复制代码

代码以下:

public class StayConsumer extends SwipeConsumer {
    private int mMinVelocity = 1000;

    public StayConsumer() {
        //不能经过滑动距离判断是否须要打开
        setOpenDistance(Integer.MAX_VALUE)
                .setMaxSettleDuration(0); //打开时无需动画,时间置为0
    }

    @Override
    protected void onDisplayDistanceChanged(int distanceXToDisplay, int distanceYToDisplay, int dx, int dy) {
        //滑动时不须要对contentView作任何改变
    }

    @Override
    public void onSwipeReleased(float xVelocity, float yVelocity) {
        //在释放时,根据速率和方向来决定是否打开
        if (Math.abs(xVelocity) > Math.abs(yVelocity)) {
            if (mDirection == DIRECTION_LEFT && xVelocity >= mMinVelocity || (mDirection == DIRECTION_RIGHT && xVelocity <= -mMinVelocity)) {
                //置为打开状态
                mCurSwipeDistanceX = getSwipeOpenDistance();
                mProgress = 1;
            }
        } else {
            if (mDirection == DIRECTION_TOP && yVelocity >= mMinVelocity || (mDirection == DIRECTION_BOTTOM && yVelocity <= -mMinVelocity)) {
                //置为打开状态
                mCurSwipeDistanceY = getSwipeOpenDistance();
                mProgress = 1;
            }
        }
        super.onSwipeReleased(xVelocity, yVelocity);
    }

    public int getMinVelocity() {
        return mMinVelocity;
    }

    //支持使用者设置最低速率的阈值
    public StayConsumer setMinVelocity(int minVelocity) {
        if (minVelocity > 0) {
            this.mMinVelocity = minVelocity;
        }
        return this;
    }
}
复制代码

是否是也很简单!

点击这里了解建立自定义SwipeConsumer的详细步骤

小结

本文介绍了SmartSwipe侧滑处理框架的使用方式及实现原理,并经过2个示例介绍了自定义侧滑效果的方法。

只是文中的示例是较为简单的侧滑效果,至于复杂的侧滑效果实现介绍,若是读者们须要的话,我接下来另外写一篇文章来单独介绍,若有须要,请给我留言!

另外,Star一个开源项目是对它最好的鼓励和支持!

相关文章
相关标签/搜索