Android 사용자 정의 View 모방 텐 센트 TIM 드 롭 다운 새로 고침 View

개술
사용자 정의 뷰 는 안 드 로 이 드 개발 의 한 대학 질문 입 니 다.우연히 TIM 메 일 박스 인터페이스의 새로 고침 View 가 재 미 있 는 것 을 보 았 습 니 다.그래서 스스로 하 나 를 실 현 했 습 니 다.먼저 TIM 안의 효과 도 를 보 세 요.

2 수요 분석
위의 동 도 를 보면 우리 가 실현 해 야 할 기능 도 알 수 있다.
4.567917.끌 리 는 진도 에 따라 작은 공의 위 치 를 이동한다.4.567918.
4
  • 작은 공의 이동 과정 에 대한 애니메이션
  • 삼 기능 실현
    새로 만 든 Refreshview 클래스 는 View 에서 계승 한 다음 에 Refreshview 에서 내부 실체 클래스 를 새로 만 듭 니 다:Circle
    Circle 코드 를 볼 게 요.
    #Cirlce.java
    
     class Circle {
     int x;
     int y;
     int r;
     int color;
    
     public Circle(int x, int y, int r, int color) {
      this.x = x;
      this.y = y;
      this.r = r;
      this.color = color;
     }
     }
    이것 은 실체 류 로 그 안에 x,y,r,color 속성 을 제공 하여 각각 원심 좌표 의 x 값,y 값,원 의 반지름 r 와 색 을 대표 합 니 다.
    이 를 통 해 작은 원구 의 관련 속성 을 저장 합 니 다.
    다음은 우리 가 평소에 사용자 정의 View 를 자주 다시 쓰 는 세 가지 방법 입 니 다.먼저 onMeasure()를 보 세 요.
    #RefreshView.java
    
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
     int widthMode = MeasureSpec.getMode(widthMeasureSpec);
     int widthSize = MeasureSpec.getSize(widthMeasureSpec);
     int heightMode = MeasureSpec.getMode(heightMeasureSpec);
     int heightSize = MeasureSpec.getSize(heightMeasureSpec);
     if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.EXACTLY) {
      setMeasuredDimension(mWidth, heightSize);
     } else if (widthMeasureSpec == MeasureSpec.EXACTLY && heightMeasureSpec == MeasureSpec.AT_MOST) {
      setMeasuredDimension(widthSize, mHeight);
     } else if (widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY) {
      setMeasuredDimension(widthSize, heightSize);
     } else {
      setMeasuredDimension(mWidth, mHeight);
     }
     }
    레이아웃 파일 에 적합 한 wrapcontent 매개 변 수 는 이 방법 을 다시 써 야 합 니 다.
    이어서 onLayout()방법 을 보십시오.
    #RefreshView.java
    
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
     super.onLayout(changed, left, top, right, bottom);
     initContentAttr(getMeasuredWidth(), getMeasuredHeight());
     resetCircles();
     }
    이 방법 에 서 는 initContentAttr()방법 을 사용 하여 내용 크기 와 resetCircles()를 초기 화하 여 세 개의 작은 공의 속성 을 초기 화 합 니 다.이 두 가지 방법 을 각각 살 펴 보 자.
    #RefreshView.java
    
     private void initContentAttr(int width, int height) {
     mContentWidth = width - getPaddingLeft() - getPaddingRight();
     mContentHeight = height - getPaddingTop() - getPaddingBottom();
     }
    이 방법 은 매우 간단 하 다.바로 padding 처 리 를 해서 진정한 레이아웃 크기 를 얻 는 것 이다.padding 을 처리 하지 않 으 면 사용자 가 padding 을 설정 하면 효력 을 잃 습 니 다.resetCircles()다시 보기:
    #RefreshView.java
    
     public static final int STATE_ORIGIN = 0;
     public static final int STATE_PREPARED = 1;
     private int mOriginState = STATE_ORIGIN;
    
     private void resetCircles() {
     if (mCircles.isEmpty()) {
      int x = mContentWidth / 2;
      int y = mContentHeight / 2;
      mGap = x - mMinRadius; //             
      Circle circleLeft = new Circle(x, y, mMinRadius, 0xffff7f0a);
      Circle circleCenter = new Circle(x, y, mMaxRadius, Color.RED);
      Circle circleRight = new Circle(x, y, mMinRadius, Color.GREEN);
      mCircles.add(LEFT, circleLeft);
      mCircles.add(RIGHT, circleRight);
      mCircles.add(CENTER, circleCenter);
     }
     if (mOriginState == STATE_ORIGIN) {
      int x = mContentWidth / 2;
      int y = mContentHeight / 2;
      for (int i = 0; i < mCircles.size(); i++) {
      Circle circle = mCircles.get(i);
      circle.x = x;
      circle.y = y;
      if (i == CENTER) {
       circle.r = mMaxRadius;
      } else {
       circle.r = mMinRadius;
      }
      }
     } else {
      prepareToStart();
     }
     }
    이 방법 은 작은 공 을 초기 화하 고 초기 화 하 는 데 사 용 됩 니 다.방법 에서 진행 되 는 두 개의 큰 if..else 문 구 는 작은 공 을 초기 화 해 야 하 는 지 판단 하 는 데 사 용 됩 니 다.두 번 째 문 구 는 작은 공의 초기 화 형 태 를 판단 하 는 데 사 용 됩 니 다.외부 에서 setOriginState()방법 을 사용 하여 작은 공의 초기 화 형 태 를 지정 할 수 있 습 니 다.지정 하지 않 으 면 기본적으로 NOMAL,즉 세 개의 공이 겹 치 는 것 입 니 다.
    #RefreshView.java
    
     /**
     *         
     * {@link #STATE_ORIGIN}     (      ),
     * {@link #STATE_PREPARED}           ,        
     */
     public void setOriginState(int state) {
     if (state == 0) {
      mOriginState = STATE_ORIGIN;
     } else {
      mOriginState = STATE_PREPARED;
     }
     }
    마지막 으로 가장 재 미 있 는 방법 인 onDraw()입 니 다.
    #RefreshView.java
    
     @Override
     protected void onDraw(Canvas canvas) {
     for (Circle circle : mCircles) {
      mPaint.setColor(circle.color);
      canvas.drawCircle(circle.x + getPaddingLeft(), circle.y + getPaddingTop(), circle.r, mPaint);
     }
     }
    이 방법 은 간단 합 니 다.mCircles 목록 에 있 는 원 을 그 렸 을 뿐 입 니 다.
    세 가지 방법 을 다 말 했 지만 이것 은 단지 몇 개의 작은 둥 근 공 을 그 렸 을 뿐이다.우리 의 수요 분석 에서 의 수 요 는 아직 실현 되 지 않 았 다.위의 방법 은 View 의 기 초 를 닦 았 기 때문에 이것 을 실현 하 는 것 도 어렵 지 않다.다음은 여러분 이 기대 하 는 수요 가 이 루어 졌 습 니 다.
    드래그 하 는 진도 에 따라 작은 공의 위 치 를 이동 합 니 다.
    구현 코드 는 다음 과 같 습 니 다:
    #RefreshView.java
    
     public void drag(float fraction) {
     if (mOriginState == STATE_PREPARED) {
      return;
     }
     if (mAnimator != null && mAnimator.isRunning()) {
      return;
     }
     if (fraction > 1) {
      return;
     }
     mCircles.get(LEFT).x = (int) (mMinRadius + mGap * (1f - fraction));
     mCircles.get(RIGHT).x = (int) (mContentWidth / 2 + mGap * fraction);
     postInvalidate();
     }
    방법 안에서 세 번 의 판단 을 하고 초기 상태 가 STATE 이면PREPARED(세 개의 공이 가장 거리 가 멀 어서 더 이상 변동 할 필요 가 없다),애니메이션 이 진행 중이 거나 진도 가 1 보다 크 면 이동 하지 않 는 다.그리고 작은 공의 속성 을 수정 하고 다시 그립 니 다.
    작은 공 이동 과정의 애니메이션
    이것 은 사용자 정의 View 의 가장 어 려 운 부분 입 니 다.수학의 작은 연산 이 필요 합 니 다.좀 번 거 롭 습 니 다.
    우 리 는 먼저 애니메이션 을 실현 하 는 논 리 를 정리 하고 시작 하 는 gif 를 보면 알 수 있 을 것 이다.애니메이션 을 시작 할 때 왼쪽 의 작은 공 은 가장 왼쪽 에 있 고 중간 에 있 는 작은 공 은 중간 에 있 으 며 오른쪽 은 가장 오른쪽 에 있다.우 리 는 하나하나 작은 공 으로 분석 했다.
    4.567917.왼쪽 작은 공:애니메이션 이 시 작 된 후에 왼쪽 의 작은 공 은 오른쪽으로 이동 하고 점점 커 졌 다.작은 공이 중심 점 까지 운동 할 때 까지 중심 점 을 지나 면 작은 공 은 계속 오른쪽으로 이동 하지만 점점 작 아 졌 다.종점 에 도착 하면 작은 공 은 사라 질 것 이다(사라 지 는 과정 은 먼저 축소 하고 다시 사라 지 는 것 이다).그 다음 에 왼쪽 에서 나 타 났 다(나타 나 는 과정 도 작은 것 에서 큰 것 으로 변화 하고 아래 는 같다).그리고 상술 한 과정 을 반복 한다4.567917.중간 공:중간의 작은 공 은 먼저 오른쪽으로 이동 하고 점점 줄 어 든 다음 에 사라 진 다음 에 왼쪽 에서 나타 나 고 마지막 에 중간 으로 이동 하 며 그 사이 에 점점 커진다.뒤 에는 상술 한 동작 이 반복 된다4.567917.오른쪽 작은 공:오른쪽 에 있 는 작은 공 은 먼저 사라 진 다음 에 왼쪽 에서 나타 나 고 그 다음 에 중간 으로 이동 하 며 그 사이 에 점점 커지 고 그 다음 에 중심 점 에서 말단 으로 이동 하 며 그 사이 에 점점 줄어든다작은 공의 이동 과정 을 정리 하 는 것 은 코드 의 실현 에 매우 도움 이 되 므 로 우 리 는 분석 할 수 있다.
    1)작은 공 마다 좌표계 의 이동 특징 이 같다.
    2)작은 공 마다 애니메이션 의 진도 이동 특징 이 다르다.
    듣 기 에 좀 까다 로 운 것 같 습 니 다.우 리 는 사람의 말로 설명 하 겠 습 니 다.
    1)각 작은 공 은 좌표계 의 이동 특징 에 대해 똑 같 습 니 다.왼쪽 의 작은 공 은 좌표 의 맨 왼쪽 에 먼저 나타 난 다음 에 오른쪽으로 이동 합 니 다.그러면 중간 과 오른쪽 작은 공 은 요?사실은 똑 같 습 니 다.그들 은 좌표 축 의 가장 왼쪽 에 있 을 때 먼저 나타 나 고 오른쪽으로 이동 합 니 다.어떤 작은 공 이 든 좌표 축 의 같은 점 에서 의 동작 과 형 태 는 일치 해 야 합 니 다.
    2)모든 작은 공이 애니메이션 의 진도 에 대한 이동 특징 은 다르다.왼쪽 의 작은 공 은 애니메이션 이 시작 할 때 가장 왼쪽 에 있 고 중간 에 있 는 작은 공 은 중간 에 있 으 며 오른쪽 은 가장 오른쪽 에 있다.애니메이션 이 시 작 된 후에 예 를 들 어 반 쯤 진행 되 었 을 때 왼쪽 의 작은 공 은 중심 점 근처 로 이동 해 야 한다.중간 은 확실히 말단(사라 짐)이 고 오른쪽 에 있 는 작은 공 은 중간 근처에 나타난다.
    위 에서 분석 한 논리 에 따 르 면 저 는 애니메이션 의 총 진 도 를 6 부 로 나 누 었 는데 왜 6 부 입 니까?위의 애니메이션 분석 을 통 해 작은 공 은 과정 을 거 쳐 야 한 다 는 것 을 알 수 있다.
    4.567917.출현(무 그 라 데 이 션 에서 초기 크기 로)
  • 가장 왼쪽 에서 중심 점 으로 이동(기간 이 커 짐)
  • 중심 점 에서 말단 으로 이동(기간 축소)
  • 사라 짐(초기 크기 에서 사라 짐)작은 공 사이 의 간격 을 아름 다운 상태 로 유지 하기 위해(애니메이션 이 시 작 된 후에 작은 공 사이 가 겹 치지 않 고 인접 한 작은 공의 간격 이 대체적으로 일치)1,4 의 등장 과 사라 지 는 단 계 를 각각 1/6 의 애니메이션 주기 로 설정 하고 중간 2,3 두 단 계 는 각각 1/3 개의 애니메이션 주 기 를 차지한다.

    이렇게 되면 등장 과 사라 짐 은 1/3 애니메이션 진 도 를 차 지 했 고 다른 두 부분 은 각각 1/3 애니메이션 진 도 를 차지 했다.예 를 들 어 애니메이션 을 시작 할 때 가장 왼쪽 에 있 는 작은 공 을 1 로 설정 하고 중간 에 있 는 작은 공 은 2 이 며 가장 오른쪽 에 있 는 작은 공 은 3 이다.
    작은 공 1 이 중심 점 으로 이동 할 때 애니메이션 은 1/3 을 진행 했다.그러면 이때 의 작은 공 2 는 말단 으로 이동 해 야 하고 작은 공 3 은 사라 지고 나타 나 는 과정 을 겪 었 기 때문에 좌표 축의 출발점 에 나타 나 야 한다.
    이 를 통 해 알 수 있 듯 이 처음에 있 었 던 상황(작은 공 하 나 는 왼쪽 에 있 고 하 나 는 가운데 에 있 으 며 하 나 는 오른쪽 에 있다)은 색깔 이 다 를 뿐이다.이런 유추 로 무한 순환 하면 아름 다운 애니메이션 을 형성 할 수 있다.
    이런 것들 을 분석 해 내 면 무슨 소 용이 있 습 니까?나 는 좌표 로 작은 공의 이동 을 확정 하 는 데 작은 문제 가 있 을 수 있다 는 것 을 알 았 기 때문에 애니메이션 의 진도 로 이 루어 졌 다.다음은 구체 적 인 실현 을 살 펴 보 자.
    작은 공의 무한 운동 을 실현 해 야 합 니 다.가장 실 용적 인 것 은 애니메이션 으로 이 루어 지 는 것 입 니 다.여기 서 저 는 속성 애니메이션 을 사 용 했 습 니 다.Animotor 클래스 초기 화:
    #RefreshView.java
    
     private void initAnimator() {
     ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
     animator.setDuration(1500);
     animator.setRepeatCount(-1);
     animator.setRepeatMode(ValueAnimator.RESTART);
     animator.setInterpolator(new LinearInterpolator());
     animator.addListener(new Animator.AnimatorListener() {
      @Override
      public void onAnimationStart(Animator animation) {
      prepareToStart(); //  View         
      }
    
      @Override
      public void onAnimationEnd(Animator animation) {
    
      }
    
      @Override
      public void onAnimationCancel(Animator animation) {
      }
    
      @Override
      public void onAnimationRepeat(Animator animation) {
      }
     });
     animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    
      @Override
      public void onAnimationUpdate(ValueAnimator animation) {
      for (Circle circle : mCircles) {
       updateCircle(circle, mCircles.indexOf(circle), animation.getAnimatedFraction());
      }
      postInvalidate();
      }
     });
     mAnimator = animator;
     }
    이것 은 무한 순환 애니메이션 으로 수 동 으로 멈 추 지 않 으 면 계속 순환 하 는 것 을 볼 수 있다.mAnimator 에 대해 서 는 모니터 도 추가 되 었 습 니 다.애니메이션 을 시작 할 때 prepareToStart()방법 을 사용 하 는 것 입 니 다.이 방법 은 낯 이 익 지 않 습 니까?맞습니다.바로 우리 위 에 resetCircles()에서 작은 공의 형 태 를 STATE 로 판단 하 는 것 입 니 다.PREPARED 는 호출 된 것 으로,이 방법 은 작은 공이 새로 고침 의 임계 점 에 도달 하도록 확보 할 것 이다.UpdateLisener 의 onAnimationUpdate()방법 중의 updateCircle()방법 을 살 펴 보 겠 습 니 다.
    #RefreshView
    
     private void updateCircle(Circle circle, int index, float fraction) {
     float progress = fraction; //    
     float virtualFraction; //           
     switch (index) {
      case LEFT:
      if (fraction < 5f / 6f) {
       progress = progress + 1f / 6f;
      } else {
       progress = progress - 5f / 6f;
      }
      break;
      case CENTER:
      if (fraction < 0.5f) {
       progress = progress + 0.5f;
      } else {
       progress = progress - 0.5f;
      }
      break;
      case RIGHT:
      if (fraction < 1f / 6f) {
       progress += 5f / 6f;
      } else {
       progress -= 1f / 6f;
      }
      break;
     }
     if (progress <= 1f / 6f) {
      virtualFraction = progress * 6;
      appear(circle, virtualFraction);
      return;
     }
     if (progress >= 5f / 6f) {
      virtualFraction = (progress - 5f / 6f) * 6;
      disappear(circle, virtualFraction);
      return;
     }
     virtualFraction = (progress - 1f / 6f) * 3f / 2f;
     move(circle, virtualFraction);
     }
    나 는 하나의 virtual Fraction 을 사용 하여 모든 작은 공의 가상 진 도 를 나 타 냈 다.예 를 들 어 애니메이션 의 전체 진도 가 0 일 때 왼쪽 작은 공의 가상 진 도 는 1/6+0(기본 적 으로 나타 나 는 과정 을 거 쳤 고 1/6 소모)이 어야 한다.중간 작은 공의 가상 진 도 는 1/6+1/3+0=1/2(기본 적 으로 나타 나 는 과정 을 거 쳤 다.중간 으로 이동)가장 오른쪽 작은 공의 가상 진 도 는 1/6+1/3+1/3+0=5/6 이다.그 다음 에 애니메이션 의 전체 진도 가 1/3 에 이 르 렀 을 때 왼쪽 공의 가상 진 도 는 1/2(중간 위치)이다.
    다음은 move(),appeared(),disapear()방법 을 살 펴 보 겠 습 니 다.
    #RefreshView
    
     private void appear(Circle circle, float fraction) {
     circle.r = (int) (mMinRadius * fraction);
     circle.x = mMinRadius;
     }
    
     private void disappear(Circle circle, float fraction) {
     circle.r = (int) (mMinRadius * (1 - fraction));
     }
    
     private void move(Circle circle, float fraction) {
     int difference = mMaxRadius - mMinRadius;
     if (fraction < 0.5) {
      circle.r = (int) (mMinRadius + difference * fraction * 2);
     } else {
      circle.r = (int) (mMaxRadius - difference * (fraction - 0.5) * 2);
     }
     circle.x = (int) (mMinRadius + mGap * 2 * fraction);
     }
    이 세 가지 방법 은 모두 매우 간단 해서 좌표 의 비례 에 따라 작은 공의 좌표 와 크기 를 계산한다.
    이상 은 전체 Refresh View 의 실현 입 니 다.원본 코드 를 볼 필요 가 있 으 면 문 말 까지 끌 어 올 릴 수 있 습 니 다.
    4 사용 및 효과
    어떻게 사용 하 는 지 보기:
    #MainActivity
    
     @Override
     protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      mRefreshView = findViewById(R.id.refresh_view);
    //  mRefreshView.setOriginState(RefreshView.STATE_PREPARED);
      Button start = findViewById(R.id.start);
      Button stop = findViewById(R.id.stop);
      SeekBar seekBar = findViewById(R.id.seek_bar);
      seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
       @Override
       public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
        mRefreshView.drag(progress / 100f);
       }
    
       @Override
       public void onStartTrackingTouch(SeekBar seekBar) {
    
       }
    
       @Override
       public void onStopTrackingTouch(SeekBar seekBar) {
    
       }
      });
      start.setOnClickListener(this);
      stop.setOnClickListener(this);
     }
    
     @Override
     public void onClick(View v) {
      switch (v.getId()) {
       case R.id.start:
        mRefreshView.start();
        break;
       case R.id.stop:
        mRefreshView.stop();
        break;
      }
     }
    효과 그림:

    녹화 소프트웨어 의 문제 로 녹색 작은 공 은 효과 가 좋 지 않 아 휴대 전화 나 가상 컴퓨터 에 표시 되 는 것 이 정상 적 이다.프로젝트 의 실제 운용 효 과 를 다시 봅 니 다.

    녹화 프로그램 이 녹색 에 알레르기 가 있 는 것 같 으 니 한 번 보 세 요.
    이 글 은 여기 서 끝 났 습 니 다.읽 어 주 셔 서 감사합니다.
    데모 주소:https://github.com/gminibird/RefreshViewTest ( 로 컬 다운로드
    총결산
    이상 은 이 글 의 전체 내용 입 니 다.본 논문 의 내용 이 여러분 의 학습 이나 업무 에 어느 정도 참고 학습 가치 가 있 기 를 바 랍 니 다.궁금 한 점 이 있 으 시 면 댓 글 을 남 겨 주 셔 서 저희 에 대한 지지 에 감 사 드 립 니 다.

    좋은 웹페이지 즐겨찾기