您的位置:首页 > 移动开发 > Android开发

[Android]通过ViewDragHelper实现滑动关闭Activity

2017-11-09 21:35 597 查看
首先来看ViewDragHelper的构造方法:

create(ViewGroup forParent, float sensitivity, ViewDragHelper.Callback cb)


需要三个参数:

第一个 ViewGroup 是承载可以被拖动的控件的

第二个是灵敏度

第三个是拖动回调。我们需要实现拖动效果,主要就是依靠重写回调方法实现

ViewDragHelper.Callback 中 boolean tryCaptureView(View child, int pointerId)
需要实现,表明 child 是否可以被拖动,可以返回 true

对于需要实现滑动返回效果来说,还有余下方法需要实现:

int clampViewPositionHorizontal(View child, int left, int dx)


表明 child 横向上可以拖动的范围,与之对应的是 clampViewPositionVertical(View child, int top, int dy)。第二个参数表示 child view 尝试的移动到位置,返回值为实际应该在的位置。

void onViewReleased(View releasedChild, float xvel, float yvel)


在用户拖拽完成、放手的时候会被调用,需要在这个方法中判断是否拖动大于屏幕距离的一半,大于就执行 返回逻辑 ,否则反之。

void onEdgeDragStarted(int edgeFlags, int pointerId)


在用户从边缘发起拖动时会触发,因为我的目的是只有从边界处拖动才能触发效果,所以需要实现。

之后就是设置 ViewDragHelper 监视边缘拖拽,并且将事件拦截和事件处理交由 ViewDragHelper 即可。这里要注意Layout中只能包含一个直接子控件,其他子控件包裹在该子控件布局中。

当然还可以添加一些美化效果。

实现了ViewDragHelper的自定义布局View代码如下:

public class SwipeBackLayout extends FrameLayout {

private ViewDragHelper mViewDragHelper;
private View mContentView;
private int mContentWidth;
private int mMoveLeft;
private boolean isClose = false;
private boolean isEdgeDrag = false;
private CallBack mCallBack;//自定义内部的回调函数,下面写
private Drawable mShadowLeft;
private static final int FULL_ALPHA = 255;
private static final int DEFAULT_SCRIM_COLOR = 0x99000000;
private int mScrimColor = DEFAULT_SCRIM_COLOR;
private float mScrimOpacity;//滑动剩余边界的百分比,设置透明帷幕Alpha百分比用
private float mScrollPercent;//滑动宽度的百分比
private Rect mTmpRect = new Rect();

//自定义控件 必备俩个构造函数
public SwipeBackLayout(Context context) {
this(context, null);//引用俩个参数的构造方法,目的是将三个构造方法连接起来.
}

public SwipeBackLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0); //引用三个参数的构造方法
}

public SwipeBackLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();//写个初始化方法,等下在里面实现 viewDragHelper,已经一些初始化操作
}

//初始化方法
private void init() {

//ViewDragHelper
mViewDragHelper = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback() {
//记录值的变化
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
//记录左边的值的变化,因为我们实现的是往右滑动,所以只记录左边的值就可以了
mScrollPercent = Math.abs((float) left / (mContentView.getWidth() + mShadowLeft.getIntrinsicWidth()));
mMoveLeft = left;
if (left == mContentWidth) {
//如果当前状态是关闭状态且左边的值等于滑动的View的宽度,
//也就是说当前的界面已经滑出屏幕,就回调finish方法,通知activity可以finish了
mCallBack.onFinish();
}
}

@Override
public boolean tryCaptureView(View child, int pointerId) {
// 默认不扑获 View
return false;
}

@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
// 拖动限制(大于左边界)
return Math.max(0, left);
}

@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
// 拖动距离大于屏幕的一半右移,拖动距离小于屏幕的一半左移
int left = releasedChild.getLeft();
if (left > getWidth() / 2) {
mViewDragHelper.settleCapturedViewAt(mContentWidth, 0);
} else {
mViewDragHelper.settleCapturedViewAt(0, 0);
}
invalidate();
}

