DragCloseHelper
介绍:
本库只对预览页面做了滑动动画的封装处理,转场动画还是基于共享元素实现
效果图:
1.demo 效果
2.视频效果,非 demo
使用步骤:
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.新建项目