ViewDragHelper实战应用之 高仿微信双向侧滑退出Activity

在之前详细介绍了ViewDragHelper的基础应用后,本来想马上为大家写几篇这个强大的ViewDragHelper功能如何应用到实战中,不过最近有点忙啊。笔者在努力充电,希望在这一年基础稳固把握好,能够提高自己的能力,达到另一个程度!

我相信,也很多读者此时此刻也在努力提高自我努力敲代码中。是!一起成长!!

好了,现在直接进入这篇文章的重点。前些天无意中看到其他开发人员通过ViewDragHelper实现侧滑退出的效果,笔者也无意中跟ios开发的同事聊天的时候,发现ios自带的侧滑效果很赞啊,重点能够双向侧滑!即上一层界面右滑的时候,下一层也会缓慢滑动,体验感非常不错!后来发现我们强大的微信也有这种功能模仿。于是,我先是去百度看看别人实现方式。。。

发现。。居然没有这个侧滑的模仿,有的顶多只是简单的右滑退出,并不是我想要的效果。因此,静下心,不如自己来写写试试看吧!很幸运,几天努力没有白费,希望分享给一起奋斗的你们!!!
大致效果图如下:

《ViewDragHelper实战应用之 高仿微信双向侧滑退出Activity》 123.gif

现在直接进入代码解释,这里需要注意几个问题:
(1)activity之间的逆向实时通信
因为上一层的实时滑动状态,是必须要通过回调告诉下一层activity状态才能够做好实时变化的。因此逆向通信很重要,这里笔者采用栈的形式管理Actiivty,通信的时候直接从栈中取出后通过接口回调的方式实现。
(2)ViewDragHelper与Activity通信
同样,在滑动变化的时候Activity需要监听滑动状态,这里笔者依然使用接口回调方式实现。
(3)注意ViewDragHelper配置问题
这个问题详细可以看上一篇博客,ViewDragHelper详细介绍。

一、Activity栈管理ActivityStackManager.java

package cn.wsy.wechatslide.utils;
import android.app.Activity;
import java.util.Stack;
/**
 * Activity 堆栈管理
 * Created by wsy on 2016/3/4.
 */
public class ActivityStackManager {
    private static Stack activityStack;
    private static ActivityStackManager activityStackManager;
    //单例模式创建
    public static ActivityStackManager getInstance() {
        if (activityStackManager == null) {
            synchronized (ActivityStackManager.class) {
                if (activityStackManager == null) {
                    activityStackManager = new ActivityStackManager();
                }
            }
        }
        return activityStackManager;
    }
    private Stack checkActivityStack() {
        if (activityStack == null) {
            activityStack = new Stack();
            return activityStack;
        }
        return activityStack;
    }
    //进栈
    public void addStack(Activity activity) {
        if (activity != null) {
            checkActivityStack().add(activity);
        }
    }
    //出栈
    public void removeStack(Activity activity) {
        if (activity != null) {
            if (checkActivityStack().contains(activity)) {
                activityStack.remove(activity);
                activity.finish();
                activity = null;
            }
        }
    }
    //获取当前栈顶
    public Activity getStackTop() {
        return (Activity) checkActivityStack().lastElement();
    }
    public int getStackSize(){
        return checkActivityStack().size();
    }
    //获取当前栈顶下面那个Activity
    public Activity getNextStack() {
        Activity activity = null;
        if (activityStack.size() >1){
            activity = (Activity) checkActivityStack().get(activityStack.size() - 2);
        }
        return activity;
    }
}
二、滑动退出View SlideQuitView.java
package cn.wsy.wechatslide.views;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;
import cn.wsy.wechatslide.R;
import cn.wsy.wechatslide.interfaces.SlideMoveInterface;
/**
 * 滑动退出-仿IOS双向滑动
 * Created by wsy on 2016/3/4.
 */
