├── .gitignore ├── README.md ├── SlidingShut.iml ├── app ├── app.iml ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── bamboy │ │ └── slidingshut │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── bamboy │ │ │ └── slidingshut │ │ │ ├── AssignViewActivity.java │ │ │ ├── ColorBackdropActivity.java │ │ │ ├── InterceptFinishActivity.java │ │ │ ├── MainActivity.java │ │ │ ├── StartInstantlyActivity.java │ │ │ ├── UpFinishActivity.java │ │ │ └── baseactivity │ │ │ ├── BamActivity.java │ │ │ ├── BamFragmentActivity.java │ │ │ ├── SlopeProgress.java │ │ │ └── UtilBaseGesture.java │ └── res │ │ ├── anim │ │ ├── act_left_in.xml │ │ ├── act_left_out.xml │ │ ├── act_right_in.xml │ │ └── act_right_out.xml │ │ ├── drawable │ │ ├── ic_back.png │ │ ├── ic_bamboy.png │ │ ├── radius_background.xml │ │ └── radius_link.xml │ │ ├── layout │ │ ├── activity_assign_view.xml │ │ ├── activity_color_backdrop.xml │ │ ├── activity_intercept_finish.xml │ │ ├── activity_main.xml │ │ ├── activity_start_instantly.xml │ │ └── activity_up_finish.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── bamboy │ └── slidingshut │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradlew ├── gradlew.bat ├── local.properties ├── picture ├── apk.jpg ├── gif01_upFinish.gif ├── gif02_startInstantly.gif ├── gif03_interceptFinish.gif ├── gif04_assignView.gif └── gif05_colorBackdrop.gif └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | <<<<<<< HEAD 6 | # Files for the Dalvik VM 7 | *.dex 8 | 9 | # Java class files 10 | *.class 11 | 12 | # Generated files 13 | bin/ 14 | gen/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | .idea/ 19 | gradle/ 20 | build/ 21 | /*/build/ 22 | 23 | # Local configuration file (sdk path, etc) 24 | local.properties 25 | 26 | # Proguard folder generated by Eclipse 27 | proguard/ 28 | 29 | # Log Files 30 | *.log 31 | app/app.iml 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SlidingShut 右滑关闭2.0 2 | 3 | ## 简介 4 | 很多APP中都会有右滑关闭功能, 5 | 尤其是在全面屏时代更显得尤为重要, 6 | 但是, 7 | Android 8.0禁止非全屏的Activity使用透明主题, 8 | 否则会引发: 9 | > Only fullscreen opaque activities can request orientation 10 | 11 | 这导致了体验最优的右滑关闭方案则不能再使用, 12 | 除非有微信那种黑科技。 13 | 14 | 手机QQ的右滑关闭虽然不需要透明主题, 15 | 但是没有实时反馈, 16 | 体验巨差, 17 | 那么就没有其他的选择了吗? 18 | 19 | 于是我总结了一下, 20 | 目前最多的有两种右滑方案: 21 | 22 | ## 1、以微信为代表的透明方案 23 | > 优点1: 24 | > 界面移动跟随手指, 25 | > 实时反馈。 26 | > 27 | > 优点2: 28 | > 拟物化, 29 | > 层级明确, 30 | > 视觉体验好。 31 | ——————————— 32 | > 缺点1: 33 | > 由于涉及到滑动速度, 34 | > 要滑多快才会触发关闭, 35 | > 这阈值永远是个未知数, 36 | > 再加上使用右滑关闭的App越来越多, 37 | > 每个App的这个阈值都不一样, 38 | > 无形中增加了关闭失败的几率 39 | > 以及用户的思考成本。 40 | > 41 | > 缺点2: 42 | > 需要透明主题, 43 | > 不仅有可能会扰乱上一个Activity的生命周期, 44 | > 而且SDK 27中非全屏Activity不可用, 45 | > 否侧会crash。 46 | 47 | 48 | ## 2、以QQ为代表的滑动方向计算 49 | > 优点: 50 | > 不需要透明主题 51 | ——————————— 52 | > 缺点1: 53 | > 没有任何反馈, 54 | > 用户无法知道当前手势是否可以触发关闭, 55 | > 误触率高。 56 | > 57 | > 缺点2: 58 | > 反应迟钝, 59 | > 由于需要手势完成才能获得完整手势, 60 | > 所以手指抬起后才会触发关闭, 61 | > 虽然微信也是抬起后关闭, 62 | > 但是视觉上和心理上却不存在此缺点。 63 | > 不过有个稍优点的方案, 64 | > 实时计算手势的, 65 | > 反应灵敏了许多, 66 | > 但却会增加误触几率。 67 | > 68 | > 缺点3: 69 | > 没有层级感, 70 | > 更不拟物化。 71 | 72 | 那么有没有其他的方案, 73 | 既不需要透明主题, 74 | 又能实时反馈? 75 | 76 | 当然, 77 | 既然我写了这篇帖子, 78 | 那么当然会给大家提供一种方案。 79 | 80 | ## 3:Bamboy右滑关闭方案 81 | > 优点1: 82 | > 不需要透明主题。 83 | > 84 | > 优点2: 85 | > 界面跟随手指, 86 | > 实时反馈, 87 | > 会不会关闭用户一目了然。 88 | > 89 | > 优点3: 90 | > 拟物化, 91 | > 视觉体验好。 92 | ——————————— 93 | > 缺点: 94 | > 没有微信的那种方案层级感强。 95 | 96 | 97 | 废话不多说, 98 | 这就给大家展示一下效果吧: 99 | 100 | ## 一、抬起模式 101 | 顾名思义 102 | 滑动过程中圆环进度条跟随手指而充盈 103 | 当圆环圆满后 104 | 抬起手指后才会触发finish(); 105 | 106 | 想要使用抬起模式 107 | 调用以下代码即可: 108 | > setUpFinish(true); 109 | > (注:默认为true,即默认抬起模式) 110 | 111 | ![抬起模式](https://github.com/Bamboy120315/SlidingShut/blob/master/gif/gif01_upFinish.gif) 112 | 113 | ## 二、即刻模式 114 | 与抬起模式相对应 115 | 只要圆环圆满就触发finish(); 116 | 相比来说 117 | 即刻模式更敏捷 118 | 但却存在误触的情况 119 | 选择适合自己的 120 | 121 | 想要使用抬起模式 122 | 调用以下代码即可: 123 | > setUpFinish(false); 124 | 125 | ![即刻模式](https://github.com/Bamboy120315/SlidingShut/blob/master/gif/gif02_startInstantly.gif) 126 | 127 | ## 三、兼容finish()被拦截的情况 128 | 当Activity中finish()方法被拦截时 129 | 已经移动的界面自动归位 130 | 归位后自动重新开启右滑关闭 131 | 132 | ![finish()被拦截](https://github.com/Bamboy120315/SlidingShut/blob/master/gif/gif03_interceptFinish.gif) 133 | 134 | ## 四、指定滑动View 135 | 看了上面几个动图 136 | 心细的朋友可能发现了 137 | 在界面移动时 138 | Activity左上角出现了一个白条条 139 | 这是因为TitleBar的颜色和界面的颜色不一致导致的 140 | 虽说无伤大雅 141 | 但作为一个视觉主义的程序员 142 | 这个小细节也是要优化的 143 | 所以我提供了只移动指定View功能 144 | 就是说不会整个界面都移动 145 | 你让他动的才会动 146 | 147 | 想要指定滑动View 148 | 调用以下代码即可: 149 | > setMoveView(rootView); 150 | 151 | ![指定View](https://github.com/Bamboy120315/SlidingShut/blob/master/gif/gif04_assignView.gif) 152 | 153 | ## 五、圆环颜色自定义 154 | 不管是出于性能 155 | 还是出于可扩展性 156 | 这个圆环进度条是完全用Canvas画出来的 157 | 所以颜色当然是支持自定义的 158 | 比如界面背景色不是白色 159 | 而是主题色时 160 | 161 | 想要指定圆环颜色 162 | 调用以下代码即可: 163 | > int color = ContextCompat.getColor(context, R.color.white); 164 | > setProgressColor(color); 165 | 166 | ![彩色背景](https://github.com/Bamboy120315/SlidingShut/blob/master/gif/gif05_colorBackdrop.gif) 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | -------------------------------------------------------------------------------- /SlidingShut.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/app.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 23 | 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 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 26 5 | defaultConfig { 6 | applicationId "com.bamboy.slidingshut" 7 | minSdkVersion 21 8 | targetSdkVersion 26 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | implementation fileTree(dir: 'libs', include: ['*.jar']) 23 | implementation 'com.android.support:appcompat-v7:26.1.0' 24 | implementation 'com.android.support.constraint:constraint-layout:1.1.3' 25 | testImplementation 'junit:junit:4.12' 26 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 27 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 28 | } 29 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/bamboy/slidingshut/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.bamboy.slidingshut; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.bamboy.slidingshut", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/java/com/bamboy/slidingshut/AssignViewActivity.java: -------------------------------------------------------------------------------- 1 | package com.bamboy.slidingshut; 2 | 3 | import android.os.Bundle; 4 | import android.widget.RelativeLayout; 5 | 6 | import com.bamboy.slidingshut.baseactivity.BamActivity; 7 | 8 | /** 9 | * 指定移动View示例 10 | *

