DragCloseHelper

More: Author   ReportBugs   
Tags:

效果图:

1.demo 效果

image

2.视频效果,非 demo

image

使用步骤:

0.依赖

    allprojects {
            repositories {
                ...
                maven { url 'https://jitpack.io' }
            }
        }
    implementation 'com.github.bauer-bao:dragclosehelper:1.0.1'

1.activity 主题设为透明

    <item name="android:windowIsTranslucent">true</item>

2.初始化

    DragCloseHelper dragCloseHelper = new DragCloseHelper(this);

3.如果是共享元素启动的页面,需要如下设置(强烈建议和共享元素一起使用,否则是没有灵魂的)

    dragCloseHelper.setShareElementMode(true);

4.设置需要进行拖拽的 View/ViewGroup,以及背景 ViewGroup(必须要设置背景色)

    dragCloseHelper.setDragCloseViews(parentV, childV);

5.设置监听

    dragCloseHelper.setDragCloseListener(new DragCloseHelper.DragCloseListener() {
        @Override
        public boolean intercept() {
            //默认 false 不拦截。比如图片在放大状态,是不需要执行拖拽动画的等等。
            return false;
        }

        @Override
        public void dragStart() {
            //拖拽开始。可以在此额外处理一些逻辑
        }

        @Override
        public void dragging(float percent) {
            //拖拽中。percent 当前的进度,取值 0-1,可以在此额外处理一些逻辑
        }

        @Override
        public void dragCancel() {
            //拖拽取消,会自动复原。可以在此额外处理一些逻辑
        }

        @Override
        public void dragClose(boolean isShareElementMode) {
            //拖拽关闭,如果是共享元素的页面,需要执行 activity 的 onBackPressed 方法,注意如果使用 finish 方法,则返回的时候没有共享元素的返回动画
            if (isShareElementMode) {
                onBackPressed();
            }
        }
    });

    dragCloseHelper.setClickListener(new DragCloseHelper.ClickListener() {
        @Override
        public void onClick(View view, boolean isLongClick) {
             //isLongClick 是否为长按事件。建议长按使用库中此方法,单击不建议使用(建议直接写在宿主代码中)。
        }
    });

6.处理 touch 事件

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        if (dragCloseHelper.handleEvent(event)) {
            return true;
        } else {
            return super.dispatchTouchEvent(event);
        }
    }

7.可以自定义最大拖拽距离和最小缩放尺寸

    setMaxExitY(int maxExitY)
    setMinScale(@FloatRange(from = 0.1f, to = 1.0f) float minScale)

常见问题:

0.如果发现问题,欢迎提 issue。如果 issue 没法及时解决,欢迎直接复制 + 黏贴 + 修改。

1.是否支持在 fragment 中使用

不支持,可以将 fragment 层当做 childview 来处理

2.滑动关闭的过程中(手指脱离屏幕),view 会显示在虚拟导航栏上

使用如下代码,效果参考微信
//隐藏状态栏
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
//透明导航栏
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);

3.滑动关闭的过程中(手机没有脱离屏幕),上一个页面设置共享动画的 view 显示空白

方法 a.在滑动开始的事件回调中(dragStart 方法),使用 rxbus/eventbus 通知上一个页面,将目标的 view 的 alpha 设为 1(view.setAlpha(1f))
方法 b.见常见问题 7

4.滑动关闭的过程中(手机脱离屏幕,view 开始返回到上个页面),上一个页面设置共享动画的 view 显示空白

方法 a.在上一页面设置 setExitSharedElementCallback 监听,并在 onCaptureSharedElementSnapshot 回调中将 sharedElement 的 alpha 设为 1,代码如下
@Override
public Parcelable onCaptureSharedElementSnapshot(View sharedElement, Matrix viewToGlobalMatrix, RectF screenBounds) {
    sharedElement.setAlpha(1f);
    return super.onCaptureSharedElementSnapshot(sharedElement, viewToGlobalMatrix, screenBounds);
}
方法 b.见常见问题 7

5.虚拟键盘显示或者隐藏之后,共享动画有异常,其中肯定有段黑屏的过程

因为键盘显示或者隐藏,页面会重新创建,需要在 manifest 中的 activity 添加 android:configChanges="screenLayout"即可

6.类似微信朋友圈,点击图片预览,左右切换图片之后返回,动画不匹配

