Android,위 챗 사 이 드 슬라이딩 페이지 닫 기 효과 구현

최근 휴대 전화 가 5.0 시스템 을 업그레이드 한 후,갑자기 위 챗 이 IOS 와 같은 사 이 드 슬라이딩 으로 현재 페이지 를 닫 는 효과 가 있다 는 것 을 발견 하고,이러한 효 과 를 자신의 프로젝트 에 추가 하려 고 한다.반복 적 으로 바퀴 를 만 들 지 않 는 다 는 원칙 에 따라 인터넷 바 이 두 에서 오랫동안 바 이 두 를 살 펴 보 았 는데 대부분 사용자 정의 View 로 이 루어 진 것 을 발 견 했 습 니 다.하지만 저 처럼 기본적으로 완 성 된 프로젝트 에 있어 모든 Activity 가 사용자 정의 View 를 다시 사용 하면 무 서운 악몽 입 니 다.
그래서 저 는 사용자 정의 View 없 이도 실현 할 수 있 는 다른 방법 을 실 현 했 습 니 다.그 하위 클래스 는 이 를 계승 하면 옆으로 미 끄 러 지 는 기능 을 가 질 수 있 습 니 다.
한 마디 만 해도 이 방법 은 5.0 이상 의 핸드폰 에 만 효과 가 있 습 니 다(어차피 위 챗 도 5.0 에서 만 사용 할 수 있 습 니 다).5.0 이하 의 것 은 무시 하 세 요!!
의 원리
모든 Activity 에 밑 에 있 는 View,즉 rootView 가 있 습 니 다.xml 레이아웃 을 불 러 올 때 시스템 은 자동 으로 이 rootView 를 생 성 합 니 다.이것 은 View 이기 때문에 일정한 코드 를 통 해 이 레이아웃 을 마음대로 이동 할 수 있 음 을 의미 합 니 다.
다음 코드 에서 보 듯 이 간단 한 몇 줄 코드 만 있 으 면 레이아웃 의 이동 을 실현 할 수 있 습 니 다.

public class SlideActivity extends AppCompatActivity {
  View mRootView;
  private GestureDetector mDetector;
  private int mWindowWidth;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_demo);
    mRootView = getWindow().getDecorView();
    mRootView.setBackgroundColor(Color.BLUE);
    mDetector = new GestureDetector(this, new GestureListener());
    mWindowWidth = getWindow().getWindowManager().getDefaultDisplay().getWidth();
  }

  @Override
  public boolean onTouchEvent(MotionEvent event) {
    return mDetector.onTouchEvent(event);
  }

  /**
   *     
   */
  private class GestureListener extends GestureDetector.SimpleOnGestureListener {
    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
      if (e1 != null) {
        handlerCurrentActivityScroll(e2);
      }
      return super.onScroll(e1, e2, distanceX, distanceY);
    }

    /**
     *         
     */
    private void handlerCurrentActivityScroll(MotionEvent e2) {
      mRootView.setTranslationX(e2.getX());
      if (e2.getX() > mWindowWidth - 20) {
        finish();
      }
    }
  }
}
이게 저희 효과 예요.

활동 연동
이 차 이 는 여전히 매우 크다.가장 뚜렷 한 점 은 우리 가 이동 할 때 지난 층 의 Activity 가 뜻밖에도 연동 되 지 않 았 다 는 것 이다.
이 문 제 를 해결 하 는 방법 도 간단 합 니 다.그림 에서 보 듯 이 Activity 를 시작 할 때마다 시스템 은 Activity 를 하나의 스 택 에 넣 습 니 다.스 택 의 작업 원 리 를 통 해 알 수 있 듯 이 APP 안의 Activity 는 한 층 씩 덮 여 있 습 니 다.위의 그림 과 같 습 니 다.이 를 위해 하나의 Activity 를 시작 할 때마다 현재 Activity 를 하나의 List 에 저장 할 수 있 습 니 다.그러면 우 리 는 현재 Activity 에서 이전 Activity 를 꺼 내 조작 할 수 있 습 니 다.
따라서 새로운 Activity 에 들 어 갈 때 onCreate 방법 에서 현재 Activity 를 목록 에 불 러 오고 종료 할 때 finish 의 재 부팅 방법 에서 현재 Activity 를 목록 에서 제거 합 니 다.
조심해!!!미 끄 러 질 때 는 Activity 에 ListView 와 같은 미끄럼 컨트롤 이 있 을 수 있 음 을 고려 해 야 하기 때문에 이벤트 에 대한 배포 제어 가 필요 합 니 다.
코드 는 다음 과 같다.

