ViewDragHelper实现右滑返回(简易版)

目录:

1、创建一个类继承任意一个常用的ViewGroup,这里我继承的是FrameLayout。

2、初始化构造器。

3、初始化ViewDragHelper对象。

4、重写onInterceptTouchEvent(); onTouchEvent(); 方法,来捕获事件并交由ViewDragHelper对象处理。

5、编写事件处理逻辑(重写CallBack方法)。

6、创建事件消费完毕后的回调接口(通知activity去finish)。

7、在需要应用此布局的activity中设置监听接口。

8、完整代码。

9、

最终效果图


gif


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的主题,但是我按照网上所说的设置之后应用会崩溃。所以需后续优化。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,313评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,369评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,916评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,333评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,425评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,481评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,491评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,268评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,719评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,004评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,179评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,832评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,510评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,153评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,402评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,045评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,071评论 2 352

推荐阅读更多精彩内容