Android QQ 목록 슬라이딩 삭제 동작

이 짝 퉁 의 새로운 QQ 목록 이 미 끄 러 지 며 삭 제 됩 니 다.전편 에 QQ 의 미 끄 러 짐 삭제 라 고 언급 되 어 있 습 니 다.추측 원 리 는 ListView 자체 의 모든 item 에 하나의 Button 이 존재 하 는 것 입 니 다.일반적인 상태 에서 숨겨 진 것 입 니 다.왼쪽으로 미 끄 러 지 는 사건 이 감지 되 었 을 때 숨겨 진 Button 을 팝 업 하지만 Button 상 태 를 전환 할 때 Button 에 게 나타 나 고 숨겨 진 애니메이션 을 줍 니 다.다음은 이 ListView 를 실현 합 니 다. 
우선 어 려 운 점 은 ListView 를 통 해 특정한 item 의 View 를 얻 는 것 입 니 다.ViewGroup 에 대해 서 는 getChildAt()방법 으로 대응 하 는 하위 view 를 직접 호출 할 수 있 습 니 다.그러나 ListView 에서 getChildAt()을 직접 사용 하면 ListView 가 미 끄 러 지면 빈 지침 이 이상 하 다 는 것 을 알 수 있 습 니 다.ListView 에 대해 서 는 getChildAt()방법 을 직접 사용 하 는 것 이 통 하지 않 는 것 이 분명 합 니 다.ListView 는 뷰 그룹 이지 만이미 누군가가 이 문제 와 해결 방법 을 설명 했다.아마도 ListView 는 많은 item 이 있 는 것 처럼 보이 지만 이것 은 보기 일 뿐이다.실제로 ListView 는 네가 볼 수 있 는 것 만 구성 했다.바로 화면 에 볼 수 있 는 그렇게 많은 item 의 view 이기 때문에 ListView 의 특정한 위치 position 의 item 의 view 를 가 져 가 야 한다.다음 코드 를 사용 해 야 합 니 다:

int firstVisiblePos = getFirstVisiblePosition() - getHeaderViewsCount();
int factPos = curPos - firstVisiblePos;
 mItemView = getChildAt(factPos);
바로 ListView 에서 현재 첫 번 째 로 볼 수 있 는 item 의 firstVisiblePos 를 가 져 오 는 것 입 니 다.물론 header view 의 수 를 빼 고 가 져 오고 싶 은 item 의 curPos 에서 firstVisiblePos 를 빼 면 해당 하 는 item 이 실제 ListView 의 위치 에 있 는 factPos 입 니 다.이제 빈 지침 이상 을 알 리 지 않 을 겁 니 다.
특정한 위 치 를 가 져 오 는 item 의 view 를 알 게 되 었 습 니 다.이 제 는 미끄럼 사건 을 감지 하여 현재 ListView 와 어떤 position 의 item 과 상호작용 을 하고 있 는 지 판단 해 야 합 니 다.ListView 에서 다음 과 같은 방법 을 사용 합 니 다.
int curPos = pointToPosition((int)curX, (int)curY);       
다음은 ListView 의 touch 사건 을 캡 처 한 것 입 니 다.SlidingDeleteListView 를 사용자 정의 하고 ListView 에서 계승 하여 onTouch Event()방법 을 다시 쓰 는 것 입 니 다.
 

  @Override
  public boolean onTouchEvent(MotionEvent event) {
    if(!mEnableSliding)
      return false;

    if(mCancelMotionEvent && event.getAction() == MotionEvent.ACTION_MOVE) {
      return true;
    } else if(mCancelMotionEvent && event.getAction() == MotionEvent.ACTION_DOWN) {
      event.setAction(MotionEvent.ACTION_CANCEL);
    }

    switch(event.getAction()) {
      case MotionEvent.ACTION_DOWN: {
        if(mTracker == null)
          mTracker = VelocityTracker.obtain();
        else
          mTracker.clear();

        mLastMotionX = event.getX();
        mLastMotionY = event.getY();
      }break;

      case MotionEvent.ACTION_MOVE: {
        mTracker.addMovement(event);
        mTracker.computeCurrentVelocity(1000);
        int curVelocityX = (int) mTracker.getXVelocity();

        float curX = event.getX();
        float curY = event.getY();
        int lastPos = pointToPosition(
            (int)mLastMotionX, (int)mLastMotionY);
        int curPos = pointToPosition((int)curX, (int)curY);
        int distanceX = (int)(mLastMotionX - curX);
        if(lastPos == curPos && (distanceX >= MAX_DISTANCE || curVelocityX < -MAX_FLING_VELOCITY)) {
          int firstVisiblePos = getFirstVisiblePosition() - getHeaderViewsCount();
          int factPos = curPos - firstVisiblePos;
          mItemView = getChildAt(factPos);
          if(mItemView != null) {
            if(mButtonID == -1)
              throw new IllegalButtonIDException("Illegal DeleteButton resource id,"
                  + "ensure excute the function setButtonID(int id)");

            mButton = mItemView.findViewById(mButtonID);
            mButton.setVisibility(View.VISIBLE);
            mButton.startAnimation(mShowAnim);

            mLastButtonShowingPos = curPos;
            mButton.setOnClickListener(new View.OnClickListener() {

              @Override
              public void onClick(View v) {
                if(mDeleteItemListener != null)
                  mDeleteItemListener.onButtonClick(v, mLastButtonShowingPos);
                mButton.setVisibility(View.GONE);

                mLastButtonShowingPos = -1;
              }
            });

            mCancelMotionEvent = true;
          }
        }
      }break;

      case MotionEvent.ACTION_UP: {
        if(mTracker != null) {
          mTracker.clear();
          mTracker.recycle();
          mTracker = null;
        }

        mCancelMotionEvent = false;

        if(mLastButtonShowingPos != -1) {
          event.setAction(MotionEvent.ACTION_CANCEL);
        }
      }break;

      case MotionEvent.ACTION_CANCEL: {
        hideShowingButtonWithAnim();
      }break;
    }

    return super.onTouchEvent(event);
  }