/**
 * Created by yuyu on 2015/10/29.
 */
public class TestActivity extends AppCompatActivity {
  View mRootView;
  private GestureDetector mGestureDetector;
  private static List<TestActivity> mActivitys = new ArrayList<>();
  /**
   *     
   */
  private float mWindowWidth;
  private TestActivity mBeforeActivity;
  /**
   *    Activity   
   */
  private float mOffsetX;
  /**
   *           
   */
  private float mOutsideWidth;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_demo);
    /**
     *    Activity      
     */
    mActivitys.add(this);
    initScrollBack();
  }

  /**
   *          
   */
  private void initScrollBack() {
    mWindowWidth = getWindowManager().getDefaultDisplay().getWidth();
    mOutsideWidth = -mWindowWidth / 4;
    mOffsetX = mOutsideWidth;
    mGestureDetector = new GestureDetector(this, new GestureListener());
    mRootView = getWindow().getDecorView();
    mRootView.setBackgroundColor(Color.BLUE);
  }

  /**
   *       ,              
   */
  @Override
  public boolean dispatchTouchEvent(@NonNull MotionEvent ev) {
    if (ev.getX() < mWindowWidth / 10) {
      if (mActivitys.size() > 1) {
        mBeforeActivity = mActivitys.get(mActivitys.size() - 2);
        beforeActivityTranslationX(mOutsideWidth);
      }
      return onTouchEvent(ev);
    }
    return super.dispatchTouchEvent(ev);
  }

  @Override
  public void finish() {
    mActivitys.remove(this);
    if (mOffsetX < 0.0001 || mOffsetX > 0.0001) {
      beforeActivityTranslationX(0);
    }
    super.finish();
  }

  public void onClick(View view) {
    Intent intent = new Intent(this, Activity5.class);
    startActivity(intent);
  }

  public View getRootView() {
    return mRootView;
  }

  /**
   *      Activity  
   */
  private void beforeActivityTranslationX(float translationX) {
    if (mBeforeActivity != null) {
      mBeforeActivity.getRootView().setTranslationX(translationX);
    }
  }

  @Override
  public boolean onTouchEvent(MotionEvent event) {
    return mGestureDetector.onTouchEvent(event);
  }

  /**
   *     
   */
  private class GestureListener extends GestureDetector.SimpleOnGestureListener {
    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
      if (e1 != null) {
        handlerCurrentActivityScroll(e2);
        handleBeforeActivityScroll(e2, distanceX);
      }
      return super.onScroll(e1, e2, distanceX, distanceY);
    }

    /**
     *         
     */
    private void handlerCurrentActivityScroll(MotionEvent e2) {
      mRootView.setTranslationX(e2.getX());
      if (e2.getX() > mWindowWidth - 20) {
        finish();
      }
    }

    /**
     *          
     */
    private void handleBeforeActivityScroll(MotionEvent e2, float distanceX) {
      if (mBeforeActivity != null) {
        mOffsetX = distanceX < 0 ? mOffsetX + Math.abs(distanceX) / 4 : mOffsetX - Math.abs(distanceX) / 4;
        if (mOffsetX > 0.0001) {
          mOffsetX = 0f;
        }
        mBeforeActivity.getRootView().setTranslationX(mOffsetX);
      }
    }
  }
}

이것 은 연동 후의 효과 도 이다.

지금 은 약간의 효과 가 있 는 셈 이지 만,위 챗 과 의 차 이 는 여전히 매우 크다.이어서 우 리 는 자동 미끄럼 처 리 를 시작 해 야 한다.
자동 미끄럼
이것 은 더 말 할 필요 가 없다.이것 은 주로 속성 애니메이션 을 이용 하여 이동 하 는 것 이다.
다음은 완전한 코드 입 니 다.

