├── .gitignore ├── README.md ├── build.gradle ├── edgeslidingback.iml ├── gif ├── 2017-06-04_20_09_56.gif └── 2017-06-04_20_28_44.gif ├── proguard-rules.pro └── src └── main ├── AndroidManifest.xml ├── java └── com │ └── geejoe │ └── edgeslidingback │ ├── EdgeSlidingBackActivity.java │ ├── EdgeSlidingBackFragment.java │ └── EdgeSlidingBackLayout.java └── res ├── anim ├── anim_slide_in.xml └── anim_slide_out.xml ├── drawable └── left_shadow.xml └── values ├── strings.xml └── styles.xml /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EdgeSlidingBack 2 | An Android library help app with edge sliding back like ios 3 | 4 | [ ![Download](https://api.bintray.com/packages/geejoe/maven/edgeslidingback/images/download.svg) ](https://bintray.com/geejoe/maven/edgeslidingback/_latestVersion) 5 | 6 | ## 简介 7 | 8 | 这是一个仿ios边缘右滑返回的库,效果见动图: 9 | 10 | ### 普通Activity,内有一个TextView 11 | ![](https://github.com/GeeJoe/EdgeSlidingBack/raw/master/gif/2017-06-04_20_09_56.gif) 12 | 13 | ### 含有ListView和ViewPager的Activity 14 | ![](https://github.com/GeeJoe/EdgeSlidingBack/raw/master/gif/2017-06-04_20_28_44.gif) 15 | 16 | ## 依赖 17 | 18 | 本项目已同步到Jcenter,可直接引用依赖: 19 | ```Java 20 | compile 'com.geejoe:edgeslidingback:1.0.7' 21 | ``` 22 | 23 | ## 使用方法 24 | 25 | 所有需要使用滑动返回的Activity须继承自EdgeSlidingBackActivity 26 | 需要关闭右滑返回可以在Activity的super.onCreate()方法之前执行setEnableSlidingBack(false); 27 | 28 | 另需要设置主题@style/AppTheme.Slide以便滑动时使下层Activity可见 29 | 此外还提供了无ActionBar的主题@style/AppTheme.Slide.NoActionBar: 30 | 31 | 例,如下设置:不需要使用滑动返回的Activity(如MainActivity),则单独设置主题 32 | 33 | ```xml 34 | 40 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | ``` 50 | 51 | #### 重写Activity的onSlidingBack()方法,处理滑动返回销毁Activity前的工作,例如需要setResult、释放资源等工作 52 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 26 5 | buildToolsVersion '26.0.2' 6 | 7 | defaultConfig { 8 | minSdkVersion 19 9 | targetSdkVersion 26 10 | versionCode 1 11 | versionName "1.0" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | compileOptions { 20 | encoding "UTF-8" 21 | } 22 | } 23 | 24 | dependencies { 25 | compile fileTree(dir: 'libs', include: ['*.jar']) 26 | compile 'com.android.support:appcompat-v7:26.1.0' 27 | } 28 | 29 | ext { 30 | bintrayRepo = 'maven' 31 | bintrayName = 'edgeslidingback' 32 | 33 | publishedGroupId = 'com.geejoe' 34 | libraryName = 'EdgeSlidingBack' 35 | artifact = 'edgeslidingback' 36 | 37 | libraryDescription = 'An Android library help app with edge sliding back like ios' 38 | 39 | siteUrl = 'https://github.com/GeeJoe/EdgeSlidingBack' 40 | gitUrl = 'https://github.com/GeeJoe/EdgeSlidingBack.git' 41 | 42 | libraryVersion = '1.0.7' 43 | 44 | developerId = 'geejoe' 45 | developerName = 'GeeJoe' 46 | developerEmail = 'geejoe_developer@outlook.com' 47 | 48 | licenseName = 'The Apache Software License, Version 2.0' 49 | licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt' 50 | allLicenses = ["Apache-2.0"] 51 | } 52 | 53 | apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/installv1.gradle' 54 | apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/bintrayv1.gradle' -------------------------------------------------------------------------------- /edgeslidingback.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /gif/2017-06-04_20_09_56.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeeJoe/EdgeSlidingBack/8e795297a92def288f74390c1e89592775c5ee37/gif/2017-06-04_20_09_56.gif -------------------------------------------------------------------------------- /gif/2017-06-04_20_28_44.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeeJoe/EdgeSlidingBack/8e795297a92def288f74390c1e89592775c5ee37/gif/2017-06-04_20_28_44.gif -------------------------------------------------------------------------------- /proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in C:\MyPrograms\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/main/java/com/geejoe/edgeslidingback/EdgeSlidingBackActivity.java: -------------------------------------------------------------------------------- 1 | package com.geejoe.edgeslidingback; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | import android.support.v7.app.AppCompatActivity; 6 | 7 | /** 8 | * Created by JoeLee on 2017/6/3 0003 09:02. 9 | */ 10 | 11 | public class EdgeSlidingBackActivity extends AppCompatActivity implements EdgeSlidingBackLayout.OnSlidingBackListener{ 12 | 13 | private boolean enableSlidingBack = true; 14 | 15 | @Override 16 | protected void onCreate(@Nullable Bundle savedInstanceState) { 17 | super.onCreate(savedInstanceState); 18 | if (enableSlidingBack) { 19 | EdgeSlidingBackLayout edgeSlidingBackLayout = new EdgeSlidingBackLayout(this); 20 | edgeSlidingBackLayout.bindActivity(this); 21 | } 22 | } 23 | 24 | protected void setEnableSlidingBack(boolean enableSlidingBack) { 25 | this.enableSlidingBack = enableSlidingBack; 26 | } 27 | 28 | @Override 29 | public void onSlidingBack() { 30 | 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/geejoe/edgeslidingback/EdgeSlidingBackFragment.java: -------------------------------------------------------------------------------- 1 | package com.geejoe.edgeslidingback; 2 | 3 | import android.support.v4.app.Fragment; 4 | 5 | /** 6 | * Created by JoeLee on 2017/6/3 0003 09:02. 7 | */ 8 | 9 | public class EdgeSlidingBackFragment extends Fragment { 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/geejoe/edgeslidingback/EdgeSlidingBackLayout.java: -------------------------------------------------------------------------------- 1 | package com.geejoe.edgeslidingback; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.drawable.Drawable; 6 | import android.support.v4.view.VelocityTrackerCompat; 7 | import android.util.AttributeSet; 8 | import android.util.DisplayMetrics; 9 | import android.util.Log; 10 | import android.view.MotionEvent; 11 | import android.view.VelocityTracker; 12 | import android.view.View; 13 | import android.view.ViewConfiguration; 14 | import android.view.ViewGroup; 15 | import android.view.Window; 16 | import android.widget.FrameLayout; 17 | import android.widget.Scroller; 18 | 19 | /** 20 | * Created by JoeLee on 2017/6/3 0003 09:00. 21 | */ 22 | 23 | public class EdgeSlidingBackLayout extends FrameLayout { 24 | 25 | private EdgeSlidingBackActivity mActivity; 26 | private EdgeSlidingBackFragment mFragment; 27 | 28 | private Scroller mScroller; 29 | 30 | private int mScreenWidth; 31 | //可滑动返回的操作区域宽度 32 | private int mTouchAreaWidth; 33 | //有效滑动距离 34 | private int mTouchSlope; 35 | 36 | //解决滑动冲突 37 | private int mLastX; 38 | private int mLastY; 39 | private int mActionDownX; 40 | 41 | private VelocityTracker vt; 42 | private int mPointedId; 43 | private float mVelocityX; 44 | private float mMinVelocity; 45 | 46 | private Drawable mShadow; 47 | private int mShadowWidth; 48 | 49 | public EdgeSlidingBackLayout(Context context) { 50 | this(context, null, 0); 51 | } 52 | 53 | public EdgeSlidingBackLayout(Context context, AttributeSet attrs) { 54 | this(context, attrs, 0); 55 | } 56 | 57 | public EdgeSlidingBackLayout(Context context, AttributeSet attrs, int defStyleAttr) { 58 | super(context, attrs, defStyleAttr); 59 | init(context); 60 | } 61 | 62 | private void init(Context context) { 63 | mScroller = new Scroller(context); 64 | DisplayMetrics dm = context.getResources().getDisplayMetrics(); 65 | mMinVelocity = (5000 * 440) / dm.densityDpi; 66 | mScreenWidth = dm.widthPixels; 67 | //屏幕宽度的十二分之一作为可滑动返回的操作区域宽度 68 | mTouchAreaWidth = mScreenWidth / 12; 69 | mTouchSlope = ViewConfiguration.get(context).getScaledTouchSlop(); 70 | mShadow = context.getResources().getDrawable(R.drawable.left_shadow); 71 | mShadowWidth = (int) context.getResources().getDisplayMetrics().density * 16; 72 | } 73 | 74 | public void bindActivity(EdgeSlidingBackActivity activity) { 75 | this.mActivity = activity; 76 | //将EdgeSlidingBackLayout作为DecorView的唯一子布局 77 | Window window = mActivity.getWindow(); 78 | ViewGroup decorView = (ViewGroup) window.getDecorView(); 79 | View rootView = decorView.getChildAt(0); 80 | decorView.removeView(rootView); 81 | addView(rootView); 82 | decorView.addView(this); 83 | } 84 | 85 | public void bindFrgment(EdgeSlidingBackFragment fragment) { 86 | this.mFragment = fragment; 87 | // TODO 88 | } 89 | 90 | @Override 91 | public boolean onInterceptTouchEvent(MotionEvent ev) { 92 | boolean intercept = false; 93 | int x = (int) ev.getX(); 94 | int y = (int) ev.getY(); 95 | switch (ev.getAction()) { 96 | case MotionEvent.ACTION_DOWN: 97 | mActionDownX = (int) ev.getX(); 98 | mLastX = x; 99 | mLastY = y; 100 | break; 101 | case MotionEvent.ACTION_MOVE: 102 | int deltaX = x - mLastX; 103 | int deltaY = y - mLastY; 104 | if (mActionDownX < mTouchAreaWidth && deltaX > mTouchSlope 105 | && Math.abs(deltaX) > Math.abs(deltaY)) { 106 | intercept = true; 107 | } 108 | break; 109 | case MotionEvent.ACTION_UP: 110 | intercept = false; 111 | break; 112 | } 113 | return intercept; 114 | } 115 | 116 | @Override 117 | public boolean onTouchEvent(MotionEvent event) { 118 | if (vt == null) { 119 | vt = VelocityTracker.obtain(); 120 | } 121 | vt.addMovement(event); 122 | int x = (int) event.getX(); 123 | int y = (int) event.getY(); 124 | switch (event.getAction()) { 125 | case MotionEvent.ACTION_DOWN: 126 | mPointedId = event.getPointerId(0); 127 | mLastX = x; 128 | mLastY = y; 129 | break; 130 | case MotionEvent.ACTION_MOVE: 131 | vt.computeCurrentVelocity(1000); 132 | mVelocityX = VelocityTrackerCompat.getXVelocity(vt, mPointedId); 133 | int deltaX = x - mLastX; 134 | int deltaY = y - mLastY; 135 | if (mActionDownX < mTouchAreaWidth 136 | && Math.abs(deltaX) > Math.abs(deltaY)) { 137 | int moveX = -deltaX; 138 | if (-getScrollX() < 0) { 139 | scrollTo(0, 0); 140 | } else { 141 | scrollBy(moveX, 0); 142 | } 143 | } 144 | mLastX = x; 145 | mLastY = y; 146 | break; 147 | case MotionEvent.ACTION_UP: 148 | Log.d("EdgeSlidingBackLayout", "onTouchEvent()@EdgeSlidingBackLayout.java:144-->>" + "mVelocityX:" + mVelocityX); 149 | if (mVelocityX > mMinVelocity || -getScrollX() > mScreenWidth / 3) { 150 | scrollRightOut(); 151 | } else { 152 | scrollResume(); 153 | } 154 | break; 155 | } 156 | return true; 157 | } 158 | 159 | private void scrollResume() { 160 | int startX = getScrollX(); 161 | int distance = -getScrollX(); 162 | mScroller.startScroll(startX, 0, distance, 0, 300); 163 | invalidate(); 164 | } 165 | 166 | private void scrollRightOut() { 167 | int startX = getScrollX(); 168 | int distance = -mScreenWidth - startX; 169 | mScroller.startScroll(startX, 0, distance, 0, 300); 170 | invalidate(); 171 | } 172 | 173 | @Override 174 | public void computeScroll() { 175 | if (mScroller.computeScrollOffset()) { 176 | scrollTo(mScroller.getCurrX(), 0); 177 | postInvalidate(); 178 | } else if (-getScrollX() >= getWidth()) { 179 | // 180 | mActivity.onSlidingBack(); 181 | mActivity.finish(); 182 | mActivity.overridePendingTransition(R.anim.anim_slide_in, R.anim.anim_slide_out); 183 | Log.d("EdgeSlidingBackLayout", "computeScroll()@EdgeSlidingBackLayout.java:161-->>" + mActivity + "销毁"); 184 | } 185 | } 186 | 187 | @Override 188 | protected void dispatchDraw(Canvas canvas) { 189 | super.dispatchDraw(canvas); 190 | drawShadow(canvas); 191 | } 192 | 193 | private void drawShadow(Canvas canvas) { 194 | mShadow.setBounds(0, 0, mShadowWidth, getHeight()); 195 | canvas.save(); 196 | canvas.translate(-mShadowWidth, 0); 197 | mShadow.draw(canvas); 198 | canvas.restore(); 199 | } 200 | 201 | interface OnSlidingBackListener { 202 | void onSlidingBack(); 203 | } 204 | } -------------------------------------------------------------------------------- /src/main/res/anim/anim_slide_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /src/main/res/anim/anim_slide_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /src/main/res/drawable/left_shadow.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | -------------------------------------------------------------------------------- /src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | EdgeSlidingBack 3 | 4 | -------------------------------------------------------------------------------- /src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 16 | 17 | 23 | 24 | 25 | --------------------------------------------------------------------------------