11 | * Created by Bamboy on 2018/10/25. 12 | */ 13 | public class AssignViewActivity extends BamActivity { 14 | 15 | /** 16 | * Activity内容View 17 | * 【即TitleBar以下的内容】 18 | */ 19 | private RelativeLayout rl_root; 20 | 21 | @Override 22 | protected void onCreate(Bundle savedInstanceState) { 23 | super.onCreate(savedInstanceState); 24 | setContentView(R.layout.activity_assign_view); 25 | 26 | rl_root = findViewById(R.id.rl_root); 27 | 28 | // 指定只移动TitleBar以下的内容 29 | setMoveView(rl_root); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/com/bamboy/slidingshut/ColorBackdropActivity.java: -------------------------------------------------------------------------------- 1 | package com.bamboy.slidingshut; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.content.ContextCompat; 5 | 6 | import com.bamboy.slidingshut.baseactivity.BamActivity; 7 | 8 | /** 9 | * 彩色背景下改变进度条颜色示例 10 | *

11 | * Created by Bamboy on 2018/10/25. 12 | */ 13 | public class ColorBackdropActivity extends BamActivity { 14 | 15 | @Override 16 | protected void onCreate(Bundle savedInstanceState) { 17 | super.onCreate(savedInstanceState); 18 | setContentView(R.layout.activity_color_backdrop); 19 | 20 | // 设置进度条颜色 21 | setProgressColor(ContextCompat.getColor(this, R.color.white)); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/bamboy/slidingshut/InterceptFinishActivity.java: -------------------------------------------------------------------------------- 1 | package com.bamboy.slidingshut; 2 | 3 | import android.animation.Animator; 4 | import android.animation.AnimatorListenerAdapter; 5 | import android.animation.ObjectAnimator; 6 | import android.os.Bundle; 7 | import android.view.View; 8 | import android.widget.Button; 9 | import android.widget.TextView; 10 | 11 | import com.bamboy.slidingshut.baseactivity.BamActivity; 12 | 13 | /** 14 | * 拦截finish()情况示例 15 | * 16 | * Created by Bamboy on 2018/11/5. 17 | */ 18 | public class InterceptFinishActivity extends BamActivity { 19 | 20 | /** 21 | * 菜单背景蒙层 22 | */ 23 | private View view_background; 24 | /** 25 | * 菜单 26 | */ 27 | private TextView tv_menu; 28 | /** 29 | * 打开菜单按钮 30 | */ 31 | private Button btn_open_menu; 32 | 33 | @Override 34 | protected void onCreate(Bundle savedInstanceState) { 35 | super.onCreate(savedInstanceState); 36 | setContentView(R.layout.activity_intercept_finish); 37 | 38 | view_background = findViewById(R.id.view_background); 39 | tv_menu = findViewById(R.id.tv_menu); 40 | btn_open_menu = findViewById(R.id.btn_open_menu); 41 | 42 | // 打开菜单按钮点击事件 43 | btn_open_menu.setOnClickListener(new View.OnClickListener() { 44 | @Override 45 | public void onClick(View view) { 46 | toggleMenu(); 47 | } 48 | }); 49 | 50 | // 展开菜单 51 | openMenu(); 52 | } 53 | 54 | /** 55 | * 切换菜单【展开/收起】状态 56 | */ 57 | private void toggleMenu() { 58 | if (tv_menu.getVisibility() == View.GONE) 59 | openMenu(); 60 | else if (tv_menu.getTranslationY() == 0) 61 | closeMenu(); 62 | } 63 | 64 | /** 65 | * 展开菜单 66 | */ 67 | private void openMenu() { 68 | if (tv_menu.getVisibility() != View.GONE) 69 | return; 70 | 71 | tv_menu.setAlpha(0); 72 | tv_menu.setVisibility(View.VISIBLE); 73 | view_background.setAlpha(0); 74 | view_background.setVisibility(View.VISIBLE); 75 | tv_menu.post(new Runnable() { 76 | @Override 77 | public void run() { 78 | 79 | int height = tv_menu.getHeight(); 80 | float btnTranslationY = btn_open_menu.getTranslationY(); 81 | 82 | tv_menu.setAlpha(1); 83 | ObjectAnimator.ofFloat(view_background, "alpha", 0, 0.7f).setDuration(300).start(); 84 | ObjectAnimator.ofFloat(tv_menu, "translationY", 0 - height, 0).setDuration(300).start(); 85 | ObjectAnimator.ofFloat(btn_open_menu, "translationY", btnTranslationY, btnTranslationY + height).setDuration(300).start(); 86 | } 87 | }); 88 | } 89 | 90 | /** 91 | * 收起菜单 92 | */ 93 | private void closeMenu() { 94 | if (tv_menu.getTranslationY() != 0) 95 | return; 96 | 97 | int height = tv_menu.getHeight(); 98 | float btnTranslationY = btn_open_menu.getTranslationY(); 99 | 100 | ObjectAnimator.ofFloat(view_background, "alpha", 0.7f, 0).setDuration(300).start(); 101 | ObjectAnimator anim = ObjectAnimator.ofFloat(tv_menu, "translationY", 0, 0 - height); 102 | anim.addListener(new AnimatorListenerAdapter() { 103 | @Override 104 | public void onAnimationEnd(Animator animation) { 105 | super.onAnimationEnd(animation); 106 | 107 | tv_menu.setVisibility(View.GONE); 108 | } 109 | }); 110 | anim.setDuration(300); 111 | anim.start(); 112 | ObjectAnimator.ofFloat(btn_open_menu, "translationY", btnTranslationY, btnTranslationY - height).setDuration(300).start(); 113 | } 114 | 115 | @Override 116 | public void finish() { 117 | if (tv_menu.getVisibility() == View.VISIBLE && tv_menu.getTranslationY() == 0) { 118 | closeMenu(); 119 | return; 120 | } 121 | super.finish(); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /app/src/main/java/com/bamboy/slidingshut/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.bamboy.slidingshut; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | 7 | import com.bamboy.slidingshut.baseactivity.BamActivity; 8 | 9 | /** 10 | * Created by Bamboy on 2018/10/24. 11 | */ 12 | public class MainActivity extends BamActivity { 13 | 14 | @Override 15 | protected void onCreate(Bundle savedInstanceState) { 16 | super.onCreate(savedInstanceState); 17 | setContentView(R.layout.activity_main); 18 | 19 | openSlideFinish(false); 20 | 21 | findViewById(R.id.btn_start_instantly).setOnClickListener(new View.OnClickListener() { 22 | @Override 23 | public void onClick(View view) { 24 | startActivity(new Intent(MainActivity.this, StartInstantlyActivity.class)); 25 | } 26 | }); 27 | 28 | findViewById(R.id.btn_start_up_finish).setOnClickListener(new View.OnClickListener() { 29 | @Override 30 | public void onClick(View view) { 31 | startActivity(new Intent(MainActivity.this, UpFinishActivity.class)); 32 | } 33 | }); 34 | 35 | findViewById(R.id.btn_intercept_finish).setOnClickListener(new View.OnClickListener() { 36 | @Override 37 | public void onClick(View view) { 38 | startActivity(new Intent(MainActivity.this, InterceptFinishActivity.class)); 39 | } 40 | }); 41 | 42 | findViewById(R.id.btn_assign_view).setOnClickListener(new View.OnClickListener() { 43 | @Override 44 | public void onClick(View view) { 45 | startActivity(new Intent(MainActivity.this, AssignViewActivity.class)); 46 | } 47 | }); 48 | 49 | findViewById(R.id.btn_color_backdrop).setOnClickListener(new View.OnClickListener() { 50 | @Override 51 | public void onClick(View view) { 52 | startActivity(new Intent(MainActivity.this, ColorBackdropActivity.class)); 53 | } 54 | }); 55 | 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/java/com/bamboy/slidingshut/StartInstantlyActivity.java: -------------------------------------------------------------------------------- 1 | package com.bamboy.slidingshut; 2 | 3 | import android.os.Bundle; 4 | 5 | import com.bamboy.slidingshut.baseactivity.BamActivity; 6 | 7 | /** 8 | * 即刻模式示例 9 | *

10 | * Created by Bamboy on 2018/10/25. 11 | */ 12 | public class StartInstantlyActivity extends BamActivity { 13 | 14 | @Override 15 | protected void onCreate(Bundle savedInstanceState) { 16 | super.onCreate(savedInstanceState); 17 | setContentView(R.layout.activity_start_instantly); 18 | 19 | setUpFinish(false); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/bamboy/slidingshut/UpFinishActivity.java: -------------------------------------------------------------------------------- 1 | package com.bamboy.slidingshut; 2 | 3 | import android.os.Bundle; 4 | 5 | import com.bamboy.slidingshut.baseactivity.BamActivity; 6 | 7 | /** 8 | * 抬起模式示例 9 | *

10 | * Created by Bamboy on 2018/10/25. 11 | */ 12 | public class UpFinishActivity extends BamActivity { 13 | 14 | @Override 15 | protected void onCreate(Bundle savedInstanceState) { 16 | super.onCreate(savedInstanceState); 17 | setContentView(R.layout.activity_up_finish); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/bamboy/slidingshut/baseactivity/BamActivity.java: -------------------------------------------------------------------------------- 1 | package com.bamboy.slidingshut.baseactivity; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.graphics.Color; 7 | import android.os.Build; 8 | import android.view.MotionEvent; 9 | import android.view.View; 10 | import android.view.Window; 11 | import android.view.WindowManager; 12 | import android.widget.ImageView; 13 | import android.widget.RelativeLayout; 14 | 15 | import com.bamboy.slidingshut.MainActivity; 16 | import com.bamboy.slidingshut.R; 17 | 18 | import java.lang.reflect.Field; 19 | 20 | /** 21 | * Activity基类 22 | *

23 | * Created by Bamboy on 2018/10/24. 24 | */ 25 | public class BamActivity extends Activity { 26 | /** 27 | * 触摸工具类 28 | */ 29 | private UtilBaseGesture mUtilGesture; 30 | /** 31 | * 标题栏 32 | */ 33 | protected RelativeLayout rl_title; 34 | /** 35 | * 标题栏关闭箭头 36 | */ 37 | protected ImageView iv_back; 38 | 39 | @Override 40 | public void setContentView(int layoutResID) { 41 | super.setContentView(layoutResID); 42 | 43 | rl_title = findViewById(R.id.rl_title); 44 | iv_back = findViewById(R.id.iv_back); 45 | 46 | /** 47 | * 如果是Android 4.4 以上,就兼容沉浸式 48 | */ 49 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 50 | try { 51 | // 初始化状态栏 52 | setImmerseTitleBar(); 53 | } catch (Exception e) { 54 | e.printStackTrace(); 55 | } 56 | } 57 | 58 | // 设置返回按钮点击事件 59 | if (iv_back != null) { 60 | iv_back.setOnClickListener(new View.OnClickListener() { 61 | @Override 62 | public void onClick(View view) { 63 | finish(); 64 | } 65 | }); 66 | } 67 | // 手势工具类 68 | mUtilGesture = new UtilBaseGesture(this); 69 | } 70 | 71 | //============================================================================================== 72 | //======================= 以 下 是 关 于 沉 浸 式 状 态 栏 ======================================== 73 | //============================================================================================== 74 | 75 | /** 76 | * 设置沉浸TitleBar 77 | */ 78 | private void setImmerseTitleBar() { 79 | 80 | Window window = getWindow(); 81 | window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, 82 | WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 83 | 84 | // Android 7.0以上 去除状态栏半透明底色 85 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 86 | try { 87 | Class decorViewClazz = Class.forName("com.android.internal.policy.DecorView"); 88 | Field field = decorViewClazz.getDeclaredField("mSemiTransparentStatusBarColor"); 89 | field.setAccessible(true); 90 | field.setInt(window.getDecorView(), Color.TRANSPARENT); //改为透明 91 | } catch (Exception e) { 92 | e.printStackTrace(); 93 | } catch (Error e) { 94 | e.printStackTrace(); 95 | } 96 | } 97 | 98 | if (rl_title == null) { 99 | return; 100 | } 101 | 102 | int barHeight = getBarHeight(this); 103 | View fillingView = rl_title.findViewById(R.id.view_filling); 104 | if (barHeight <= 0 || fillingView == null) { 105 | return; 106 | } 107 | 108 | if (rl_title instanceof RelativeLayout) { 109 | fillingView.setLayoutParams( 110 | new RelativeLayout.LayoutParams(1, barHeight)); 111 | } 112 | } 113 | 114 | /** 115 | * 获取状态栏高度 116 | * 117 | * @param context 118 | * @return 状态栏高度 119 | */ 120 | public int getBarHeight(Context context) { 121 | int barHeight = -1; 122 | 123 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { 124 | barHeight = 0; 125 | } 126 | 127 | if (barHeight == -1) { 128 | Class c = null; 129 | Object obj = null; 130 | Field field = null; 131 | int x = 0; 132 | 133 | try { 134 | c = Class.forName("com.android.internal.R$dimen"); 135 | obj = c.newInstance(); 136 | field = c.getField("status_bar_height"); 137 | x = Integer.parseInt(field.get(obj).toString()); 138 | barHeight = context.getResources().getDimensionPixelSize(x); 139 | 140 | } catch (Exception e1) { 141 | e1.printStackTrace(); 142 | return 0; 143 | } 144 | } 145 | return barHeight; 146 | } 147 | 148 | //============================================================================================== 149 | //======================= 以 下 是 关 于 手 势 右 滑 关 闭 ======================================== 150 | //============================================================================================== 151 | 152 | /** 153 | * 绑定手势 154 | */ 155 | public boolean dispatchTouchEvent(MotionEvent ev) { 156 | if (null != mUtilGesture && mUtilGesture.motionEvent(ev)) { 157 | return true; 158 | } 159 | return super.dispatchTouchEvent(ev); 160 | } 161 | 162 | /** 163 | * 开启滑动关闭界面 164 | * 165 | * @param open 166 | */ 167 | protected void openSlideFinish(boolean open) { 168 | if (mUtilGesture == null) { 169 | return; 170 | } 171 | mUtilGesture.openSlideFinish(open); 172 | } 173 | 174 | /** 175 | * 抬起关闭 176 | * 177 | * @param upFinish 【true:手指抬起后再关闭页面】 178 | * 【false:进度条圆满就立刻关闭页面】 179 | */ 180 | public void setUpFinish(boolean upFinish) { 181 | if (mUtilGesture == null) { 182 | return; 183 | } 184 | mUtilGesture.setUpFinish(upFinish); 185 | } 186 | 187 | /** 188 | * 设置进度条颜色 189 | */ 190 | public void setProgressColor(int color) { 191 | if (mUtilGesture != null) 192 | mUtilGesture.setProgressColor(color); 193 | } 194 | 195 | /** 196 | * 滑动View 197 | * 【滑动过程中会移动的View】 198 | */ 199 | public void setMoveView(View SlideView) { 200 | mUtilGesture.setRootView(SlideView); 201 | } 202 | 203 | //============================================================================================== 204 | //======================= 以 下 是 关 于 界 面 跳 转 动 画 ======================================== 205 | //============================================================================================== 206 | 207 | /** 208 | * 打开新Activity 209 | * 210 | * @param intent intent 211 | */ 212 | public void startActivity(Intent intent) { 213 | startActivity(intent, true); 214 | } 215 | 216 | /** 217 | * 打开新Activity 218 | * 219 | * @param intent intent 220 | * @param animIn 新Activity进入的动画 221 | * @param animOut 当前Activity退出的动画 222 | */ 223 | public void startActivity(Intent intent, int animIn, int animOut) { 224 | super.startActivity(intent); 225 | overridePendingTransition(animIn, animOut); 226 | } 227 | 228 | /** 229 | * 打开新的Activity 230 | * 231 | * @param intent intent 232 | * @param isAnim 是否开启过渡动画 233 | */ 234 | public void startActivity(Intent intent, boolean isAnim) { 235 | if (isAnim) { 236 | startActivity(intent, R.anim.act_right_in, R.anim.act_left_out); 237 | } else { 238 | super.startActivity(intent); 239 | } 240 | } 241 | 242 | 243 | @Override 244 | public void finish() { 245 | finish(true); 246 | } 247 | 248 | /** 249 | * 退出Activity 250 | * 251 | * @param animIn 老Activity进入的动画 252 | * @param animOut 当前Activity退出的动画 253 | */ 254 | public void finish(int animIn, int animOut) { 255 | super.finish(); 256 | if (false == this instanceof MainActivity) 257 | overridePendingTransition(animIn, animOut); 258 | } 259 | 260 | /** 261 | * 退出Activity 262 | * 263 | * @param isAnim 是否开启过渡动画 264 | */ 265 | public void finish(boolean isAnim) { 266 | if (isAnim) { 267 | finish(R.anim.act_left_in, R.anim.act_right_out); 268 | } else { 269 | super.finish(); 270 | } 271 | } 272 | } 273 | -------------------------------------------------------------------------------- /app/src/main/java/com/bamboy/slidingshut/baseactivity/BamFragmentActivity.java: -------------------------------------------------------------------------------- 1 | package com.bamboy.slidingshut.baseactivity; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.graphics.Color; 6 | import android.os.Build; 7 | import android.support.v4.app.FragmentActivity; 8 | import android.view.MotionEvent; 9 | import android.view.View; 10 | import android.view.Window; 11 | import android.view.WindowManager; 12 | import android.widget.ImageView; 13 | import android.widget.RelativeLayout; 14 | 15 | import com.bamboy.slidingshut.R; 16 | 17 | import java.lang.reflect.Field; 18 | 19 | /** 20 | * Created by Bamboy on 2018/10/24. 21 | */ 22 | public class BamFragmentActivity extends FragmentActivity { 23 | /** 24 | * 触摸工具类 25 | */ 26 | private UtilBaseGesture mUtilGesture; 27 | /** 28 | * 标题栏 29 | */ 30 | protected RelativeLayout rl_title; 31 | /** 32 | * 标题栏关闭箭头 33 | */ 34 | protected ImageView iv_back; 35 | 36 | @Override 37 | public void setContentView(int layoutResID) { 38 | super.setContentView(layoutResID); 39 | 40 | rl_title = findViewById(R.id.rl_title); 41 | iv_back = findViewById(R.id.iv_back); 42 | 43 | /** 44 | * 如果是Android 4.4 以上,就兼容沉浸式 45 | */ 46 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 47 | try { 48 | // 初始化状态栏 49 | setImmerseTitleBar(); 50 | } catch (Exception e) { 51 | e.printStackTrace(); 52 | } 53 | } 54 | 55 | // 设置返回按钮点击事件 56 | if (iv_back != null) { 57 | iv_back.setOnClickListener(new View.OnClickListener() { 58 | @Override 59 | public void onClick(View view) { 60 | finish(); 61 | } 62 | }); 63 | } 64 | // 手势工具类 65 | mUtilGesture = new UtilBaseGesture(this); 66 | } 67 | 68 | //============================================================================================== 69 | //======================= 以 下 是 关 于 沉 浸 式 状 态 栏 ======================================== 70 | //============================================================================================== 71 | 72 | /** 73 | * 设置沉浸TitleBar 74 | */ 75 | private void setImmerseTitleBar() { 76 | 77 | 78 | Window window = getWindow(); 79 | window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, 80 | WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 81 | 82 | // Android 7.0以上 去除状态栏半透明底色 83 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 84 | try { 85 | Class decorViewClazz = Class.forName("com.android.internal.policy.DecorView"); 86 | Field field = decorViewClazz.getDeclaredField("mSemiTransparentStatusBarColor"); 87 | field.setAccessible(true); 88 | field.setInt(window.getDecorView(), Color.TRANSPARENT); //改为透明 89 | } catch (Exception e) { 90 | e.printStackTrace(); 91 | } catch (Error e) { 92 | e.printStackTrace(); 93 | } 94 | } 95 | 96 | if (rl_title == null) { 97 | return; 98 | } 99 | 100 | int barHeight = getBarHeight(this); 101 | View fillingView = rl_title.findViewById(R.id.view_filling); 102 | if (barHeight <= 0 || fillingView == null) { 103 | return; 104 | } 105 | 106 | if (rl_title instanceof RelativeLayout) { 107 | fillingView.setLayoutParams( 108 | new RelativeLayout.LayoutParams(1, barHeight)); 109 | } 110 | } 111 | 112 | /** 113 | * 获取状态栏高度 114 | * 115 | * @param context 116 | * @return 状态栏高度 117 | */ 118 | public int getBarHeight(Context context) { 119 | int barHeight = -1; 120 | 121 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { 122 | barHeight = 0; 123 | } 124 | 125 | if (barHeight == -1) { 126 | Class c = null; 127 | Object obj = null; 128 | Field field = null; 129 | int x = 0; 130 | 131 | try { 132 | c = Class.forName("com.android.internal.R$dimen"); 133 | obj = c.newInstance(); 134 | field = c.getField("status_bar_height"); 135 | x = Integer.parseInt(field.get(obj).toString()); 136 | barHeight = context.getResources().getDimensionPixelSize(x); 137 | 138 | } catch (Exception e1) { 139 | e1.printStackTrace(); 140 | return 0; 141 | } 142 | } 143 | return barHeight; 144 | } 145 | 146 | //============================================================================================== 147 | //======================= 以 下 是 关 于 手 势 右 滑 关 闭 ======================================== 148 | //============================================================================================== 149 | 150 | /** 151 | * 绑定手势 152 | */ 153 | public boolean dispatchTouchEvent(MotionEvent ev) { 154 | if (null != mUtilGesture && mUtilGesture.motionEvent(ev)) { 155 | return true; 156 | } 157 | return super.dispatchTouchEvent(ev); 158 | } 159 | 160 | /** 161 | * 开启滑动关闭界面 162 | * 163 | * @param open 164 | */ 165 | protected void openSlideFinish(boolean open) { 166 | if (mUtilGesture == null) { 167 | return; 168 | } 169 | mUtilGesture.openSlideFinish(open); 170 | } 171 | 172 | /** 173 | * 抬起关闭 174 | * 175 | * @param upFinish 【true:手指抬起后再关闭页面】 176 | * 【false:进度条圆满就立刻关闭页面】 177 | */ 178 | public void setUpFinish(boolean upFinish) { 179 | if (mUtilGesture == null) { 180 | return; 181 | } 182 | mUtilGesture.setUpFinish(upFinish); 183 | } 184 | 185 | /** 186 | * 设置进度条颜色 187 | */ 188 | public void setProgressColor(int color) { 189 | if (mUtilGesture != null) 190 | mUtilGesture.setProgressColor(color); 191 | } 192 | 193 | /** 194 | * 滑动View 195 | * 【滑动过程中会移动的View】 196 | */ 197 | public void setMoveView(View SlideView) { 198 | mUtilGesture.setRootView(SlideView); 199 | } 200 | 201 | //============================================================================================== 202 | //======================= 以 下 是 关 于 界 面 跳 转 动 画 ======================================== 203 | //============================================================================================== 204 | 205 | /** 206 | * 打开新Activity 207 | * 208 | * @param intent intent 209 | */ 210 | public void startActivity(Intent intent) { 211 | startActivity(intent, true); 212 | } 213 | 214 | /** 215 | * 打开新Activity 216 | * 217 | * @param intent intent 218 | * @param animIn 新Activity进入的动画 219 | * @param animOut 当前Activity退出的动画 220 | */ 221 | public void startActivity(Intent intent, int animIn, int animOut) { 222 | super.startActivity(intent); 223 | overridePendingTransition(animIn, animOut); 224 | } 225 | 226 | /** 227 | * 打开新的Activity 228 | * 229 | * @param intent intent 230 | * @param isAnim 是否开启过渡动画 231 | */ 232 | public void startActivity(Intent intent, boolean isAnim) { 233 | if (isAnim) { 234 | startActivity(intent, R.anim.act_right_in, R.anim.act_left_out); 235 | } else { 236 | super.startActivity(intent); 237 | } 238 | } 239 | 240 | 241 | @Override 242 | public void finish() { 243 | finish(true); 244 | } 245 | 246 | /** 247 | * 退出Activity 248 | * 249 | * @param animIn 老Activity进入的动画 250 | * @param animOut 当前Activity退出的动画 251 | */ 252 | public void finish(int animIn, int animOut) { 253 | super.finish(); 254 | overridePendingTransition(animIn, animOut); 255 | } 256 | 257 | /** 258 | * 退出Activity 259 | * 260 | * @param isAnim 是否开启过渡动画 261 | */ 262 | public void finish(boolean isAnim) { 263 | if (isAnim) { 264 | finish(R.anim.act_left_in, R.anim.act_right_out); 265 | } else { 266 | super.finish(); 267 | } 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /app/src/main/java/com/bamboy/slidingshut/baseactivity/SlopeProgress.java: -------------------------------------------------------------------------------- 1 | package com.bamboy.slidingshut.baseactivity; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Paint; 6 | import android.graphics.RectF; 7 | import android.support.annotation.Nullable; 8 | import android.support.v4.content.ContextCompat; 9 | import android.util.AttributeSet; 10 | import android.view.View; 11 | 12 | import com.bamboy.slidingshut.R; 13 | 14 | /** 15 | * 圆形进度条 16 | *

17 | * Created by Bamboy on 2018/10/18. 18 | */ 19 | public class SlopeProgress extends View { 20 | /** 21 | * 当前进度 22 | */ 23 | private int progress = 0; 24 | /** 25 | * 总进度 26 | */ 27 | private int maxProgress = 100; 28 | /** 29 | * 圆环颜色 30 | */ 31 | private int ringColor; 32 | /** 33 | * 直径 34 | */ 35 | private float diam = 100; 36 | /** 37 | * 线条 38 | */ 39 | private float line = 13; 40 | 41 | public SlopeProgress(Context context) { 42 | super(context); 43 | 44 | ringColor = ContextCompat.getColor(context, R.color.colorPrimary); 45 | } 46 | 47 | @Override 48 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 49 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 50 | int width = getMeasuredWidth(); 51 | int height = getMeasuredHeight(); 52 | 53 | int diameter = Math.min(width, height); 54 | 55 | line = diameter * 0.13f; 56 | diam = diameter - line * 2; 57 | } 58 | 59 | @Override 60 | protected void onDraw(Canvas canvas) { 61 | super.onDraw(canvas); 62 | 63 | int width = getMeasuredWidth(); 64 | int height = getMeasuredHeight(); 65 | float length = Math.min(width, height); 66 | 67 | // =============== 声明画笔 =============== 68 | // 初始化画笔 69 | Paint ringPaint = new Paint(); 70 | // 设置消除锯齿 71 | ringPaint.setAntiAlias(true); 72 | // 设置防抖,即边缘柔化 73 | ringPaint.setDither(true); 74 | // 设置颜色 75 | ringPaint.setColor(ringColor); 76 | // 设置实心 77 | ringPaint.setStyle(Paint.Style.STROKE); 78 | // 设置画笔的宽度 79 | ringPaint.setStrokeWidth(line); 80 | // 设置线条圆角 81 | ringPaint.setStrokeCap(Paint.Cap.ROUND); 82 | 83 | // =============== 画圆环 =============== 84 | // 进度 85 | float pro = (float) progress / (float) maxProgress; 86 | // 旋转角度 87 | float startAngle = 180 - (pro * 180); 88 | // 圆环进度 89 | float sweepAngle = pro * 360; 90 | // 计算内边距 91 | int padding = (int) ((length - diam) / 2); 92 | RectF oval = new RectF(padding, padding, padding + diam, padding + diam); 93 | 94 | // 绘制圆圈 95 | canvas.drawArc(oval, startAngle, sweepAngle, false, ringPaint); 96 | 97 | 98 | // =============== 画叉号 =============== 99 | // 修改透明度 100 | ringPaint.setAlpha(pro == 1 ? 255 : (int) (pro / 2.5 * 255)); 101 | // 修改画笔的宽度 102 | ringPaint.setStrokeWidth(line * 0.7f); 103 | 104 | // 线距离边缘的距离,值越小,叉号越大,值范围在 0 ~ 0.5 之间 105 | float linePadding = 0.373f; 106 | 107 | // 第一根直线的【起点/终点】坐标 108 | float lineStartX_1 = length * linePadding; 109 | float lineStartY_1 = length * linePadding; 110 | float lineEndX_1 = length * (1 - linePadding); 111 | float lineEndY_1 = length * (1 - linePadding); 112 | 113 | // 第二根直线的【起点/终点】坐标 114 | float lineStartX_2 = length * linePadding; 115 | float lineStartY_2 = length * (1 - linePadding); 116 | float lineEndX_2 = length * (1 - linePadding); 117 | float lineEndY_2 = length * linePadding; 118 | 119 | // 绘制直线 120 | canvas.drawLines(new float[]{ 121 | // 绘制一根直线 每四数字(两个点的坐标)确定一条线 122 | lineStartX_1, lineStartY_1, lineEndX_1, lineEndY_1, 123 | // 绘制第二根直线 每四数字(两个点的坐标)确定一条线 124 | lineStartX_2, lineStartY_2, lineEndX_2, lineEndY_2 125 | }, ringPaint); 126 | } 127 | 128 | /** 129 | * 总进度 130 | */ 131 | public int getMaxProgress() { 132 | return maxProgress; 133 | } 134 | 135 | /** 136 | * 总进度 137 | */ 138 | public void setMaxProgress(int maxProgress) { 139 | this.maxProgress = maxProgress; 140 | } 141 | 142 | /** 143 | * 设置颜色 144 | */ 145 | public void setRingColor(int ringColor) { 146 | if (ringColor != 0) 147 | this.ringColor = ringColor; 148 | } 149 | 150 | /** 151 | * 线条 152 | */ 153 | public float getLine() { 154 | return line; 155 | } 156 | 157 | /** 158 | * 线条 159 | */ 160 | public void setLine(float line) { 161 | this.line = line; 162 | } 163 | 164 | /** 165 | * 获取当前进度 166 | * 167 | * @return 168 | */ 169 | public int getProgress() { 170 | return progress; 171 | } 172 | 173 | /** 174 | * 设置进度 175 | * 176 | * @param progress 177 | */ 178 | public void setProgress(int progress) { 179 | if (progress < 0) { 180 | progress = 0; 181 | } 182 | if (progress > maxProgress) { 183 | progress = maxProgress; 184 | } 185 | 186 | this.progress = progress; 187 | invalidate(); 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /app/src/main/java/com/bamboy/slidingshut/baseactivity/UtilBaseGesture.java: -------------------------------------------------------------------------------- 1 | package com.bamboy.slidingshut.baseactivity; 2 | 3 | import android.animation.Animator; 4 | import android.animation.AnimatorListenerAdapter; 5 | import android.animation.ObjectAnimator; 6 | import android.app.Activity; 7 | import android.os.Build; 8 | import android.view.MotionEvent; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | 12 | /** 13 | * BamActivity触摸工具类 14 | *

15 | * Created by Bamboy on 2018/10/24. 16 | */ 17 | public class UtilBaseGesture { 18 | 19 | /** 20 | * 起始触点的X轴范围 21 | * 大于这个范围则不会触发右滑关闭 22 | */ 23 | public final static int SLIDE_SCREEN_WIDTH_SCALE = 30; 24 | 25 | private Activity mActivity; 26 | /** 27 | * 屏幕宽度 28 | */ 29 | private int mScreenWidth; 30 | /** 31 | * 滑动起点 32 | */ 33 | private int startSlidingX; 34 | /** 35 | * 界面移动距离 36 | */ 37 | private int move; 38 | /** 39 | * 移动最大值 40 | */ 41 | private int maxMove; 42 | /** 43 | * 移动与滑动的比例 44 | * 同样滑动距离的情况下,比例越大,界面移动越小 45 | */ 46 | private float moveScaling; 47 | 48 | /** 49 | * Activity的Layout的容器 50 | */ 51 | private ViewGroup rootGroup; 52 | /** 53 | * Activity的Layout的根View 54 | * 【滑动过程中会移动的View】 55 | */ 56 | private View rootView; 57 | /** 58 | * 滑动进度条 59 | */ 60 | private SlopeProgress mProgress; 61 | /** 62 | * 进度条宽度 63 | */ 64 | private int progressWidth; 65 | 66 | /** 67 | * 滑动关闭开关 68 | */ 69 | private boolean slideOpen = true; 70 | /** 71 | * 抬起关闭 72 | * 【true:手指抬起后再关闭页面】 73 | * 【false:进度条圆满就立刻关闭页面】 74 | */ 75 | private boolean upFinish = true; 76 | 77 | /** 78 | * 构造 79 | * 80 | * @param activity 81 | */ 82 | public UtilBaseGesture(Activity activity) { 83 | this.mActivity = activity; 84 | 85 | // 获取根布局,用于右滑关闭 86 | rootGroup = activity.findViewById(android.R.id.content); 87 | rootView = rootGroup.getChildAt(0); 88 | 89 | int width = activity.getWindowManager().getDefaultDisplay().getWidth(); 90 | int height = activity.getWindowManager().getDefaultDisplay().getHeight(); 91 | 92 | mScreenWidth = Math.min(width, height); 93 | maxMove = (int) (mScreenWidth * 0.06); 94 | moveScaling = 0.2f; 95 | progressWidth = mScreenWidth / 9; 96 | } 97 | 98 | /** 99 | * 初始化进度条 100 | */ 101 | private void initProgress() { 102 | mProgress = new SlopeProgress(mActivity); 103 | mProgress.setMaxProgress(100); 104 | 105 | ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(progressWidth, progressWidth); 106 | mProgress.setLayoutParams(params); 107 | 108 | if (rootGroup != null) 109 | rootGroup.addView(mProgress); 110 | } 111 | 112 | //============================================================================================== 113 | //======================= 以 下 是 关 于 属 性 方 法 ============================================= 114 | //============================================================================================== 115 | 116 | /** 117 | * 开启滑动关闭界面 118 | * 119 | * @param open 120 | */ 121 | public void openSlideFinish(boolean open) { 122 | slideOpen = open; 123 | } 124 | 125 | /** 126 | * 抬起关闭 127 | * 128 | * @param upFinish 【true:手指抬起后再关闭页面】 129 | * 【false:进度条圆满就立刻关闭页面】 130 | */ 131 | public void setUpFinish(boolean upFinish) { 132 | this.upFinish = upFinish; 133 | } 134 | 135 | /** 136 | * Activity的Layout的根View 137 | * 【滑动过程中会移动的View】 138 | */ 139 | public void setRootView(View rootView) { 140 | this.rootView = rootView; 141 | } 142 | 143 | /** 144 | * 获取移动距离 145 | * 146 | * @return 147 | */ 148 | public int getMove() { 149 | return move; 150 | } 151 | 152 | /** 153 | * 设置进度条颜色 154 | */ 155 | public void setProgressColor(int color) { 156 | if (mProgress == null) 157 | initProgress(); 158 | 159 | if (mProgress != null) 160 | mProgress.setRingColor(color); 161 | } 162 | 163 | /** 164 | * 设置移动距离 165 | * 166 | * @param move 167 | */ 168 | public void setMove(int move) { 169 | 170 | if (move > maxMove) 171 | move = (int) (maxMove + (move - maxMove) * 0.5f); 172 | 173 | rootView.setX(move); 174 | this.move = move; 175 | 176 | if (move > maxMove) 177 | move = maxMove; 178 | 179 | // 更新进度条 180 | mProgress.setProgress((int) (101 * ((float) move / (float) maxMove))); 181 | 182 | // 判断是否关闭Activity 183 | if (false == upFinish && mProgress.getProgress() >= mProgress.getMaxProgress()) { 184 | finish(); 185 | } 186 | } 187 | 188 | //============================================================================================== 189 | //======================= 以 下 是 关 于 手 势 触 摸 计 算 ======================================== 190 | //============================================================================================== 191 | 192 | /** 193 | * 处理触摸事件 194 | * 195 | * @param motionEvent 触摸事件 196 | */ 197 | public boolean motionEvent(MotionEvent motionEvent) { 198 | if (false == slideOpen) { 199 | return false; 200 | } 201 | try { 202 | // 触摸监听,用于监听手指运动 203 | switch (motionEvent.getAction()) { 204 | // 手指按下 205 | case MotionEvent.ACTION_DOWN: 206 | return actionDown(motionEvent); 207 | 208 | // 手指移动 209 | case MotionEvent.ACTION_MOVE: 210 | return actionMove(motionEvent); 211 | 212 | // 手指抬起 213 | case MotionEvent.ACTION_UP: 214 | return actionUp(motionEvent); 215 | 216 | default: 217 | break; 218 | } 219 | } catch (Exception e) { 220 | e.printStackTrace(); 221 | } 222 | return false; 223 | } 224 | 225 | /** 226 | * 手指按下 227 | * 228 | * @param motionEvent 229 | * @return 230 | */ 231 | private boolean actionDown(MotionEvent motionEvent) { 232 | // 计算X轴 233 | startSlidingX = (int) motionEvent.getX(); 234 | if (false == slideOpen || 235 | rootView == null || 236 | startSlidingX > mScreenWidth / SLIDE_SCREEN_WIDTH_SCALE) { 237 | return false; 238 | } 239 | 240 | // 添加进度条 241 | if (mProgress == null) { 242 | // 初始化进度条 243 | initProgress(); 244 | } 245 | 246 | // 设置背景色 247 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && rootView.getBackground() != null) { 248 | rootGroup.setBackground(rootView.getBackground()); 249 | } 250 | 251 | return true; 252 | } 253 | 254 | /** 255 | * 手指移动 256 | * 257 | * @param motionEvent 258 | * @return 259 | */ 260 | private boolean actionMove(MotionEvent motionEvent) { 261 | float nowX = motionEvent.getX(); 262 | if (false == slideOpen || 263 | rootView == null || 264 | startSlidingX > mScreenWidth / SLIDE_SCREEN_WIDTH_SCALE) { 265 | return false; 266 | } 267 | 268 | // 计算滑动距离 269 | float nowMove = (int) ((nowX > startSlidingX ? nowX - startSlidingX : 0) * moveScaling); 270 | 271 | // 更新移动位置 272 | setMove((int) nowMove); 273 | 274 | // 进度条的Y轴跟随手指, 275 | // 这里减去(mProgress.getHeight() * 0.7f) 276 | // 是因为手指触摸时实际触点和视觉触点不一致,所以要减小这个视觉误差 277 | mProgress.setTranslationY(motionEvent.getY() - mProgress.getHeight() * 0.7f); 278 | return true; 279 | } 280 | 281 | /** 282 | * 手指抬起 283 | * 284 | * @param motionEvent 285 | * @return 286 | */ 287 | private boolean actionUp(MotionEvent motionEvent) { 288 | if (false == slideOpen 289 | || rootView == null 290 | || rootView.getX() == 0 291 | || startSlidingX == mScreenWidth) 292 | return false; 293 | 294 | // 判断是否关闭Activity 295 | if (upFinish && mProgress.getProgress() >= mProgress.getMaxProgress()) { 296 | finish(); 297 | } else { 298 | ObjectAnimator.ofInt(this, "move", getMove(), 0).setDuration(200).start(); 299 | } 300 | return true; 301 | } 302 | 303 | private void finish() { 304 | slideOpen = false; 305 | startSlidingX = mScreenWidth; 306 | ObjectAnimator anim = ObjectAnimator.ofInt(this, "move", getMove(), 0); 307 | anim.setStartDelay(100); 308 | anim.setDuration(200); 309 | anim.addListener(new AnimatorListenerAdapter() { 310 | @Override 311 | public void onAnimationEnd(Animator animation) { 312 | super.onAnimationEnd(animation); 313 | 314 | slideOpen = true; 315 | } 316 | }); 317 | anim.start(); 318 | 319 | mActivity.finish(); 320 | } 321 | } 322 | -------------------------------------------------------------------------------- /app/src/main/res/anim/act_left_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/anim/act_left_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/anim/act_right_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/anim/act_right_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bamboy120315/SlidingShut/3e13edeb8dcb6ceb8d8c37fca4aef8eae1287b58/app/src/main/res/drawable/ic_back.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_bamboy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bamboy120315/SlidingShut/3e13edeb8dcb6ceb8d8c37fca4aef8eae1287b58/app/src/main/res/drawable/ic_bamboy.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/radius_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/radius_link.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_assign_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 14 | 15 | 16 | 20 | 21 | 29 | 30 | 31 | 41 | 42 | 43 | 44 | 49 | 50 | 53 | 54 | 58 | 59 | 69 | 70 | 80 | 81 | 91 | 92 | 104 | 105 | 117 | 118 | 130 | 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_color_backdrop.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 12 | 13 | 14 | 18 | 19 | 27 | 28 | 29 | 39 | 40 | 48 | 49 | 50 | 51 | 55 | 56 | 60 | 61 | 72 | 73 | 85 | 86 | 98 | 99 | 111 | 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_intercept_finish.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 14 | 15 | 16 | 20 | 21 | 29 | 30 | 31 | 41 | 42 | 43 | 44 | 48 | 49 | 52 | 53 | 58 | 59 | 70 | 71 | 83 | 84 | 96 | 97 | 98 | 99 | 100 | 106 | 107 | 119 | 120 |