안드로이드 완두 꼬투리 모방 앱 리스트 추가 효과

11000 단어
이 글은 위챗 공식 계정 goolin블로그(곽림) 독점 발표
효과도.gif
엊그저께 안드로이드폰(ps: 아이폰폰을 사용해 왔다)을 사서 앱 시장에 내려주려고 했는데 완두콩꼬투리를 좋아해서 완두콩꼬투리를 만들었어요. 완두콩꼬투리에서 앱을 다운받았는데 리스트가 화면에 들어오는 게 재미있어서 직접 흉내를 내려고 했어요.
사고의 방향
처음에 이 효과를 보았을 때 리스트 인터페이스와 상세 인터페이스가 Activity+dialog인지 아니면 두 개의 Activity인지 생각했다. 나중에 생각해 보니 상세 인터페이스에 데이터가 많은 것이 Activity일 가능성이 크지 않고 리스트 Activity일 수도 있고 상세 Activity일 수도 있다. 그러면 이 디자인에 대해 해결해야 할 문제가 많을 것이다.
  • 점프할 때 어떻게 클릭한 Item View가 상세 정보Activity에서 같은 위치에 나타날까요?
  • 점프에 성공한 후에 어떻게 앞의 Activity 내용을 볼 수 있습니까?
  • 클릭한 Item View를 상세 정보 페이지로 변경하려면 어떻게 해야 합니까?
  • 자세히 보기 아래에서 화면을 꺼낼 때 어떻게 자세히 보기Activity를 종료합니까?
  • 아래로 당길 때 배경색의 투명도를 어떻게 동적으로 바꿉니까?

  • 이런 문제들을 가지고 저희가 하나하나 분석해서 해결해 보도록 하겠습니다.
    이루어지다
  • 점프할 때 어떻게 클릭한 Item View가 상세 정보Activity에서 같은 위치에 나타날까요?우리는 View가 레이아웃이 완성된 후에 부류 View의 맨 위에 있는 속성 top가 있다는 것을 알고 있다. 그러면 두 개의Activity에서 View의 맨 위에 있는 높이 top를 일치하게 설정하면 된다. 그리고 점프할 때 점프 애니메이션을 제거하면 시각적인 틈새 없는 연결을 실현할 수 있다. 다음은 구체적인 코드
  • 를 살펴보자.
    int viewMarginTop = view.getTop() + getResources().getDimensionPixelOffset(R.dimen.bar_view_height);
    Intent intent = new Intent(MainActivity.this, DetailActivity.class);
    intent.putExtra("viewMarginTop", viewMarginTop);
    intent.putExtra("imageId", (int) array[0]);
    intent.putExtra("appName", (String) array[1]);startActivity(intent);
    overridePendingTransition(0, 0);
    

    view는 현재 클릭한 Item View,view입니다.getTop () 은 ItemView가 RecyclerView 맨 위에 있는 높이입니다. getResources ().dimensionPixel Offset(R.dimen.bar view height)는 RecyclerView 위에 있는 Title View의 높이입니다. 저는 상태 표시줄을 숨겼기 때문에 모든viewMarginTop은 현재 클릭한 Item View가 상태 표시줄 맨 위에 있는 높이입니다.overridePendingTransition(0,0)은 점프 애니메이션을 없애고 시각에 틈이 없도록 하는 것이다. 자세한 내용은Activity가 어떻게 표시되는지 아래에서 분석한다.
  • 점프에 성공한 후에 어떻게 앞의 Activity 내용을 볼 수 있습니까?사실은 상세 정보Activity 배경을 투명하게 설정하고 상세 정보View의 부류 View 배경을 투명하게 설정하면 됩니다. 다음은 코드 구현을 보십시오. 이것은Activity에 투명한 Theme
  • 를 설정하는 것입니다.
    
    
    
  • 클릭한 Item View를 상세 정보 페이지로 변경하려면 어떻게 해야 합니까?세부 정보 페이지로 이동한 후 목록 페이지에서 클릭한 Item View를 표시하고 위쪽 높이
  • 를 설정합니다.
    mSVRootLl.setContentInitMarginTop(mViewMarginTop);
    //  
    public void setContentInitMarginTop(int marginTop) {    
        mContentMarginTop = marginTop;    
        requestLayout();
     }
    

    mViewMarginTop은 목록 인터페이스에서 전달되는 매개 변수이고 mSVRootLl은 ScrollView 아래의 루트LinearLayout입니다. 자세한 페이지는 굴러갈 수 있기 때문에 ScrollView가 필요합니다. 높이를 설정한 후에 RequestLayout 방법으로 레이아웃을 시작하고 onLayout 방법으로 레이아웃 높이를 설정하면 됩니다.
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {    
        super.onLayout(changed, l, t, r, b);    
    
        int contentTop = mContentMarginTop + mTouchMoveOffset;    
        mContentLL.layout(0, contentTop, mContentLlWidth, !mIsAnimation ? contentTop + mContentLlHeight : mInitBottom + mContentBottomOffset);   
    
        if(!mIsLayoutImageView) return;    
        int left = mMargin + mImageLeftOffset;    
        int top = mMargin + mImageTopOffset;    
        mIconImageView.layout(left, top, left + mIconImageViewWidth, top + mIconImageViewHeight);}
    

    mContent MarginTop은 바로 방금 설정한 높이입니다. mTouch Move Offset 기본값은 0이고 mIs Animation 기본값은false입니다. mIs Layout ImageView 기본값은false입니다. 이런 매개 변수의 의미는 다음에 분석할 것입니다. 이렇게 하면 View의 높이는 목록 인터페이스에서 클릭한 Item View의 높이와 같습니다. 다음에 클릭한 Item View가 어떻게 상세 페이지로 바뀌었는지 분석합니다.
    클릭한 Item View가 상세 정보Activity에 도착한 후에 Linear Layout 레이아웃이 되었다. 이 레이아웃은 세 부분으로 나뉜다. 타이틀 레이아웃, 중간 레이아웃,bottom 레이아웃이다. 기본 타이틀과bottom은 숨겨져 있기 때문에 기본적인 효과는 목록 인터페이스에서 Item View를 클릭한 효과이다. 이 View가 나타나면 바로 애니메이션을 통해 상세 인터페이스로 변한다.바로 위 애니메이션이 완성된 후의 효과입니다. 다음은 애니메이션의 논리 코드를 살펴보겠습니다.
    private void startAnimation() {    
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1).setDuration(400);    
        valueAnimator.setStartDelay(100);    
        valueAnimator.start();    
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {   
         
           @Override        
           public void onAnimationUpdate(ValueAnimator animation) {            
               float ratio = (float) animation.getAnimatedValue();           
               //                    
               int contentTopOffset = (int) (ratio * mContentTopOffsetNum);            
               //                     
               int contentBottomOffset = (int) (ratio * mContentBottomOffsetNum);            
               //                   
               int imageLeftOffset = (int) (ratio * mImageLeftOffsetNum);            
               //                   
               int imageTopOffset =  (int) (ratio * mImageTopOffsetNum); 
         
               mSVRootLl.setAllViewOffset(mViewMarginTop - contentTopOffset, contentBottomOffset, imageLeftOffset, imageTopOffset);        
            }    
         });    
       valueAnimator.addListener(new AnimatorListenerAdapter() {        
            @Override        
            public void onAnimationEnd(Animator animation) {     
              super.onAnimationEnd(animation);   
              mSVRootLl.setAnimationStatus(false);        
              mBottomLl.setVisibility(View.VISIBLE);     
              mTitleLl.setVisibility(View.VISIBLE);       
           }   
       });
    }
    

    onAnimationUpdate 방법에서 ratio에 따라 4개의 편이량을 계산하는 것을 볼 수 있다. 이 4개의 편이량은 무슨 소용이 있을까?클릭한 Item View가 애니메이션을 통해 상세 뷰로 바뀝니다.이 변화의 과정은 4가지 부분을 포함한다. 1: Item View의 상단 거리가 상단 거리가 점점 가까워진다. 2: Item View의 하단 거리가 하단 거리가 점점 가까워진다. 3: Item View의 그림은 천천히 가운데에 있다. 4: Item View의 그림은 천천히 아래로 가까워진다. (아래로 다가오지 않으면 애니메이션이 끝난 후에 title 레이아웃이 표시되고 그림은 아래로 점프하는 문제가 있다)
    그러면 이 네 부분이 이동하는 총 거리를 ratio에 곱하면 애니메이션 실행 과정에서 매번 편이량이다. 그리고 편이량을 계속 설정해서 RequestLayout 방법을 호출하여 레이아웃을 해서 View가 애니메이션의 효과에 도달하도록 한다.위에서 말한 mIs Animation 이 필드는 이때true입니다. mIs Layout ImageView는true입니다. 애니메이션을 실행할 때만 이미지 컨트롤을 다시 레이아웃하고 애니메이션이 끝난 후에title 레이아웃과bottom 레이아웃을 표시합니다
  • 자세히 보기 아래에서 화면을 꺼낼 때 어떻게 자세히 보기Activity를 종료합니까?효과에서 볼 수 있듯이 드롭다운 높이가 절반을 넘으면 등속으로 아래로 미끄러지고, 화면이 미끄러지면 Activity를 끄고, 절반보다 작으면 원래 상태로 되돌려줍니다. 이 기능은 이벤트 분배 원리를 사용해야 합니다. ScrollView Y축이 0으로 굴러가고 드롭다운으로 굴러갈 때 View가 이 이벤트를 미끄러지게 됩니다. ACTIONDOWN 레코드 초기점, ACTIONMOVE는 현재 점을 얻고 현재 점은 초기 점이 미끄러지는 거리(바로 위의 mTouch Move Offset 변수)를 줄이고 리퀘스트 Layout 방법에 따라 레이아웃을 요청하여 onLayout 방법으로 인터페이스를 리셋한다. 손가락이 풀릴 때 미끄러지는 거리가 절반을 넘는지 판단하고 서로 다른 상태에 따라 애니메이션을 통해 mTouch Move Offset의 수치를 바꾸어 인터페이스를 리셋한다.다음은 제스처가 미끄러지고 풀린 후 애니메이션이 실행되는 코드
  • 를 보겠습니다.
      @Override
      public boolean onTouchEvent(MotionEvent event) {    
         boolean consumption = true;    
         switch (event.getAction()) {        
            case MotionEvent.ACTION_DOWN:            
                mInitY = event.getY();            
                getParent().requestDisallowInterceptTouchEvent(true);            
                break;        
            case MotionEvent.ACTION_MOVE:           
                float moveY = event.getY();            
                float yOffset = moveY - mInitY;           
                //              
                if((mParentScrollView.getScrollY() <= 0 && moveY >= mInitY) || mIsDrag) {     
                    setTouchMoveOffset(yOffset);                
                    mIsDrag = true;                
                    consumption = true;            
                } else {                
                    getParent().requestDisallowInterceptTouchEvent(false);       
                    consumption = false;            
                }            
                break;        
            case MotionEvent.ACTION_UP:            
                mIsDrag = false;            
                boolean isUp = false;            
                int animationMoveOffset;           
                if(mContentLL.getTop() <= mCenterVisibleViewHeight / 2 + mTitleViewHeight) {                
                     animationMoveOffset = mTouchMoveOffset;                
                     isUp = true;            
                } else {                
                     animationMoveOffset = mParentScrollView.getHeight() - mContentLL.getTop();    
                }              
                startAnimation(animationMoveOffset, isUp, mTouchMoveOffset);         
                break;    
         }   
             return consumption;
    }
    
    public void startAnimation(final int moveOffset, final boolean isUp, final int currentMoveOffset) {    
       int duration = moveOffset / mTouchSlop * 10;   
       if(duration <= 0) duration = 300;    
       ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1).setDuration(duration);    
       valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {        
          @Override        
          public void onAnimationUpdate(ValueAnimator animation) {            
              float ratio = (float) animation.getAnimatedValue();            
              if(isUp)                
               mTouchMoveOffset = (int) (moveOffset * (1 - ratio));            
              else                
               mTouchMoveOffset = currentMoveOffset + (int) (moveOffset * ratio);      
               requestLayout();            
               updateBgColor(mTouchMoveOffset);        
          }    
       });    
       valueAnimator.addListener(new AnimatorListenerAdapter() {        
         @Override        
         public void onAnimationEnd(Animator animation) {     
             super.onAnimationEnd(animation);            
             if(!isUp && mOnCloseListener != null) mOnCloseListener.onClose();        
         }    
     });    
       valueAnimator.start();
    }
    

    애니메이션 실행 후 콜백 방법을 호출하여 상세 정보 닫기 Activity
    mSVRootLl.setOnCloseListener(new SVRootLinearLayout.OnCloseListener() {    
         @Override    
         public void onClose() {        
            finish();        
           overridePendingTransition(0, 0);    
        }
    });
    
  • 아래로 당길 때 배경색의 투명도를 어떻게 동적으로 바꿉니까?코드를 통해 제스처가 미끄러질 때와 애니메이션을 할 때 업데이트BgColor 방법을 호출한 것을 볼 수 있다
  • public void updateBgColor(int offset) {    
       if(mOnUpdateBgColorListener != null) {        
          float ratio = BigDecimalUtils.divide(offset, mCenterVisibleViewHeight);        
        if(ratio > 1) ratio = 1;        
        if(ratio < 0) ratio = 0;        
        mOnUpdateBgColorListener.onUpdate(ratio);    
       }
    }
    

    이 방법의 mOn Update Bg Color Listener는 자세한 정보Activity 설정입니다. onUpdate 방법을 호출하여 배경의 색 투명도를 변경합니다.
    mSVRootLl.setOnUpdateBgColorListener(new SVRootLinearLayout.OnUpdateBgColorListener() {    
        @Override    
        public void onUpdate(float ratio) {        
          mRootCDrawable.setAlpha((int) (mColorInitAlpha - mColorInitAlpha * ratio));    
        }
    });
    

    끝말
    지금까지 프로젝트의 논리와 코드를 모두 분석했습니다. 문장을 통해 우리는 하나의 기능을 개발할 때 먼저 대체적인 사고방식을 가지고 이 기능을 어떻게 실현하고 싶은지 알 수 있습니다. 그리고 사고방식 중의 문제를 하나하나 해독하고 마지막에 연결하면 됩니다. 읽어주셔서 감사합니다. 원본을 보고 싶은 분은 github 주소로 이동하셔도 됩니다.https://github.com/chenpengfei88/WdjAppDetail스타도 환영합니다. 팔로우 본인도 환영합니다. 감사합니다.

    좋은 웹페이지 즐겨찾기