当前位置:   article > 正文

安卓Activity上滑关闭效果实现

安卓Activity上滑关闭效果实现

最近在做一个屏保功能,需要支持如图的上滑关闭功能。

因为屏保是可以左右滑动切换的,内部是一个viewpager

做这个效果的时候,关键就是要注意外层拦截触摸事件时,需要有条件的拦截,不能影响到内部viewpager的滑动处理。

以下是封装好的自定义view,继承自FrameLayout:

  1. import android.animation.Animator;
  2. import android.animation.ObjectAnimator;
  3. import android.content.Context;
  4. import android.util.AttributeSet;
  5. import android.view.MotionEvent;
  6. import android.view.VelocityTracker;
  7. import android.view.View;
  8. import android.view.ViewConfiguration;
  9. import android.view.animation.Animation;
  10. import android.view.animation.TranslateAnimation;
  11. import android.widget.FrameLayout;
  12. import androidx.annotation.NonNull;
  13. public class SlideCloseFrameLayout extends FrameLayout {
  14. /**
  15. * 滑动监听器
  16. */
  17. public interface OnSlideCloseListener {
  18. /**
  19. * 滑动开始时调用
  20. */
  21. void onStartSlide();
  22. /**
  23. * 滑动结束&动画结束时调用,isClose为true表示滑动关闭,为false表示滑动恢复原位
  24. * @param isClose
  25. */
  26. void onStopSlide(boolean isClose);
  27. }
  28. private OnSlideCloseListener onSlideCloseListener;
  29. private static final String TAG = "SlideCloseFrameLayout";
  30. private float downY = 0; // 记录手指按下时的Y坐标
  31. private boolean isSlideAction = false; // 标记是否为滑动关闭动作
  32. private VelocityTracker velocityTracker = null; // 速度跟踪器
  33. private float lastTranslationY = 0; // 记录上一次的TranslationY值,用于滑动时的位置更新
  34. public SlideCloseFrameLayout(Context context) {
  35. super(context);
  36. }
  37. public SlideCloseFrameLayout(Context context, AttributeSet attrs) {
  38. super(context, attrs);
  39. }
  40. public SlideCloseFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
  41. super(context, attrs, defStyleAttr);
  42. }
  43. @Override
  44. public boolean onInterceptTouchEvent(MotionEvent event) {
  45. try {
  46. int action = event.getAction();
  47. switch (action) {
  48. case MotionEvent.ACTION_DOWN:
  49. downY = event.getRawY();
  50. if (downY > getHeight() - getHeight() / 5f) {
  51. initVelocityTracker();
  52. velocityTracker.addMovement(event);
  53. return false; // 拦截事件
  54. }
  55. break;
  56. case MotionEvent.ACTION_MOVE:
  57. velocityTracker.addMovement(event);
  58. velocityTracker.computeCurrentVelocity(1000);
  59. float xVelocity = velocityTracker.getXVelocity();
  60. float yVelocity = velocityTracker.getYVelocity();
  61. if (Math.abs(yVelocity) > ViewConfiguration.get(getContext()).getScaledMinimumFlingVelocity()
  62. && Math.abs(yVelocity) > Math.abs(xVelocity)) {
  63. // 如果超过最小判定距离,并且Y轴速度大于X轴速度,才视为纵向滑动
  64. if (yVelocity < 0) {
  65. // 向下滑动
  66. if (onSlideCloseListener != null) {
  67. onSlideCloseListener.onStartSlide();
  68. }
  69. isSlideAction = true;
  70. return true;
  71. }
  72. }
  73. break;
  74. case MotionEvent.ACTION_UP:
  75. case MotionEvent.ACTION_CANCEL:
  76. isSlideAction = false;
  77. break;
  78. }
  79. } catch (Exception e) {
  80. e.printStackTrace();
  81. }
  82. return super.onInterceptTouchEvent(event);
  83. }
  84. @Override
  85. public boolean onTouchEvent(MotionEvent event) {
  86. try {
  87. if (isSlideAction) {
  88. velocityTracker.addMovement(event);
  89. int action = event.getAction();
  90. switch (action) {
  91. case MotionEvent.ACTION_MOVE:
  92. float moveDistance = event.getRawY() - downY;
  93. if (moveDistance < 0) { // 仅当向上滑动时处理
  94. lastTranslationY = moveDistance;
  95. this.setTranslationY(moveDistance);
  96. }
  97. break;
  98. case MotionEvent.ACTION_UP:
  99. case MotionEvent.ACTION_CANCEL:
  100. velocityTracker.computeCurrentVelocity(1000);
  101. float velocityY = velocityTracker.getYVelocity();
  102. if (Math.abs(velocityY) > 1000 || Math.abs(lastTranslationY) > getHeight() / 5f) {
  103. slideUpAndExit();
  104. } else {
  105. slideBack();
  106. }
  107. releaseVelocityTracker();
  108. isSlideAction = false;
  109. break;
  110. }
  111. return true;
  112. }
  113. } catch (Exception e) {
  114. e.printStackTrace();
  115. }
  116. return super.onTouchEvent(event);
  117. }
  118. public boolean isSlideAction() {
  119. return isSlideAction;
  120. }
  121. public OnSlideCloseListener getOnSlideCloseListener() {
  122. return onSlideCloseListener;
  123. }
  124. public void setOnSlideCloseListener(OnSlideCloseListener onSlideCloseListener) {
  125. this.onSlideCloseListener = onSlideCloseListener;
  126. }
  127. private void initVelocityTracker() {
  128. if (velocityTracker == null) {
  129. velocityTracker = VelocityTracker.obtain();
  130. } else {
  131. velocityTracker.clear();
  132. }
  133. }
  134. private void releaseVelocityTracker() {
  135. if (velocityTracker != null) {
  136. velocityTracker.recycle();
  137. velocityTracker = null;
  138. }
  139. }
  140. private void slideUpAndExit() {
  141. // 执行上移退出动画
  142. TranslateAnimation exitAnimation = new TranslateAnimation(0, 0, getTranslationY(), -getHeight());
  143. exitAnimation.setDuration(300);
  144. exitAnimation.setFillAfter(false);
  145. exitAnimation.setAnimationListener(new Animation.AnimationListener() {
  146. @Override
  147. public void onAnimationStart(Animation animation) {
  148. }
  149. @Override
  150. public void onAnimationEnd(Animation animation) {
  151. // 动画结束后的操作
  152. setVisibility(View.GONE); // 隐藏或其他逻辑
  153. if (onSlideCloseListener != null) {
  154. onSlideCloseListener.onStopSlide(true);
  155. }
  156. }
  157. @Override
  158. public void onAnimationRepeat(Animation animation) {
  159. }
  160. });
  161. startAnimation(exitAnimation);
  162. this.setTranslationY(0); // 重置TranslationY值
  163. }
  164. private void slideBack() {
  165. // 使用属性动画使视图回到原位
  166. ObjectAnimator animator = ObjectAnimator.ofFloat(this, "translationY", getTranslationY(), 0);
  167. animator.setDuration(300);
  168. animator.start();
  169. animator.addListener(new Animator.AnimatorListener(){
  170. @Override
  171. public void onAnimationStart(@NonNull Animator animation) {
  172. }
  173. @Override
  174. public void onAnimationEnd(@NonNull Animator animation) {
  175. if (onSlideCloseListener != null) {
  176. onSlideCloseListener.onStopSlide(false);
  177. }
  178. }
  179. @Override
  180. public void onAnimationCancel(@NonNull Animator animation) {
  181. if (onSlideCloseListener != null) {
  182. onSlideCloseListener.onStopSlide(false);
  183. }
  184. }
  185. @Override
  186. public void onAnimationRepeat(@NonNull Animator animation) {
  187. }
  188. });
  189. }
  190. }

Activity使用时,只需要把根View设置为这个自定义view,然后透明主题,透明背景,同时关闭Activity的进入退出动画,便可以实现如图效果了。

嵌套使用时,不会影响到内部的Viewpager或其他可滑动view

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/334389
推荐阅读
相关标签
  

闽ICP备14008679号