public class SlideQuitView extends LinearLayout {
    /**
     * slide control
     */
    private ViewDragHelper mDragHelper;
    /**
     * All of the parentView
     */
    private View parentView;
    /**
     * left shadow
     */
    private Drawable mLeftShadow;
    /**
     * Current position for Edge
     */
    private int currentEdge;
    /**
     * Current position X
     */
    private int currentX;
    /**
     * The distance of View when moving
     */
    private int moveDistance;
    /**
     * This is original point of parentView ;
     */
    private Point originalPoint = new Point();
    /**
     * Whether to use slide quie
     */
    private boolean isSlideQuit = false;
    /**
     * Whether the view is moving
     */
    private boolean isMoving = false;
    /**
     * one by action doing here
     */
    private boolean isAvoidMoreAction = false;
    /**
     * quit position
     */
    private int QUIT_POSITION = 0;
    /**
     * shadow width
     */
    private final int SHADOW_WIDTH = 50;
    /**
     * MOVED_DISTANCE
     */
    private final int MOVED_RATE = 2;
    /**
     * MOVED_RESUME_DISTANCE
     */
    private int MOVED_RESUME_DISTANCE = 0;
    /**
     * Location Scope when view are moving
     */
    private final int LOCATION_SCOPE = 10;
    /**
     * Interface for view are moving
     */
    private SlideMoveInterface moveInterface;
    /**
     * Context here
     */
    private Context context;
    public SlideQuitView(Context context) {
        super(context);
        init(context);
    }
    public SlideQuitView(Context context, boolean isSlideQuit) {
        super(context);
        this.isSlideQuit = isSlideQuit;
        init(context);
    }
    public SlideQuitView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }
    private void init(Context context) {
        this.context = context;
        mDragHelper = ViewDragHelper.create(this, 1.0f, new MyViewDragHelperCallBack());
        mDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);
        mLeftShadow = getResources().getDrawable(R.drawable.left_shadow);
    }
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        if (parentView == null) {
            parentView = getChildAt(0);
            originalPoint.x = (int) parentView.getX();
            originalPoint.y = (int) parentView.getY();
            QUIT_POSITION = getWidth() / 3;
            MOVED_RESUME_DISTANCE = getWidth() / 4;
        }
    }
    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        parentView = getChildAt(0);
    }
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return mDragHelper.shouldInterceptTouchEvent(ev);
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mDragHelper.processTouchEvent(event);
        return true;
    }
    @Override
    public void computeScroll() {
        if (mDragHelper.continueSettling(true)) {
            ViewCompat.postInvalidateOnAnimation(this);//开启自动滑动
            invalidate();
        }
    }
    @Override
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
        drawShadow(canvas);
    }
    private void drawShadow(Canvas canvas) {
        canvas.save();
        mLeftShadow.setBounds(moveDistance - SHADOW_WIDTH, 0, moveDistance, getHeight());
        mLeftShadow.draw(canvas);
        canvas.restore();
    }
    public void slideViewMoving(int dx) {
        if (dx == 0) {
        } else if (dx < 0) {
            offsetLeftAndRight(dx / MOVED_RATE);
        } else {
            if (!isAvoidMoreAction) {
                setX(-(MOVED_RESUME_DISTANCE));
                isAvoidMoreAction = !isAvoidMoreAction;
            }
            int x = (int) getX();
            if (x >= -LOCATION_SCOPE && x <= LOCATION_SCOPE) {
                isMoving = false;
                setX(0);
                return;
            }
            offsetLeftAndRight(dx / MOVED_RATE);
        }
        invalidate();
    }
    public void slideViewReseting() {
        isAvoidMoreAction = false;
        setX(0);
    }
    public void slideViewResumeing() {
        isAvoidMoreAction = false;
        setX(-(MOVED_RESUME_DISTANCE));
    }
    public void setMovingListener(SlideMoveInterface slideMoveInterface) {
        this.moveInterface = slideMoveInterface;
    }
    private class MyViewDragHelperCallBack extends ViewDragHelper.Callback {
        @Override
        public boolean tryCaptureView(View view, int i) {
            return false;
        }
        @Override
        public void onEdgeDragStarted(int edgeFlags, int pointerId) {
            super.onEdgeDragStarted(edgeFlags, pointerId);
            currentEdge = edgeFlags;
            mDragHelper.captureChildView(parentView, pointerId);
        }
        @Override
        public int getViewHorizontalDragRange(View child) {
            return getMeasuredWidth() - child.getMeasuredWidth();
        }
        @Override
        public int getViewVerticalDragRange(View child) {
            return getMeasuredHeight() - child.getMeasuredHeight();
        }
        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            if (isSlideQuit) {
                if (left <0) left = 0;
                currentX = left;
                return left;
            } else {
                if (isMoving) {//正在移动
                    currentX = left;
                    return left;
                }
                return 0;
            }
        }
        @Override
        public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
            super.onViewPositionChanged(changedView, left, top, dx, dy);
            if (currentEdge == ViewDragHelper.EDGE_LEFT) {//touch left
                moveDistance = left;
                if (moveInterface != null) {
                    moveInterface.moving(dx);
                }
                invalidate();
            }
        }
        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            super.onViewReleased(releasedChild, xvel, yvel);
            if (currentEdge == ViewDragHelper.EDGE_LEFT) {
                if (isSlideQuit) {
                    if (currentX > QUIT_POSITION) {
                        if (moveInterface != null) moveInterface.resetPostion();
                        ((Activity) context).finish();
                        // ((Activity) context).overridePendingTransition(0, 0);
                    } else {
                        //回到原点
                        mDragHelper.settleCapturedViewAt(originalPoint.x, originalPoint.y);
                        if (moveInterface != null) moveInterface.resumePostion();
                    }
                } else {
                    mDragHelper.settleCapturedViewAt(originalPoint.x, originalPoint.y);
                }
                invalidate();
            }
        }
    }
}
三、滑动移动回调监听SlideMoveInterface.java
package cn.wsy.wechatslide.interfaces;
/**
 * 滑动监控状态接口
 * Created by wsy on 2016/3/4.
 */
