GitHub:DragBottom
适用于查看大图的下拉拖拽返回控件
共享元素适用于5.0+
最初看到这个效果是在IOS系统的短信App中,后来在IOS微信、腾讯新闻中都发现了类似的效果。
网络上好像也没有类似的代码,所以决定自己动手写一个。
一想到拖拽,首先会想到ViewDragHelper,关于这个类的使用方法可以参考
http://blog.csdn.net/lmj623565791/article/details/46858663
Hongyang的博客解析
需求分析:在使用微信、腾讯新闻中该效果只允许向往下拖拽一定距离后处于触摸状态时便可全屏拖拽。并且向上向下拖拽时,会对视图的背景透明度、大小有一定变化,越往下越透明,可见上级界面。
当往下拖动一定距离松手时,触发返回上级界面。反之距离不够,视图将返回原先位置。
1、创建ViewDragHelper
mDragHelper=ViewDragHelper.create(this,1.0f,new ViewDragHelper.Callback(){
@Override
public boolean tryCaptureView(View child, int pointerId) {
//允许子视图进行拖拽,这里默认都允许
return true;
}
});
2、设定拖拽边界以及方向,只允许向下
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
//不允许向上拖动,取值top表示允许上下拖拽-1920~1920
//这里设置假设top<0时,直接设定为0,即不允许向上拖拽
return Math.max(top,0);
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
//0不允许水平拖拽,取值left表示允许左右拖拽left=-1080~1080
return 0;
}
3、如此就能设定向下拖拽了,接下来往下拖动一定距离时,释放限制,这里设定为100px,代码改造后如下
/**是否允许所有方向拖拽*/
boolean needDrag;
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
if (needDrag) {
return top;
}
if (top < 0) {//只允许向下拖拽
top = 0;
} else if (top > 100) {//向下拖拽超过100px后,释放允许任何方向拖拽
needDrag = true;
}
return top;
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
return needDrag ? left : 0;
}
4、
向下拖拽时要有缩放的一个效果,我们需要重写另一个函数
/**
*
* @param changedView 被拖动的View
* @param left 水平拖动距离
* @param top 垂直拖动距离
* @param dx 每次拖拽产生的水平距离x2-x1
* @param dy 每次拖拽产生的垂直距离y2-y1
*/
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
super.onViewPositionChanged(changedView, left, top, dx, dy);
}
4.1、为了计算出实时的拖动百分比
直接用top除以view的高度即可:
向下拖动100px后公示为:1-100/1920=0.95、
向下拖动300px后公示为:1-300/1920=0.85。
即:View从原始尺寸渐渐缩小,这里我们给定最小缩放倍率0.5f,最大1.0f。当然这个倍率也可以随时调整
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
super.onViewPositionChanged(changedView, left, top, dx, dy);
float present = 1 - (top * 1.0f) / (getHeight());
float maxScale = Math.min(present, 1.0f);//Max,1.0f
float minScale = Math.max(0.5f, maxScale);//Min,5.0f;
changedView.setScaleX(minScale);
changedView.setScaleY(minScale);
}
5、接下来是背景透明度的变换,要达到的效果是能看到上一级Activity的界面。
所以首页要将当期的Activity的主题样式设置成透明化
<style name="AppTheme.drag">
<item name="android:windowIsTranslucent">true</item>
</style>
应用
<activity android:name=".DragActivity"
android:theme="@style/AppTheme.drag"/>
5.1、背景透明度的变化第一时间想到的就是View的背景颜色
了解过Activity布局层级的都知道顶层布局是DecorView
所以我们首先给DecorView设置为纯黑色,然后拖动时修改其背景颜色即可
在布局中取得DecorView:
if (getContext() instanceof Activity) {
((Activity) getContext()).getWindow().getDecorView().setBackgroundColor(DEF_BG_COLOR);
}
和View缩放相同的道理,实时获取百分比即可,最大不超过255
/**
*
* @param changedView 被拖动的View
* @param left 水平拖动距离
* @param top 垂直拖动距离
* @param dx 每次拖拽产生的水平距离x2-x1
* @param dy 每次拖拽产生的垂直距离y2-y1
*/
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
super.onViewPositionChanged(changedView, left, top, dx, dy);
float present = 1 - (top * 1.0f) / (getHeight());
if (getContext() instanceof Activity) {
int alpah = Math.min((int) (255 * present), 255);
((Activity) getContext()).getWindow().getDecorView().setBackgroundColor(Color.argb(alpah, 0, 0, 0));
}
float maxScale = Math.min(present, 1.0f);//Max,1.0f
float minScale = Math.max(0.5f, maxScale);//Min,5.0f;
changedView.setScaleX(minScale);
changedView.setScaleY(minScale);
}
6接下来最后一个步骤要设定一个拖动触发关闭的界限,这里取了View拖动的1/4距离长度,当然可以随意修改。
超过设定距离,触发关闭
反之,将View归位
定义变量
boolean mNeedRelease;
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
super.onViewPositionChanged(changedView, left, top, dx, dy);
mNeedRelease = top > getHeight() * 0.25;//Release
重写释放拖拽的函数,触发onBackPressed方法finish掉当前Activity
boolean mNeedRelease;
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
if (mNeedRelease) {
if (getContext() instanceof Activity) {
((Activity) getContext()).onBackPressed();
}
} else {
needDrag = false;
//让视图归位
mDragHelper.settleCapturedViewAt(finalLeft, finalTop);
releasedChild.setScaleX(1.0f);
releasedChild.setScaleY(1.0f);
invalidate();
}
}