위의 코드 를 설명 하기 전에 안 드 로 이 드 의 touch 사건 의 배포 원 리 를 간단하게 말씀 드 리 겠 습 니 다.주로 MotionEvent.ACTION 입 니 다.DOWN 이 사건 은 가장 중요 합 니 다.사건 의 배 포 는 한 번 에 두 부분 이 있 습 니 다.'자'는 ViewGroup 이 시스템 에 전달 되 는 ACTION 을 가 져 오 는 것 을 말 합 니 다.DOWN 이벤트,ViewGroup 의 onInterceptTouchEvent()방법 을 먼저 호출 합 니 다.이 방법 은 이 사건 ViewGroup 이 캡 처 하고 싶 은 지,true 로 돌아 가면 ACTIONDOWN 이벤트 가 View Group 에 배 포 된 onTouchEvent()방법 으로 처리 되 었 습 니 다.이 사건 은 부모 view 에 의 해 캡 처 되 었 고 하위 view 는 더 이상 이 벤트 를 가 져 오지 않 을 것 임 을 표시 합 니 다.ViewGroup 의 onInterceptTouchEvent()방법 이 false 로 되 돌아 오 면 ViewGroup 이 이 사건 을 캡 처 하지 않 고 다음 사건 이 발생 하 는 위치 에 하위 view 가 존재 한다 면 ViewGroup 은 이 ACTION 을DOWN 사건 은 이 하위 view 에 전달 되 어 처 리 됩 니 다.이 과정 은 사건 의 배포 과정 이 고 그 다음은'회'이다.회'이 과정 은 사건 의 소모 과정 이다.하위 view 의 onTouch Event()방법 은 true 로 돌아 가면 이 ACTION 을 나타 낸다.DOWN 이벤트 가 이 하위 view 에 소모 되면 ViewGroup 은 onTouchEvent()방법 으로 이 사건 을 받 아들 이지 않 습 니 다.이 사건 이 소모 되 었 기 때 문 입 니 다.하위 view 의 onTouchEvent()방법 이 false 로 돌아 오 면 하위 view 가 이 ACTION 을 소모 하지 않 음 을 표시 합 니 다.DOWN 이벤트(물론,하위 view 는 이 사건 을 처리 할 수 있 지만 false 로 돌아 가면 사건 을 View Group 에 던 져 주 고 많은 일 을 할 수 있 습 니 다)이후 사건 은 부모 view 에 게 돌아 갑 니 다.최종 MotionEvent.ACTIONDOWN 이벤트 가 어느 층 의 view 에 소모 되 었 는 지,다음 의 후속 touch 이벤트,예 를 들 어 ACTIONUP、ACTION_MOVE、ACTION_CANCEL 등 사건 은 소모 ACTION 에 게 직접 전 달 됩 니 다.DOWN 이벤트 의 view,다른 층 의 view 는 다음 ACTION 까지 후속 사건 을 받 지 않 습 니 다.DOWN 사건. 
이상 의 코드 는 switch 의 코드 블록 에 잠시 관심 을 가 집 니 다.MotionEvent.ACTION 이 검출 되 었 습 니 다.DOWN 이벤트 때 현재 touch 이벤트 의 위 치 를 기록 하 는 동시에 mTracker 를 먼저 가 져 옵 니 다.이것 은 Velocity Tracker 대상 입 니 다.안 드 로 이 드 가 제공 하 는 현재 미끄럼 이벤트 의 속 도 를 계산 하 는 데 사 용 됩 니 다.Motion Event.ACTION 감지MOVE 이벤트,우 리 는 두 가지 상황 에서 처리 해 야 합 니 다.한 가지 상황 은 사용자 가 일정한 거 리 를 미 끄 러 지면 button 을 팝 업 하 는 것 입 니 다.이 거 리 는 현재 미 끄 러 진 위치 와 이번 ACTION 입 니 다.DOWN 이 기록 한 이벤트 위치의 거 리 는 두 번 째 상황 은 사용자 가 미끄럼 속도 가 한 한도 값 을 초과 할 때 button 을 팝 업 하 는 것 입 니 다.이 속도 의 계산 은 앞에서 언급 한 mTracker 를 사용 하 는 것 입 니 다.용법 은 매우 간단 합 니 다.ACTION 검출UP 사건 은 현재 의 이번 상호작용 이 완성 되 었 음 을 나타 내 고 우 리 는 청 소 를 할 수 있 습 니 다.에 대해 ACTIONCANCEL 사건,이 건 잠시 여 기 를 살 게 요.이 건 대 들 보 를 훔 쳐 기둥 을 바 꾸 는 작은 기술 로 시스템 을 괴 롭 히 세 요~ 
위의 ACTIONMOVE 이벤트 에서 이벤트 가 처리 되 고 button 이 뜨 면 다음 에 ACTION 이 검출 됩 니 다.DOWN 이벤트,이 이벤트 가 발생 한 위치 가 button 의 영역 에 없 으 면 사용자 가 팝 업 button 을 누 르 지 않 았 음 을 표시 합 니 다.이 button 을 제거 해 야 합 니 다.즉,이 button 을 숨 겨 야 합 니 다.여기 에는 앞서 언급 한 View Group 의 onInterceptTouchEvent()방법 을 사용 해 야 합 니 다.이번 ACTIONDOWN 이벤트 가 하위 view 에 전달 되 기 전에 캡 처 되 었 습 니 다.물론 이번 사건 이 button 을 클릭 한 사건 인지 아 닌 지 판단 하 십시오. 
private boolean isClickButton(MotionEvent ev) {

    mButton.getLocationOnScreen(mShowingButtonLocation);

    int left = mShowingButtonLocation[0];
    int right = mShowingButtonLocation[0] + mButton.getWidth();
    int top = mShowingButtonLocation[1];
    int bottom = mShowingButtonLocation[1] + mButton.getHeight();

    return (ev.getRawX() >= left
        && ev.getRawX() <= right
        && ev.getRawY() >= top
        && ev.getRawY() <= bottom);
  }         onInterceptTouchEvent()  : 

  @Override
  public boolean onInterceptTouchEvent(MotionEvent ev) {
    if(mEnableSliding && mLastButtonShowingPos != -1 &&
        ev.getAction() == MotionEvent.ACTION_DOWN && !isClickButton(ev)) {
      ev.setAction(MotionEvent.ACTION_CANCEL);
      mCancelMotionEvent = true;

      return true;
    }

    return super.onInterceptTouchEvent(ev);
  };  