public interface SlideMoveInterface {
    /**
     * 上一层Activity正在移动的时候回调
     *
     * @param dx
     */
    public void moving(int dx);
    /**
     * 上一层Activity销毁的时候回调
     */
    public void resetPostion();
    /**
     * 上一层Activity没有超过一半的时候回调
     */
    public void resumePostion();
}
四、封装的activity子类
package cn.wsy.wechatslide;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import cn.wsy.wechatslide.interfaces.SlideMoveInterface;
import cn.wsy.wechatslide.utils.ActivityStackManager;
import cn.wsy.wechatslide.views.SlideQuitView;
/**
 * slideQuit base activity
 * Created by wsy on 2016/3/4.
 */
public class SlideActivity extends AppCompatActivity {
    private SlideQuitView slideView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityStackManager.getInstance().addStack(this);
    }
    @Override
    public void setContentView(int layoutResID) {
        slideView = new SlideQuitView(this);
        View childView = LayoutInflater.from(this).inflate(layoutResID, null);
        ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        childView.setLayoutParams(params);
        slideView.addView(childView);
        super.setContentView(slideView);
    }
    public void setContentView(int layoutResID, boolean isSlideQuit) {
        slideView = new SlideQuitView(this, isSlideQuit);
        View childView = LayoutInflater.from(this).inflate(layoutResID, null);
        ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        childView.setLayoutParams(params);
        slideView.addView(childView);
        super.setContentView(slideView);
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        SlideActivity nextActivity = (SlideActivity) ActivityStackManager.getInstance().getNextStack();
        if (nextActivity !=null )
        nextActivity.activityReseting();
        ActivityStackManager.getInstance().removeStack(this);
    }
    public void activityMoving(int dx) {
        slideView.slideViewMoving(dx);
    }
    public void activityReseting() {
        slideView.slideViewReseting();
    }
    public void activityResumeing() {
        slideView.slideViewResumeing();
    }
    public void setSlideMoveListener(SlideMoveInterface slideMoveInterface) {
        slideView.setMovingListener(slideMoveInterface);
    }
}

这里简单介绍下,实现原理:

(1)activity是否需要滑动退出,主要在设置容器setContentView()的时候,除了传进去的LayoutID,如果构造的时候传进去isSlideQuit
为true的时候,将会有侧滑功能。

(2)如果需要异步滑动控制下面的activity,达到同时双向滑动的时候,必须在上一层的activity,实现
SlideMoveInterface接口,并且调用setSlideMoveListener()注入接口进去。例如以下代码:

package cn.wsy.wechatslide;
import android.os.Bundle;
import cn.wsy.wechatslide.interfaces.SlideMoveInterface;
import cn.wsy.wechatslide.utils.ActivityStackManager;
/**
 * Created by wsy on 2016/3/4.
 */
public class ChatActivity extends SlideActivity implements SlideMoveInterface {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.chat_activity,true);
        setSlideMoveListener(this);
    }
    @Override
    public void moving(int dx) {
        SlideActivity activity = (SlideActivity) ActivityStackManager.getInstance().getNextStack();
        activity.activityMoving(dx);
    }
    @Override
    public void resetPostion() {
        SlideActivity activity = (SlideActivity) ActivityStackManager.getInstance().getNextStack();
        activity.activityReseting();
    }
    @Override
    public void resumePostion() {
        SlideActivity activity = (SlideActivity) ActivityStackManager.getInstance().getNextStack();
        activity.activityResumeing();
    }
}

(3)为了实现右滑可以看到下一层activity的时候,我们的背景颜色必须微透明,例如以下改变主题的代码:

<resources>

 <!-- Base application theme. -->

 <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">

 <!-- Customize your theme here. -->

 </style>

 <style name="MyAppTheme" parent="AppTheme">

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

 <item name="android:windowNoTitle">true</item>

 <item name="android:windowIsTranslucent">true</item>

 <item name="android:windowAnimationStyle">@android:style/Animation.Translucent</item>

 <item name="windowNoTitle">true</item>

 </style>

</resources>

如果需要先学习的朋友可以留下邮箱在这里,我会尽快发给你们看看,一起学习!

傻小孩b mark

博客地址:http://blog.csdn.net/yyeeqe_sy/article/category/5663107

欢迎加入 qq群: 450302004 (移动开发者狂热群)
在这里,可以谈理想,可以谈技术,更可以谈帅哥美女欢迎

    原文作者:DevSiven
    原文地址: https://www.jianshu.com/p/fffceb56ff67
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