ViewDragHelper实现右滑返回(简易版)
2016-10-25 本文已影响55人
YuanBLQ
目录:
1、创建一个类继承任意一个常用的ViewGroup,这里我继承的是FrameLayout。
2、初始化构造器。
3、初始化ViewDragHelper对象。
4、重写onInterceptTouchEvent(); onTouchEvent(); 方法,来捕获事件并交由ViewDragHelper对象处理。
5、编写事件处理逻辑(重写CallBack方法)。
6、创建事件消费完毕后的回调接口(通知activity去finish)。
7、在需要应用此布局的activity中设置监听接口。
8、完整代码。
9、坑。
最终效果图
1、创建一个类继承任意一个常用的ViewGroup,这里我继承的是FrameLayout。
public class SwipeBackLayout extends FrameLayout
2、初始化构造器。
public SwipeBackLayout(Context context) {
this(context,null,0);
}
public SwipeBackLayout(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public SwipeBackLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public SwipeBackLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
3、初始化ViewDragHelper对象。
private void init() {
mDragHelper = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback() {
@Override
public boolean tryCaptureView(View child, int pointerId) {
return false;
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
mCurrentPosition = Math.max(0,left);
return mCurrentPosition;
}
//最好重写此方法,避免无法横向滑动
@Override
public int getViewHorizontalDragRange(View child) {
return 1;
}
@Override
public void onEdgeDragStarted(int edgeFlags, int pointerId) {
super.onEdgeDragStarted(edgeFlags, pointerId);
mEdgeFlag = edgeFlags;
mDragHelper.captureChildView(mView,pointerId);
}
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
if (mEdgeFlag == ViewDragHelper.EDGE_LEFT){
if (mCurrentPosition > mWidth/5){
//结束activity
mDragHelper.settleCapturedViewAt(mWidth,mPoint.y);
mCurrentPosition = mWidth;
}else {
//回到原位
mDragHelper.settleCapturedViewAt(mPoint.x,mPoint.y);
mCurrentPosition = mPoint.x;
}
invalidate();
}
}
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
super.onViewPositionChanged(changedView, left, top, dx, dy);
if (mEdgeFlag == ViewDragHelper.EDGE_LEFT){
mView.setAlpha(1-mCurrentPosition/mWidth);
if (mCurrentPosition >= mWidth){
if (mFinishScrollListener != null){
mFinishScrollListener.complete();
}
}
}
}
});
// 设置左边缘侧滑
mDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);
}
4、重写onInterceptTouchEvent(); onTouchEvent(); 方法,来捕获事件并交由ViewDragHelper对象处理。
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return mDragHelper.shouldInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mDragHelper.processTouchEvent(event);
return true;
}
5、编写事件处理逻辑(重写CallBack方法)。
参见第3步
6、创建事件消费完毕后的回调接口(通知activity去finish)。
public interface OnFinishScrollListener{
void complete();
}
public void setOnFinishScrollListener(OnFinishScrollListener listener){
mFinishScrollListener = listener;
}
7、在需要应用此布局的activity中设置监听接口。
public class SwipeBackActivity extends AppCompatActivity {
private SwipeBackLayout mSwipeBackLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_swipeback);
mSwipeBackLayout = (SwipeBackLayout) findViewById(R.id.swipe);
mSwipeBackLayout.setOnFinishScrollListener(new SwipeBackLayout.OnFinishScrollListener() {
@Override
public void complete() {
SwipeBackActivity.this.finish();
overridePendingTransition(0,R.anim.out_to_right);
}
});
}
}
8、完整代码
package view.swipeBackUtils;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Point;
import android.os.Build;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
/**
* Created by Chy-PC on 2016/10/23.
*/
public class SwipeBackLayout extends FrameLayout {
private ViewDragHelper mDragHelper;
//要滑动的view
private View mView;
//view原来的位置
private Point mPoint = new Point();
//当前滑动的距离
private int mCurrentPosition;
//父布局的宽度
private int mWidth;
//哪个边缘标志
private int mEdgeFlag;
private OnFinishScrollListener mFinishScrollListener;
public SwipeBackLayout(Context context) {
this(context,null,0);
}
public SwipeBackLayout(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public SwipeBackLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public SwipeBackLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
/**
* 初始化
* created at 2016/10/23 18:11
**/
private void init() {
mDragHelper = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback() {
@Override
public boolean tryCaptureView(View child, int pointerId) {
return false;
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
mCurrentPosition = Math.max(0,left);
return mCurrentPosition;
}
@Override
public int getViewHorizontalDragRange(View child) {
return 1;
}
@Override
public void onEdgeDragStarted(int edgeFlags, int pointerId) {
super.onEdgeDragStarted(edgeFlags, pointerId);
mEdgeFlag = edgeFlags;
mDragHelper.captureChildView(mView,pointerId);
}
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
if (mEdgeFlag == ViewDragHelper.EDGE_LEFT){
if (mCurrentPosition > mWidth/5){
//结束activity
mDragHelper.settleCapturedViewAt(mWidth,mPoint.y);
mCurrentPosition = mWidth;
}else {
//回到原位 ☆☆这个坐标相对于谁的?父布局??
mDragHelper.settleCapturedViewAt(mPoint.x,mPoint.y);
mCurrentPosition = mPoint.x;
}
invalidate();
}
}
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
super.onViewPositionChanged(changedView, left, top, dx, dy);
if (mEdgeFlag == ViewDragHelper.EDGE_LEFT){
mView.setAlpha(1-mCurrentPosition/mWidth);
if (mCurrentPosition >= mWidth){
if (mFinishScrollListener != null){
mFinishScrollListener.complete();
}
}
}
}
});
// 设置左边缘侧滑
mDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);
}
/**
* view拦截事件处理
* created at 2016/10/23 18:10
**/
@Override
public void computeScroll() {
super.computeScroll();
if (mDragHelper.continueSettling(true)){
invalidate();
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return mDragHelper.shouldInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mDragHelper.processTouchEvent(event);
return true;
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mView = getChildAt(0);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
mPoint.x = mView.getLeft();
mPoint.y = mView.getTop();
mWidth = getWidth();
}
/**
* 滑动结束后回调接口
* created at 2016/10/23 18:51
**/
public interface OnFinishScrollListener{
void complete();
}
/**
* 让activity中的SwipeBackLayout调用此方法,去finish该activity
* created at 2016/10/23 18:57
**/
public void setOnFinishScrollListener(OnFinishScrollListener listener){
mFinishScrollListener = listener;
}
}
9、坑
①、在SwipeBackLayout中需要获取父布局宽度以及子控件的原始坐标,这必须在onLayout方法中获取,因为此方法是在布局摆完后回调的。
②、重写右滑activity的退出动画,这样才能和谐一点。这里我只是将退出动画设为从左往右退出,具体动画还未设置。
③、为了使右滑可见下一层activity,需设置activity的主题为透明,在styles.xml中添加
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowIsTranslucent">true</item>
④、此时右滑是无法看见当前activity后面activity的,需要设置activity的主题,但是我按照网上所说的设置之后应用会崩溃。所以需后续优化。