@Override
public void onEdgeDragStarted(int edgeFlags, int pointerId) {
// 移动子 View
mViewDragHelper.captureChildView(mContentView, pointerId);
}
});
mViewDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);
setShadow();//设置侧滑的边框,这个方法的代码下面写哈

}

@Override
public void computeScroll() {
super.computeScroll();
mScrimOpacity = 1 - mScrollPercent;
//一定要做这个操作,否则onViewReleased不起作用
if (mViewDragHelper != null && mViewDragHelper.continueSettling(true)) {
invalidate();
}
}

public void setShadow() {
mShadowLeft = getResources().getDrawable(R.mipmap.shadow_left);
invalidate();
}

@Override
protected void onFinishInflate() {
super.onFinishInflate();
//SwipeBackFrameLayout的子View有且只有一个,否则抛异常
if (getChildCount() != 1) {
throw new IllegalStateException("SwipeBackFrameLayout must host one child.");
}
//取得当前布局的第一个子View,也是唯一一个子View
//也就是activity的主要布局
mContentView = getChildAt(0);
}

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);    //获取当前界面宽度
mContentWidth = mContentView.getWidth();
}

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
//把事件传递给ViewDragHelper
return mViewDragHelper.shouldInterceptTouchEvent(ev);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
//把事件传递给ViewDragHelper
mViewDragHelper.processTouchEvent(event);
invalidate();
return true;
}

//画一个子项 ,到时候把acitivity的主题设置下就可以看到下面的activity了
@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
final boolean drawContent = child == mContentView;
boolean ret = super.drawChild(canvas, child, drawingTime);
if (drawContent && mViewDragHelper.getViewDragState() != ViewDragHelper.STATE_IDLE) {
drawShadow(canvas, child);
drawScrim(canvas, child);
}
return ret;
}

//这个是那个阴影线
private void drawShadow(Canvas canvas, View child) {
final Rect childRect = mTmpRect;
child.getHitRect(childRect);
mShadowLeft.setBounds(childRect.left - mShadowLeft.getIntrinsicWidth(), childRect.top,childRect.left, childRect.bottom);
mShadowLeft.draw(canvas);
}

//这个就是画那个透明渐变出来的帷幕
private void drawScrim(Canvas canvas, View child) {
final int baseAlpha = (mScrimColor & 0xff000000) >>> 24;
final int alpha = (int) (baseAlpha * mScrimOpacity);
final int color = alpha << 24 | (mScrimColor & 0xffffff);
canvas.clipRect(0, 0, child.getLeft(), getHeight());
canvas.drawColor(color);
}

//界面移出屏幕时接口回调
public interface CallBack {
void onFinish();//这个就可以直接用了咯,然后在acitiviy中实例化接口
}

//设置回调接口,提供给activity实现接口
public void setCallBack(CallBack callBack) {
mCallBack = callBack;
}
}


然后还需要一些配置:

如上述自定义布局View中用到的Shadow:



需要在拖动后看到上一个 Activity 的内容,所以需要将屏幕最顶层的 View 背景设置为透明,在 style 中的App中添加两个标签,然后给Activity设置一下theme属性:

<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowIsTranslucent">true</item>


//设置下theme属性就好啦
<activity
android:name=".BActivity"
android:theme="@style/AppTheme.TransparentActivity"    />


需要侧滑退出的activity的布局文件中嵌套上咱得自定义SwipeBackLayout:

<?xml version="1.0" encoding="utf-8"?>
<xxx.xxx.SwipeBackLayout
android:id="@+id/swipe_layout_two"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- 因为咱得这个侧滑退出自定义控件下面只能有一个子布局,所以把最外层布局嵌套在他的里面了。 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#00eeaa"        />
</xxx.xxx.SwipeBackLayout>


在activity中得到侧滑退出布局,设置他的回调接口:

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_b); //找到控件
//设置回调,然后就没然后了~~~
SwipeBackLayout swipeBackLayoutTwo = (SwipeBackLayout) findViewById(R.id.swipe_layout_two);
swipeBackLayoutTwo.setCallBack(new SwipeBackLayout.CallBack() {
@Override
public void onFinish() {
finish();
}
});
}


最后,启动该Activity,尝试滑动关闭

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android
相关文章推荐