안 드 로 이 드 시 뮬 레이 션 하 얏 트 페이지 인 터 랙 션 효과 인 스 턴 스 코드

개술
새 버 전의 사 운 드 위 에 타이 앱 은 재생 페이지 의 상호작용 이 매우 재 미 있 습 니 다.재생 기 를 아래로 끌 어 내 린 다음 에 바닥 에 작은 상 자 를 띄 울 수도 있 고 좌우 로 끌 수도 있 습 니 다.그리고 다시 연주 할 때 도 해당 하 는 효과 가 있 습 니 다.이런 상호작용 효 과 는 톱 영상 과 동 영상 에 전념 하 는 app 에서 도 흔히 볼 수 있 습 니 다.
며칠 전에 네티즌 들 이 이 효 과 를 본 것 을 보 니 괜 찮 은 것 같 습 니 다.지금 공유 하면 코드 를 최적화 할 수 있 습 니 다.여기 플레이 어 는 B 역 의 ijkplayer 를 사용 하고 먼저 두 장의 동 영상 을 올 립 니 다.

그림 이 아래쪽 에 도착 하면 좌우 로 드래그 합 니 다.

실현 적 사고
우선,보기 축소 효 과 를 드래그 하면 저 희 는 반드시 하나의 View 를 사용자 정의 해 야 합 니 다.저희 프로젝트 의 장면 에 따라 저 희 는 두 개의 View 가 필요 합 니 다.하 나 는 드래그 하 는 View 이 고 다른 하 나 는 상하 로 움 직 이 는 View(축소 할 수 있 는 View)입 니 다.드래그 를 실현 하기 위해 저 희 는 반드시 View DragHelper 라 는 종 류 를 사용 해 야 한 다 는 것 을 알 고 있 습 니 다.이런 종 류 는 드래그 하기 위해 디자인 된 것 입 니 다.
그 다음 에 끝까지 끌 어 당 기 는 View 에 대해 우 리 는 좌우 로 끌 어 당 기 는 효 과 를 실현 해 야 합 니 다.이것 도 쉽게 이 루어 집 니 다.우 리 는 View DragHelper 의 onView Position Changed 방법 을 통 해 현재 보기 의 상황 을 판단 하면 View 로 크기 조정 과 그 라 데 이 션 을 할 수 있 습 니 다.
코드 분석
우선 용 기 를 사용자 정의 합 니 다.용기 의 init 방법 은 두 개의 View 를 초기 화 합 니 다.mFlexView(도대체 드래그 하 는 View)와 mFollowView(터치 크기 에 따라 View)

 private void init(Context context, AttributeSet attrs) {

    final float density = getResources().getDisplayMetrics().density;
    final float minVel = MIN_FLING_VELOCITY * density;

    ViewGroupCompat.setMotionEventSplittingEnabled(this, false);
    FlexCallback flexCallback = new FlexCallback();
    mDragHelper = ViewDragHelper.create(this, 1.0f, flexCallback);
    //       
    mDragHelper.setMinVelocity(minVel);

    post(new Runnable() {
      @Override
      public void run() {

        //         View,  mFlexView       View,mLinkView    View
        mFlexView = getChildAt(0);
        mFollowView = getChildAt(1);

        mDragHeight = getMeasuredHeight() - mFlexView.getMeasuredHeight();

        mFlexWidth = mFlexView.getMeasuredWidth();
        mFlexHeight = mFlexView.getMeasuredHeight();

      }
    });

  }