ACTION 을 캡 처 할 지 말 지 판단 하기DOWN 이벤트,현재 button 이 팝 업 되 었 는 지 먼저 판단 합 니 다.button 이 팝 업 될 때마다 현재 팝 업 된 item 의 위치 mLastButton ShowingPos 를 기록 하기 때 문 입 니 다.그리고 지금 이 ACTION 인지 아 닌 지...DOWN 이벤트;팝 업 단추 누 를 지 여부 입 니 다.모든 조건 이 부합 되면 우 리 는 이 ACTION 을 압수 할 것 이다.DOWN 이벤트,onInterceptTouchEvent()방법 return true.이렇게 하면 ACTIONDOWN 사건 은 이 SlidingDeleteListView 의 onTouchEvent()방법 으로 전 달 됩 니 다.여기 서 앞의 ACTION 을 설명 하 겠 습 니 다.CANCEL 사건,onTouchEvent()방법 에서 ACTION 으로 판 단 됩 니 다.DOWN,그리고 앞 에 onInterceptTouchEvent()에 표 시 된 mCancel MotionEvent 입 니 다.이 표 시 는 ACTION 을 캡 처 했 음 을 표시 합 니 다.DOWN 이벤트,이 ACTION 을 특수 처리 해 야 합 니 다.DOWN 이벤트,그리고 onTouchEvent()방법 에서 이번 ACTION 을 어떻게 처리 하 는 지 보 세 요.다운 사건 은? 

