赞
踩
最近在做一个屏保功能,需要支持如图的上滑关闭功能。
因为屏保是可以左右滑动切换的,内部是一个viewpager
做这个效果的时候,关键就是要注意外层拦截触摸事件时,需要有条件的拦截,不能影响到内部viewpager的滑动处理。
以下是封装好的自定义view,继承自FrameLayout:
-
-
- import android.animation.Animator;
- import android.animation.ObjectAnimator;
- import android.content.Context;
- import android.util.AttributeSet;
- import android.view.MotionEvent;
- import android.view.VelocityTracker;
- import android.view.View;
- import android.view.ViewConfiguration;
- import android.view.animation.Animation;
- import android.view.animation.TranslateAnimation;
- import android.widget.FrameLayout;
-
- import androidx.annotation.NonNull;
-
- public class SlideCloseFrameLayout extends FrameLayout {
-
- /**
- * 滑动监听器
- */
- public interface OnSlideCloseListener {
- /**
- * 滑动开始时调用
- */
- void onStartSlide();
-
- /**
- * 滑动结束&动画结束时调用,isClose为true表示滑动关闭,为false表示滑动恢复原位
- * @param isClose
- */
- void onStopSlide(boolean isClose);
- }
-
- private OnSlideCloseListener onSlideCloseListener;
-
- private static final String TAG = "SlideCloseFrameLayout";
- private float downY = 0; // 记录手指按下时的Y坐标
- private boolean isSlideAction = false; // 标记是否为滑动关闭动作
- private VelocityTracker velocityTracker = null; // 速度跟踪器
- private float lastTranslationY = 0; // 记录上一次的TranslationY值,用于滑动时的位置更新
-
- public SlideCloseFrameLayout(Context context) {
- super(context);
- }
-
- public SlideCloseFrameLayout(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public SlideCloseFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent event) {
- try {
- int action = event.getAction();
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- downY = event.getRawY();
- if (downY > getHeight() - getHeight() / 5f) {
- initVelocityTracker();
- velocityTracker.addMovement(event);
- return false; // 拦截事件
- }
- break;
- case MotionEvent.ACTION_MOVE:
- velocityTracker.addMovement(event);
- velocityTracker.computeCurrentVelocity(1000);
- float xVelocity = velocityTracker.getXVelocity();
- float yVelocity = velocityTracker.getYVelocity();
- if (Math.abs(yVelocity) > ViewConfiguration.get(getContext()).getScaledMinimumFlingVelocity()
- && Math.abs(yVelocity) > Math.abs(xVelocity)) {
- // 如果超过最小判定距离,并且Y轴速度大于X轴速度,才视为纵向滑动
- if (yVelocity < 0) {
- // 向下滑动
- if (onSlideCloseListener != null) {
- onSlideCloseListener.onStartSlide();
- }
- isSlideAction = true;
- return true;
- }
- }
- break;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- isSlideAction = false;
- break;
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- return super.onInterceptTouchEvent(event);
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- try {
- if (isSlideAction) {
- velocityTracker.addMovement(event);
- int action = event.getAction();
- switch (action) {
- case MotionEvent.ACTION_MOVE:
- float moveDistance = event.getRawY() - downY;
- if (moveDistance < 0) { // 仅当向上滑动时处理
- lastTranslationY = moveDistance;
- this.setTranslationY(moveDistance);
- }
- break;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- velocityTracker.computeCurrentVelocity(1000);
- float velocityY = velocityTracker.getYVelocity();
- if (Math.abs(velocityY) > 1000 || Math.abs(lastTranslationY) > getHeight() / 5f) {
- slideUpAndExit();
- } else {
- slideBack();
- }
- releaseVelocityTracker();
- isSlideAction = false;
- break;
- }
- return true;
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- return super.onTouchEvent(event);
- }
-
- public boolean isSlideAction() {
- return isSlideAction;
- }
-
- public OnSlideCloseListener getOnSlideCloseListener() {
- return onSlideCloseListener;
- }
-
- public void setOnSlideCloseListener(OnSlideCloseListener onSlideCloseListener) {
- this.onSlideCloseListener = onSlideCloseListener;
- }
-
- private void initVelocityTracker() {
- if (velocityTracker == null) {
- velocityTracker = VelocityTracker.obtain();
- } else {
- velocityTracker.clear();
- }
- }
-
- private void releaseVelocityTracker() {
- if (velocityTracker != null) {
- velocityTracker.recycle();
- velocityTracker = null;
- }
- }
-
- private void slideUpAndExit() {
- // 执行上移退出动画
- TranslateAnimation exitAnimation = new TranslateAnimation(0, 0, getTranslationY(), -getHeight());
- exitAnimation.setDuration(300);
- exitAnimation.setFillAfter(false);
- exitAnimation.setAnimationListener(new Animation.AnimationListener() {
- @Override
- public void onAnimationStart(Animation animation) {
- }
-
- @Override
- public void onAnimationEnd(Animation animation) {
- // 动画结束后的操作
- setVisibility(View.GONE); // 隐藏或其他逻辑
- if (onSlideCloseListener != null) {
- onSlideCloseListener.onStopSlide(true);
- }
- }
-
- @Override
- public void onAnimationRepeat(Animation animation) {
- }
- });
- startAnimation(exitAnimation);
- this.setTranslationY(0); // 重置TranslationY值
- }
-
- private void slideBack() {
- // 使用属性动画使视图回到原位
- ObjectAnimator animator = ObjectAnimator.ofFloat(this, "translationY", getTranslationY(), 0);
- animator.setDuration(300);
- animator.start();
- animator.addListener(new Animator.AnimatorListener(){
- @Override
- public void onAnimationStart(@NonNull Animator animation) {
-
- }
-
- @Override
- public void onAnimationEnd(@NonNull Animator animation) {
- if (onSlideCloseListener != null) {
- onSlideCloseListener.onStopSlide(false);
- }
- }
-
- @Override
- public void onAnimationCancel(@NonNull Animator animation) {
- if (onSlideCloseListener != null) {
- onSlideCloseListener.onStopSlide(false);
- }
- }
-
- @Override
- public void onAnimationRepeat(@NonNull Animator animation) {
-
- }
- });
- }
- }

Activity使用时,只需要把根View设置为这个自定义view,然后透明主题,透明背景,同时关闭Activity的进入退出动画,便可以实现如图效果了。
嵌套使用时,不会影响到内部的Viewpager或其他可滑动view
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。