public class SlideActivity extends AppCompatActivity {
  private static final String TAG = "SlideActivity";
  private static List<SlideActivity> mActivitys = new ArrayList<>();
  /**
   *     
   */
  private GestureDetector mGestureDetector;
  private View mRootView;
  private boolean isScroll = false;
  /**
   *     
   */
  private float mWindowWidth;
  private SlideActivity mBeforeActivity;
  /**
   *    Activity   
   */
  private float mOffsetX;
  /**
   *           
   */
  private float mOutsideWidth;
  private boolean canScrollBack = true;
  private boolean canScroll = false;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
    getWindow().setEnterTransition(new Slide(Gravity.RIGHT));
    super.onCreate(savedInstanceState);
    /**
     *    Activity      
     */
    mActivitys.add(this);
    initScrollBack();
  }

  @Override
  public void startActivity(Intent intent) {
    startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle());
  }

  /**
   *          
   */
  private void initScrollBack() {
    mWindowWidth = getWindowManager().getDefaultDisplay().getWidth();
    mOutsideWidth = -mWindowWidth / 4;
    mOffsetX = mOutsideWidth;
    mGestureDetector = new GestureDetector(this, new GestureListener());
    mRootView = getWindow().getDecorView();

  }

  /**
   *      Activity  
   */
  private void beforeActivityTranslationX(float translationX) {
    if (mBeforeActivity != null) {
      mBeforeActivity.getRootView().setTranslationX(translationX);
    }
  }

  /**
   *        
   *
   * @param canScrollBack true     
   */
  protected void setCanScrollBack(boolean canScrollBack) {
    this.canScrollBack = canScrollBack;
  }

  public View getRootView() {
    return mRootView;
  }

  @Override
  public void finish() {
    mActivitys.remove(this);
    if (mOffsetX < 0.0001 || mOffsetX > 0.0001) {
      beforeActivityTranslationX(0);
    }
    super.finish();
  }

  /**
   *       
   */
  @Override
  public boolean dispatchTouchEvent(@NonNull MotionEvent ev) {
    if (canScrollBack && ev.getX() < mWindowWidth / 10) {
      if (mActivitys.size() > 1) {
        mBeforeActivity = mActivitys.get(mActivitys.size() - 2);
        beforeActivityTranslationX(mOutsideWidth);
      }
      canScroll = true;
      return onTouchEvent(ev);
    }
    return super.dispatchTouchEvent(ev);
  }

  @Override
  public boolean onTouchEvent(MotionEvent event) {
    if (canScrollBack && canScroll) {
      if (event.getAction() == MotionEvent.ACTION_UP && isScroll) {
        isScroll = false;
        canScroll = false;
        //    Activity
        if (event.getX() > mWindowWidth / 2) {
          if (mBeforeActivity != null) {
            ObjectAnimator.ofFloat(mBeforeActivity.getRootView(), "translationX", mOffsetX, 0).setDuration(500).start();
          }
          ObjectAnimator moveIn = ObjectAnimator.ofFloat(mRootView, "translationX", event.getX(), mWindowWidth);
          moveIn.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
              super.onAnimationEnd(animation);
              finish();
            }
          });
          moveIn.setDuration(500).start();
          //    
        } else if (event.getX() < mWindowWidth / 2) {
          ObjectAnimator.ofFloat(mRootView, "translationX", event.getX(), 0).setDuration(500).start();
          if (mBeforeActivity != null) {
            ObjectAnimator.ofFloat(mBeforeActivity.getRootView(), "translationX", mOffsetX, mOutsideWidth).setDuration(500).start();
          }
          mOffsetX = mOutsideWidth;
        }
      } else {
        mGestureDetector.onTouchEvent(event);
      }
    }
    return true;
  }

  /**
   *     
   */
  private class GestureListener extends GestureDetector.SimpleOnGestureListener {
    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
      if (e1 != null) {
        handlerCurrentActivityScroll(e2);
        handleBeforeActivityScroll(e2, distanceX);
      }
      return super.onScroll(e1, e2, distanceX, distanceY);
    }

    /**
     *         
     */
    private void handlerCurrentActivityScroll(MotionEvent e2) {
      isScroll = true;
      mRootView.setTranslationX(e2.getX());
      if (e2.getX() > mWindowWidth - 20) {
        finish();
      }
    }

    /**
     *          
     */
    private void handleBeforeActivityScroll(MotionEvent e2, float distanceX) {
      if (mBeforeActivity != null) {
        mOffsetX = distanceX < 0 ? mOffsetX + Math.abs(distanceX) / 4 : mOffsetX - Math.abs(distanceX) / 4;
        if (mOffsetX > 0.0001) {
          mOffsetX = 0f;
        }
        mBeforeActivity.getRootView().setTranslationX(mOffsetX);
      }
    }
  }
}
저희 의 최종 효과 도 입 니 다.

이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

좋은 웹페이지 즐겨찾기