else if(mCancelMotionEvent && event.getAction() == MotionEvent.ACTION_DOWN) {
      event.setAction(MotionEvent.ACTION_CANCEL);
    } 
네,대 들 보 를 훔 쳐 서 기둥 을 바 꾸 고 현재 의 ACTIONDOWN 이벤트 가 ACTION 으로 바 뀌 었 습 니 다.CANCEL 사건,ACTION 에서CANCEL 사건 의 처 리 는 gone 이 현재 팝 업 된 button 을 제거 하 는 것 입 니 다.그러면 두 가지 상황 에서 ACTIONDOWN 이 구분 해서 추가 처 리 를 했 습 니 다.
동시에 우 리 는 ACTION 에서UP 이벤트 에서 판단 이 있 습 니 다.현재 mLastButton ShowingPos 가-1 이 아니라면 이번 에는 사용자 가 팝 업 button 을 미 끄 러 뜨리 는 작업 임 을 나타 냅 니 다.이번 touch 사건 은 저희 가 처 리 했 습 니 다.그러면 저 희 는 이번 ACTION 을UP 이 벤트 는 ListView 자체 의 기본 슈퍼.onTouchEvent()논리 로 처리 되 었 습 니 다.앞의 ACTION 때 문 입 니 다.DOWN 및 ACTIONMOVE 우 리 는 모두 기본 절 차 를 밟 고 있 습 니 다.그럼 지금 ListView 의 원래 논 리 는 ACTION 을 기다 리 고 있 습 니 다.UP 이벤트 발송,이렇게 하면 ListView 자체 의 OnItemClick 또는 OnItemLongClick 사건 의 촉발 입 니 다.생각해 보 세 요.만약 에 우리 가 숨겨 진 button 을 팝 업 했 는데 ListView 가 OnItemClick 이나 OnItemLongClick 을 처리 하면 적당 하지 않 을 것 입 니 다.그래서 여기 서 우 리 는 시스템 을 조금 속 이 고 원래 의 ACTION 을UP 를 ACTION 으로 교체CANCEL,이렇게 button 의 팝 업 을 처리 한 후에 ListView 원래 의 OnItemClick 이나 OnItemLongClick 사건 을 처리 하지 않 습 니 다.

 if(mLastButtonShowingPos != -1) { 
        event.setAction(MotionEvent.ACTION_CANCEL); 
      }  
