在之前详细介绍了ViewDragHelper的基础应用后,本来想马上为大家写几篇这个强大的ViewDragHelper功能如何应用到实战中,不过最近有点忙啊。笔者在努力充电,希望在这一年基础稳固把握好,能够提高自己的能力,达到另一个程度!
我相信,也很多读者此时此刻也在努力提高自我努力敲代码中。是!一起成长!!
好了,现在直接进入这篇文章的重点。前些天无意中看到其他开发人员通过ViewDragHelper实现侧滑退出的效果,笔者也无意中跟ios开发的同事聊天的时候,发现ios自带的侧滑效果很赞啊,重点能够双向侧滑!即上一层界面右滑的时候,下一层也会缓慢滑动,体验感非常不错!后来发现我们强大的微信也有这种功能模仿。于是,我先是去百度看看别人实现方式。。。
发现。。居然没有这个侧滑的模仿,有的顶多只是简单的右滑退出,并不是我想要的效果。因此,静下心,不如自己来写写试试看吧!很幸运,几天努力没有白费,希望分享给一起奋斗的你们!!!
大致效果图如下:
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 (移动开发者狂热群)
在这里,可以谈理想,可以谈技术,更可以谈帅哥美女欢迎