ViewDragHelper 의 리 셋 은 해 야 할 일이 많 습 니 다.mFlexView 가 드래그 할 때 mFlexView 와 mFollowView 의 상응하는 변화 효 과 를 동시에 설정 해 야 합 니 다.mFlexView 가 풀 릴 때 닫 거나 걷 는 등의 효 과 를 처리 해 야 합 니 다.그래서 여기 서 우 리 는 뷰 드 래 곤 헬 퍼 의 각종 리 셋 사건 을 감청 해 야 한다.이것 도 본 기능 의 가장 핵심 적 인 것 이다.

 private class FlexCallback extends ViewDragHelper.Callback {

    @Override
    public boolean tryCaptureView(View child, int pointerId) {
      // mFlexView       
      return mFlexView == child;
    }

    @Override
    public int clampViewPositionHorizontal(View child, int left, int dx) {
      return Math.max(Math.min(mDragWidth, left), -mDragWidth);
    }

    @Override
    public int getViewHorizontalDragRange(View child) {
      return mDragWidth * 2;
    }

    @Override
    public int clampViewPositionVertical(View child, int top, int dy) {
      if (!mVerticalDragEnable) {
        //            mFlexView               ,  mDragHeight          
        return mDragHeight;
      }
      return Math.max(Math.min(mDragHeight, top), 0);
    }

    @Override
    public int getViewVerticalDragRange(View child) {
      return mDragHeight;
    }

    @Override
    public void onViewReleased(View releasedChild, float xvel, float yvel) {

      if (mHorizontalDragEnable) {
        //         ,               ,          
        if (xvel > 1500) {
          mDragHelper.settleCapturedViewAt(mDragWidth, mDragHeight);
          mIsClosing = true;
        } else if (xvel < -1500) {
          mDragHelper.settleCapturedViewAt(-mDragWidth, mDragHeight);
          mIsClosing = true;
        } else {
          //            ,            ,    releasedChild.getLeft()    
          float alpha = releasedChild.getAlpha();
          if (releasedChild.getLeft() < 0 && alpha <= 0.4f) {
            mDragHelper.settleCapturedViewAt(-mDragWidth, mDragHeight);
            mIsClosing = true;
          } else if (releasedChild.getLeft() > 0 && alpha <= 0.4f) {
            mDragHelper.settleCapturedViewAt(mDragWidth, mDragHeight);
            mIsClosing = true;
          } else {
            mDragHelper.settleCapturedViewAt(0, mDragHeight);
          }
        }
      } else {
        //                     
        if (yvel > 1500) {
          mDragHelper.settleCapturedViewAt(0, mDragHeight);
        } else if (yvel < -1500) {
          mDragHelper.settleCapturedViewAt(0, 0);
        } else {
          //   releasedChild.getTop()         
          if (releasedChild.getTop() <= mDragHeight / 2) {
            mDragHelper.settleCapturedViewAt(0, 0);
          } else {
            mDragHelper.settleCapturedViewAt(0, mDragHeight);
          }
        }
      }
      invalidate();
    }

    @Override
    public void onViewPositionChanged(final View changedView, int left, int top, int dx, int dy) {

      float fraction = top * 1.0f / mDragHeight;

      // mFlexView     
      mFlexScaleRatio = 1 - 0.5f * fraction;
      mFlexScaleOffset = changedView.getWidth() / 20;
      //       
      changedView.setPivotX(changedView.getWidth() - mFlexScaleOffset);
      changedView.setPivotY(changedView.getHeight() - mFlexScaleOffset);
      //     
      changedView.setScaleX(mFlexScaleRatio);
      changedView.setScaleY(mFlexScaleRatio);

      // mFollowView      
      float alphaRatio = 1 - fraction;
      //      
      mFollowView.setAlpha(alphaRatio);
      //        dy  top,    mFlexView   
      mFollowView.setTop(mFollowView.getTop() + dy);

      //       ,changedView top    mDragHeight,           
      mHorizontalDragEnable = top == mDragHeight;

      if (mHorizontalDragEnable) {
        //           ,          mFlexView   (  getWidth),         mFlexView    
        mDragWidth = (int) (changedView.getMeasuredWidth() * 0.5f);

        //   mFlexView    ,                 
        changedView.setAlpha(1 - Math.abs(left) * 1.0f / mDragWidth);

        //           ,        
        mVerticalDragEnable = left < 0 && left >= -mDragWidth * 0.05;

      } else {
        //          
        changedView.setAlpha(1);
        mDragWidth = 0;

        mVerticalDragEnable = true;

      }

      if (mFlexLayoutPosition == null) {
        //          
        mFlexLayoutPosition = new ChildLayoutPosition();
        mFollowLayoutPosition = new ChildLayoutPosition();
      }

      //         
      mFlexLayoutPosition.setPosition(mFlexView.getLeft(), mFlexView.getRight(), mFlexView.getTop(), mFlexView.getBottom());
      mFollowLayoutPosition.setPosition(mFollowView.getLeft(), mFollowView.getRight(), mFollowView.getTop(), mFollowView.getBottom());

      //      Log.e("FlexCallback", "225 -onViewPositionChanged(): 【" + mFlexView.getLeft() + ":" + mFlexView.getRight() + ":" + mFlexView.getTop() + ":" + mFlexView
      //          .getBottom() + "】 【" + mFollowView.getLeft() + ":" + mFollowView.getRight() + ":" + mFollowView.getTop() + ":" + mFollowView.getBottom() + "】");

    }

  }

다음은 측정 과 포 지 셔 닝 을 처리 합 니 다.우리 가 실현 하 는 배열 효 과 는 LinearLayout 수직 배열 과 유사 합 니 다.여 기 는 measure Child With Margins 의 height Use 를 다시 설정 해 야 합 니 다.onLayout 에서 위치 캐 시가 비어 있 지 않 을 때 바로 위 치 를 정 하 는 이 유 는 View DragHelper 가 터치 이벤트 하위 요 소 를 처리 하 는 데 이동 을 하 는 등 요소 가 UI 를 업데이트 하면 다시 Layout 를 만 들 수 있 기 때문에 FlexCallback 의 onView Position Changed 방법 으로 위 치 를 기록 한 다음 재생 할 때 Layout 를 통 해 이전 보 기 를 복원 해 야 합 니 다.

@Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

    int desireHeight = 0;
    int desireWidth = 0;

    int tmpHeight = 0;

    if (getChildCount() != 2) {
      throw new IllegalArgumentException("          View!");
    }

    if (getChildCount() > 0) {
      for (int i = 0; i < getChildCount(); i++) {
        final View child = getChildAt(i);
        //            
        //   heightUse:             ,           view       ;         View    ,         
        measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, tmpHeight);
        //           
        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
        //        ,        
        desireWidth = Math.max(desireWidth, child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
        //        
        tmpHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
        desireHeight += tmpHeight;
      }
      //         
      desireWidth += getPaddingLeft() + getPaddingRight();
      desireHeight += getPaddingTop() + getPaddingBottom();
      //                     
      desireWidth = Math.max(desireWidth, getSuggestedMinimumWidth());
      desireHeight = Math.max(desireHeight, getSuggestedMinimumHeight());
    }
    //        
    setMeasuredDimension(resolveSize(desireWidth, widthMeasureSpec), resolveSize(desireHeight, heightMeasureSpec));
  }

  @Override
  protected void onLayout(boolean changed, int l, int t, int r, int b) {

    if (mFlexLayoutPosition != null) {
      //      ViewDragHelper         ,    View UI      Layout  ,        ViewDragHelper    View   ,         
      // Log.e("YytLayout1", "292 -onLayout(): " + "        ");
      mFlexView.layout(mFlexLayoutPosition.getLeft(), mFlexLayoutPosition.getTop(), mFlexLayoutPosition.getRight(), mFlexLayoutPosition.getBottom());
      mFollowView.layout(mFollowLayoutPosition.getLeft(), mFollowLayoutPosition.getTop(), mFollowLayoutPosition.getRight(), mFollowLayoutPosition.getBottom());
      return;
    }

    final int paddingLeft = getPaddingLeft();
    final int paddingTop = getPaddingTop();

    int multiHeight = 0;

    int count = getChildCount();

    if (count != 2) {
      throw new IllegalArgumentException("            2!");
    }

    for (int i = 0; i < count; i++) {
      //               
      final View child = getChildAt(i);
      MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

      int left = paddingLeft + lp.leftMargin;
      int right = child.getMeasuredWidth() + left;

      int top = (i == 0 ? paddingTop : 0) + lp.topMargin + multiHeight;
      int bottom = child.getMeasuredHeight() + top;

      child.layout(left, top, right, bottom);

      multiHeight += (child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
    }

  }

터치 이벤트 의 처 리 는 크기 조정 이 mFlexView 의 실제 너비 에 영향 을 주지 않 기 때문에 ViewDragHelper 는 mFlexView 의 실제 너비 와 높 은 구역 을 차단 하기 때문에 손가락 이 mFlexView 시각 적 범위 에 떨 어 졌 는 지 판단 하고 ViewDragHelper 의 shouldInterceptTouchEvent 방법 을 조정 합 니 다.

 @Override
  public boolean onInterceptTouchEvent(MotionEvent ev) {

    // Log.e("YytLayout", mFlexView.getLeft() + ";" + mFlexView.getTop() + " --- " + ev.getX() + ":" + ev.getY());

    //         mFlexView    ,            
    float left = mFlexView.getLeft() + mFlexWidth * (1 - mFlexScaleRatio) - mFlexScaleOffset * (1 - mFlexScaleRatio);
    float top = mFlexView.getTop() + mFlexHeight * (1 - mFlexScaleRatio) - mFlexScaleOffset * (1 - mFlexScaleRatio);

    //               mFlexView       
    mInFlexViewTouchRange = ev.getX() >= left && ev.getY() >= top;

    if (mInFlexViewTouchRange) {

      return mDragHelper.shouldInterceptTouchEvent(ev);

    } else {
      return super.onInterceptTouchEvent(ev);
    }
  }

  @Override
  public boolean onTouchEvent(MotionEvent event) {
    if (mInFlexViewTouchRange) {
      //           ,        ,   Layout  View     ,       Layout
      mDragHelper.processTouchEvent(event);
      return true;
    } else {
      //   mFlexView     ,   View    ,  false,       
      return false;
    }
  }

동시에 스크롤 이벤트 에 대한 감청 이 필요 합 니 다.이 곳 에서 닫 힌 모든 이동 실행 이벤트 가 필요 합 니 다.

 @Override
  public void computeScroll() {
    if (mDragHelper.continueSettling(true)) {
      invalidate();
    } else if (mIsClosing && mOnLayoutStateListener != null) {
      //         ,       ,        
      mOnLayoutStateListener.onClose();
      mIsClosing = false;
    }
  }

  /**
   *              
   */
  public interface OnLayoutStateListener {

    void onClose();

  }

  public void setOnLayoutStateListener(OnLayoutStateListener onLayoutStateListener) {
    mOnLayoutStateListener = onLayoutStateListener;
  }

  /**
   *     
   */
  public void expand() {
    mDragHelper.smoothSlideViewTo(mFlexView, 0, 0);
    invalidate();
  }
그리고 실제 응용 에서 리 턴 후 상세 한 페이지 의 효 과 를 실현 하려 면 우 리 는 스스로 조합 View 를 실현 해 야 한다.이것 은 여러분 이 직접 볼 수 있다소스 코드
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

좋은 웹페이지 즐겨찾기