步骤 1.在图片预览,切换图片之后,需要将最新的索引值通知上一页面
步骤 2.上一页面接受到通知之后,更新索引值
步骤 3.在 onMapSharedElements 回调中更新 Map,代码如下
@Override
public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
    super.onMapSharedElements(names, sharedElements);
    //更新共享元素键值对
    View view = adapter.getViewByPosition(photosRv, updateIndex, R.id.rv_item_fake_iv);
    if (view != null) {
        sharedElements.put("share_photo", view);
    }
}

7.在滑动返回的过程中,出现种种的 view 显示空白的问题,可以统统使用此答案

步骤 1.在布局文件中,在目标 view 的底下,新建一个同样的 fakeView
步骤 2.将目标 view 的共享动画,全部转移设置到 fakeView 上
效果和微信朋友圈类似,不需要设置上面问题 3 和问题 4 的代码

8.如果用 viewpager,可能会出现 pointerIndex out of range pointerIndex=-1 pointerCount=1,需要 try-catch,或者使用 HackyViewPager

try {
    if (dragCloseHelper.handleEvent(event)) {
        return true;
    } else {
        return super.dispatchTouchEvent(event);
    }
} catch (IllegalArgumentException e) {
    e.printStackTrace();
    return true;
}

9.使用 ViewPager + com.github.chrisbanes.photoview.PhotoView,vp 的左右滑动和 pv 的缩放存在冲突

这个属于 vp 和 pv 的 bug,和本库没有任何关系,可以发现将滑动关闭注释掉之后,依然存在这问题。
如果不是 com.github.chrisbanes.photoview.PhotoView,则需要自己实现如下对应的方法。
处理方法:
步骤 1.使用自定义 HackyViewPager
步骤 2.photoview.setOnMatrixChangeListener,具体代码如下
imageView.setOnMatrixChangeListener(rect -> {
    //因为 demo 中此页面是全屏处理,所以需要获取整个屏幕的宽度(包括横屏的时候虚拟按键的宽度),如果非全屏,自己更改此处的宽度
    int screenW = ScreenUtil.getWidthDpi(this);
    /**
     * 因为发现加载完成之后,matrix 会和正常情况有误差,所以需要获取误差值,此处取系统误差值 getScaledTouchSlop
     * 但是 getScaledTouchSlop 默认是 8*系统密度,所以可以根据自己需求去设定
     *
     * 同时 scale 也会存在一定的误差,正常情况下可能在 0.99-1.01 之间
     */
    int delta = ViewConfiguration.get(this).getScaledTouchSlop() / 2;
    if (rect.width() < screenW) {
        //预览的时候,图片左右边界小于屏幕宽度 --> 此处存在三种情况,正常+缩小+放大(图片宽度还处于小于屏幕宽度的时候)
        if (imageView.getScale() < 0.99 || imageView.getScale() > 1.01) {
            //getscale 会存在误差,如果是缩小或者放大状态,拦截 vp 的事件
            viewPager.setLocked(true);
        } else {
            //属于正常状态,可以左右滑动
            viewPager.setLocked(false);
        }
    } else {
        //预览的时候,大于或者等于屏幕宽度 --> 对应的情况,正常 + 放大
        if (Math.abs(rect.left - 0) <= delta || Math.abs(rect.right - screenW) <= delta) {
            //图片的左右边界,在误差范围内 --> 对应情况,正常 + 放大(图片拖拽到边界)
            viewPager.setLocked(false);
        } else {
            //放大,没拖拽到边界
            viewPager.setLocked(true);
        }
    }
});

更新日志:

V1.0.1

1.可放大图片的场景下,解决在放大状态下点击和长按事件失效的问题
2.可放大图片的场景下,解决没法双指手势放大的问题

V1.0.0

1.解决全屏的情况下(状态栏隐藏和虚拟导航栏隐藏的情况),显示状态栏和显示虚拟导航栏的手势冲突的问题
2.优化滑动过程中的缩放比例

V0.0.10

1.添加单击和长按事件监听

V0.0.9

1.优化在 viewpager 中使用的体验

V0.0.8

1.添加对 ACTION_CANCEL 的处理
2.appcompat 依赖修改为 compileOnly

V0.0.7

1.新建项目
Apps
About Me
GitHub: Trinea
Facebook: Dev Tools