마지막 으로 onTouchEvent()방법 을 다시 쓰 면 이 사용자 정의 ListView 의 onItemClick()과 onItemLongClick()방법 에 영향 을 미 치지 않 을 까요?정 답 은 이 방안 이 아 닙 니 다.onTouchEvent()방법 은 캡 처 되 지 않 은 사건 에 대해 슈퍼.onTouchEvent(ev)로 돌아 가기 때문에 미끄럼 사건 의 검 측 도 처 리 했 습 니 다.시스템 이 이번 사건 에 대한 처리 절 차 를 방 해 했 는 지,캡 처 한 사건 은 사건 의 완전한 생명 주 기 를 주 었 습 니 다.CANCEL 이벤트 로 touch 의 상호작용 을 끝 냅 니 다)여기 서 생명 주 기 를 말씀 드 리 겠 습 니 다.ACTION 으로DOWN 이벤트 시작,ACTIONUP 또는 ACTIONCANCEL 사건 이 끝나 고 중간 에 일련의 ACTION 이 섞 여 있 습 니 다.MOVE 사건.제 최초의 방안 은 ListView.setOnTouch Listener()를 사용 하고 이 Touch Listener 의 onTouch()방법 을 실현 하 는 것 입 니 다.이렇게 사건 을 처리 하 는 것 은 약간 복잡 합 니 다.이 컨트롤 의 처리 논 리 는 ACTION 에 있 기 때 문 입 니 다.MOVE 에서 button 이 나 오 면 모든 후속 ACTIONMOVE 이벤트 무효 화,무효 화 되 지 않 으 면 후속 ACTIONMOVE 이벤트 ListView 는 여전히 받 을 수 있 습 니 다.사용 자 는 ListView 를 위아래 로 끌 수 있 습 니 다.ListView 의 item 이 몇 개의 공 통 된 view 를 다시 사용 하 는 친구 라 는 것 을 알 면 다음 에 무슨 bug 가 나 올 지 생각 할 수 있 습 니 다.바로 button 이 꺼 지지 않 았 던 item 이 화면 에 나타 나 면 button 이 나타 납 니 다.이 item 은 사라 진 item 의 view 를 다시 사용 하기 때 문 입 니 다.그럼 제 가 OnTouch Listener.onTouch()방법 을 사용 할 때 button 이 뜨 자마자 true 로 되 돌아 와 서 이 사건 이 OnTouch Listener 에 의 해 처리 되 었 음 을 나타 내 는데 여기 가 문제 가 생 겼 습 니 다.앞의 ACTION 때 문 입 니 다.DOWN 이 벤트 는 false 로 되 돌아 갑 니 다.touch 의 상호작용 을 나타 내 는 초기 이 벤트 는 ListView 의 기본 onTouchEvent()논리 적 으로 처리 되 었 습 니 다.(false 로 돌아 가 야 합 니 다.그렇지 않 으 면 모든 이벤트 가 OnTouchListener 와 먹 혔 습 니 다)기본 onTouchEvent()에서 이번 ACTION 을 어떻게 처 리 했 는 지 모 르 기 때 문 입 니 다.DOWN,보통 ListView 이지 만 이번 ACTION 소모DOWN,OnItemClick 또는 OnItemLongClick 사건 의 처 리 를 시작 합 니 다.이것 은 아 이 템 의 클릭 사건 이 모두 ListView 의 onTouch Event()에 의 해 처리 되 기 때 문 입 니 다.ACTIONDOWN 은 ListView 자체 의 onTouchEvent()에 소모 되 었 으 나 후속 ACTIONMOVE 심지어 ACTIONUP 이벤트 가 OnTouchListener 에 의 해 소모 되면 기본 적 인 onTouchEvent()에 전달 되 지 않 고 처리 할 수 없습니다.원래 완전한 touch 수명 주기 가 딱딱 하 게 두 부분 으로 자 르 여 두 곳 에서 처리 되 었 습 니 다.그러면 큰 추진 문 제 를 초래 할 수 있 습 니 다.가장 눈 에 띄 는 것 은 ListView 자체 의 OnitemClickListener 등 사건 을 처리 하 는 모니터 가 미끄럼 사건 을 처리 하 는 코드 와 충돌 하 는 것 입 니 다.미 끄 러 진 후에 button 이 튀 어 나 온 것 같 습 니 다.현재 미끄럼 사건 을 처리 하 는 item 은 하 이 라이트 선택 상태(android 에서 pressed 로 표시)입 니 다.손가락 이 화면 을 떠 났 더 라 도.마지막 으로 채택 한 방안 은 사건 처리 의 논 리 를 유지 하 는 것 이다.한 방법 안에 시스템 사건 의 정상 적 인 분배 운행 을 할 수 있 을 뿐만 아니 라 그 자체 도 미끄럼 사건 을 처리 할 수 있다.
마지막 코드 는 나의 github 에 제출 되 었 다https://github.com/YoungLeeForeverBoy/SlidingDeleteListView 
다음은 이 컨트롤 의 전시 입 니 다:

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

좋은 웹페이지 즐겨찾기