안 드 로 이 드 는 목록 리 셋 을 막 는 실현 방법 이 있 습 니 다.

본 고 는 댐퍼 드 롭 다운 리 셋 목록 의 실현 을 소개 할 것 입 니 다.먼저 효과 미리 보 기 를 살 펴 보 겠 습 니 다.
이것 은 드 롭 다운 상태 입 니 다.

이것 은 손가락 을 내 려 놓 은 후 listView 가 새로 고침 상태 로 굴 러 갈 때의 모습 입 니 다.

1.어떻게 호출 합 니까?
비록 효과 도 는 모양 이 그다지 예 쁘 지 않 지만,주로 그 파란색 배경 이 맞 는 지,괜 찮 습 니 다.이것 은 배경 일 뿐 입 니 다.우리 의 드 롭 다운 리 셋 목록 의 실현 을 알 게 된 후에 당신 은 이 배경 을 쉽게 수정 하여 원 하 는 UI 효 과 를 실현 할 수 있 습 니 다!말 이 많 지 않 습 니 다.이 드 롭 다운 목록 을 어떻게 사용 하 는 지 먼저 말씀 드 리 겠 습 니 다.이것 도 저희 가 코드 를 작성 하여 실현 하고 자 하 는 목표 입 니 다.

    final PullToRefreshListView eListView = (PullToRefreshListView) rootView.findViewById(R.id.profile_listView);
    eListView.setOnLoadCallBack(new PullToRefreshListView.OnLoadCallBack() {
      @Override
      public int whereToLoad() {
        return PullToRefreshListView.DEFAULT_WHERE_TO_LOAD;
      }
      @Override
      public void onLoad() {
        eListView.postDelayed(new Runnable() {
          @Override
          public void run() {
            eListView.setLoadingFinish();
          }
        }, 5000);
      }
      @Override
      public void cancelLoad() {
      }
      @Override
      public Drawable refreshDrawable() {
        return new ColorDrawable(Color.CYAN);
      }
    });
    eListView.setAdapter(new BaseAdapter() {
      @Override
      public int getCount() {
        return 30;
      }
      @Override
      public Object getItem(int position) {
        return null;
      }
      @Override
      public long getItemId(int position) {
        return 0;
      }
      @Override
      public View getView(int position, View convertView, ViewGroup parent) {
        TextView tv;
        if (convertView == null) {
          tv = new TextView(getActivity());
          tv.setGravity(Gravity.CENTER_VERTICAL);
          tv.setHeight(200);
          tv.setBackgroundColor(Color.WHITE);
        } else {
          tv = (TextView) convertView;
        }
        tv.setText(position+"");
        return tv;
      }
    });
상기 코드 에서 PullToRefreshListView 의 사용 은 adapter 에서 ListView 와 같 습 니 다.물론 드 롭 다운 리 셋 기능 을 실현 하기 때문에 데이터 어댑터 를 수정 할 필요 가 없습니다.PullToRefreshListView 의 인 스 턴 스 는 OnLoadCallBack 리 셋 을 설정 해 야 합 니 다.이 리 셋 은 다음 과 같은 네 가지 방법 이 필요 합 니 다.

  /**
   *        
   */
  public interface OnLoadCallBack {
    /**
     *       listView             
     * @return listView   y   ,in dp
     */
    int whereToLoad();
    /**
     *             
     */
    void onLoad();
    /**
     *     
     */
    void cancelLoad();
    /**
     *        
     * @return   drawable
     */
    Drawable refreshDrawable();
  }
where ToLoad 방법 은 PullToRefreshListView 대상 의 드 롭 다운 리 셋 시 어느 위치 에 머 무 르 는 지 알려 줍 니 다.구체 적 으로 말 하면 상기 제2 장 효과 그림 에서 파란색 배경의 높이 입 니 다.onLoad 방법 은 드 롭 다운 새로 고침 의 리 셋 입 니 다.호출 자 는 여기에서 새로 고침 동작 을 수행 할 수 있 습 니 다.cancelLoad 방법 은 새로 고침 동작 을 취소 하 는 것 입 니 다.호출 자 는 여기에서 새로 고침 동작 을 취소 해 야 합 니 다.
상기 방법 에 따 르 면,onLoad 방법 에서 실행 되 는 것 은 하나의 스 레 드 나 AsyncTask 일 것 이 며,cancelLoad 방법 에서 해 야 할 일 은 이 스 레 드 나 AsyncTask 를 취소 하 는 것 이 라 고 추측 할 수 있 습 니 다.마지막 으로 refresh Drawable 방법 이 있 습 니 다.이 방법 은 listView 의 배경 을 수정 하기 위해 호출 자 에 게 제공 되 는 것 입 니 다.호출 자 는 좋아 하 는 배경 Drawable 을 되 돌려 줄 수 있 습 니 다.
어떻게 호출 하 는 지 알 게 된 후에 우 리 는 이 PullToRefreshListView 를 한 걸음 한 걸음 실현 해 야 한다.
2.dispatchDraw 에서 서브 View 를 다시 그 려 드 롭 다운 시각 을 실현 한다.
PullToRefreshListView 가 실현 하 는 관건 은 이 listVIEW 의 하위 View 를 다시 그 리 는 것 이다.View Group 을 다시 그 리 는 하위 View 는 일반적으로 dispatchDraw 방법 에서 이 루어 집 니 다.따라서 우리 의 PullToRefreshListView 는 ListView 류 에서 계승 하여 dispatchDraw 방법 을 다시 불 러 옵 니 다.

  @Override
  protected void dispatchDraw(Canvas canvas) {
    super.dispatchDraw(canvas);
    if (distanceY > 0) {
      if (refreshDrawable == null) {
        refreshDrawable = onLoadCallBack.refreshDrawable();
      }
      if (refreshDrawable == null) {
        canvas.drawColor(Color.GRAY);
      } else {
        int left = getPaddingLeft();
        int top = getPaddingTop();
        refreshDrawable.setBounds(left, top, getWidth()+left, getHeight()+top);
        refreshDrawable.draw(canvas);
      }
      canvas.save();
      canvas.translate(getPaddingLeft(), getPaddingTop() + distanceY);
      for (int i=0;i<getChildCount();i++) {
        View child = getChildAt(i);
        drawChild(canvas, child, getDrawingTime());
      }
      canvas.restore();
    }
  }
서브 뷰 를 다시 그 리 는 관건 은 이 코드 에 있다.

canvas.translate(getPaddingLeft(), getPaddingTop() + distanceY); 
서브 뷰 를 다시 그리 기 전에,우 리 는 먼저 canvas 를 위로 이동 시 켜 distance Y 거 리 를 이동 해 야 한다.왜 그 럴 까요?캔버스 에 서브 뷰 를 그 리 는 방법 을 먼저 살 펴 보 겠 습 니 다.
drawChild 방법의 문 서 는 어떻게 말 합 니까?
protected boolean drawChild (Canvas canvas, View child, long drawingTime)
Added in API level 1 Draw one child of this View Group. This method is responsible for getting the canvas in the right state. This includes clipping, translating so that the child's scrolled origin is at 0, 0, and applying any animation transformations.
Parameters canvas The canvas on which to draw the child child Who to draw drawingTime The time at which draw is occurring Returns True if an invalidate() was issued
draw Child 방법 으로 이 View Group 의 키 View 를 그 릴 수 있 습 니 다.이 방법 은 canvas 를 정확 한 상태 로 만들어 야 합 니 다.이 상 태 는...
canvas 에 대한 클립 재단,translate 평가 작업 등 을 통 해 이 하위 View 를 canvas 의(0,0)위치 에 두 는 것 입 니 다.
무슨 뜻 이 죠?쉽게 말 하면 drawChild 방법 은 child view 를 canvas(0,0)위치 에 그 리 므 로 이 child view 를
canvas 의 정확 한 위 치 는 다시 그리 기 전에 canvas 를 자 르 고 이동 하 는 등 작업 이 필요 합 니 다.예 를 들 어 canvas 하나 와 child view 가 있 습 니 다.
child view 는(0,0)위치 에 그 려 야 하기 때문에 우리 눈 앞 에 보 이 는 child view 는 canvas 의 꼭대기 에 있 습 니 다.그러나 그림 을 그리 기 전에 우 리 는...
canvas 는 100 개의 픽 셀 단 위 를 위로 이동 한 다음 child view 를(0,0)위치 에 그립 니 다.그러면 우리 눈 앞 에 보 이 는 child view 의 위 치 는?
canvas 의(0,100)위치 에 있 습 니 다.
상기 분석 에 의 하면 우 리 는 서브 뷰 를 다시 그 리 는 원 리 는 다음 과 같다 는 것 을 알 수 있다.
PullToRefreshListView 가 상단 까지 굴 러 갔 을 때 미끄럼 제스처 를 모니터링 하여 distance Y 를 계산 하여 canvas 를 위로 얼마나 이동 시 키 고 서브 View 를 다시 그 려 야 하 는 지 확인 하면 PullToRefreshListView 가 미끄럼 제스처 에 따라 아래로 당 기 는 기능 을 실현 할 수 있 습 니 다.
3.드 롭 다운 거리 계산
재 그림 을 실현 한 후에 우리 가 해 야 할 일 은 distance Y 를 어떻게 계산 하 는 것 입 니까?우리 의 초보적인 생각 은 미 끄 러 지 는 거리 에 따라 계산 하고 우리 가 댐퍼 효 과 를 실현 해 야 한 다 는 것 을 고려 하 는 것 이다.즉,미 끄 러 지 는 거리 가 길 어 지면 서 PullToRefreshListView 의 드 롭 다운 거 리 는 점점 짧 아 질 것 이다.PullToRefreshListView 실현 에서 저 는 지수 함 수 를 사용 하여 이 댐퍼 효 과 를 실현 합 니 다.구체 적 으로 계산 하면 다음 과 같 습 니 다.

distanceY = ev.getY() - pullStartY; 
distanceY = (float) (Math.exp(-ev.getY() / pullStartY / 40) * distanceY); 
우 리 는 마이너스 지 수 는 가속도 가 거리 에 따라 작 아 지 는 단조 로 운 증가 함수 라 는 것 을 알 고 있다.나 는 손가락 미끄럼 거 리 를 이용 하여 마이너스 지 수 를 계산 하 는 것 을 PullToRefreshListView 의 미끄럼 거 리 를 참고 기준 으로 하면 댐퍼 드 롭 다운 효 과 를 실현 할 수 있다.
4.모니터 제스처 로 ListView 가 드 롭 다운 상태 에 들 어 갔 는 지 판단 하고 distanceY 업데이트
더 나 아가 우리 가 실현 하고 자 하 는 것 은 제스처 에 대한 감 시 였 다.PullToRefreshListView 에서 우 리 는 onTouchEvent 방법 에서 처리 했다.

@Override 
public boolean onTouchEvent(MotionEvent ev) { 
  if (lastAction == -1 && ev.getActionMasked() == MotionEvent.ACTION_DOWN) { 
    //       
    lastAction = MotionEvent.ACTION_DOWN; 
    cancelAnimating(); 
    L.d(TAG, "touch down"); 
  } else if (lastAction == MotionEvent.ACTION_MOVE && ev.getActionMasked() == MotionEvent.ACTION_UP) { 
    //     ,     
    isPulling = false; 
    lastAction = -1; 
    startAnimating(); 
    L.d(TAG, "touch up"); 
  } else if (lastAction == MotionEvent.ACTION_DOWN) { 
    if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) { 
      //          ,     
      if (isTop && !isPulling) { 
        // listView              ,     
        pullStartY = ev.getY(); 
        lastAction = MotionEvent.ACTION_MOVE; 
        isPulling = true; 
      } 
    } 
  } else if (lastAction == MotionEvent.ACTION_MOVE) { 
    if (isTop) { 
      //    
      distanceY = ev.getY() - pullStartY; 
      L.d(TAG, distanceY + ""); 
      if (distanceY > 0) { 
        distanceY = (float) (Math.exp(-ev.getY() / pullStartY / 40) * distanceY); 
        //            move     ,        
        ev.setAction(MotionEvent.ACTION_DOWN); 
      } else { 
        distanceY = 0; 
        //            listView         ,   move           
        ev.setAction(MotionEvent.ACTION_MOVE); 
      } 
    } else { 
      //           listView listView             ,        ,            
      lastAction = MotionEvent.ACTION_DOWN; 
      isPulling = false; 
      distanceY = 0; 
    } 
  } 
  return super.onTouchEvent(ev); 
} 
이 코드 는 상대 적 으로 좀 복잡 하 니 우 리 는 천천히 해석 합 시다.우선,저 희 는 lastAction 변 수 를 기록 하여 이전 제스처 가 무엇 인지 기록 합 니 다.isPulling 변 수 는 현재 PullToRefreshListView 가 드 롭 다운 상태 에 있 는 지,isTop 변 수 는 현재 PullToRefreshListView 가 위로 굴 러 갔 는 지 기록 합 니 다.
onTouchEvent 방법의 과부하 실현 에서 처음에 PullToRefreshListView 는 어떠한 손짓 도 받 아들 이지 않 았 고 사용자 가 손가락 을 누 르 고 출발 할 때 ACTIONDOWN 이벤트 때,나 는 이 동작 을 기록 하고 사용자 가 미 끄 러 질 때,이때 PullToRefreshListView 가"위로 스크롤"하지 않 으 면 아무런 처리 도 하지 않 고,반대로 lastAction 을 ACTION 으로 업데이트 합 니 다.MOVE 상태 에서 isPulling 변 수 를 업데이트 하고 현재 손가락 의 위 치 를 드 롭 다운 거 리 를 계산 하 는 시작 위치 로 기록 하 며 드 롭 다운 새로 고침 을 시작 한 다음 드 롭 다운 과정 에서 PullToRefreshListView 드 롭 다운 거 리 를 계산 하여 하위 View 를 다시 그립 니 다.
이 제스처 처리 가 실 현 될 때 사용자 가 드 롭 다운 과정 에서 갑자기 PullToRefreshListView 를 위로 당 깁 니 다.PullToRefreshListView 를'위로 굴 러 가 는 상태'가 아 닌 상태 로 끌 어 올 리 면 드 롭 다운 상 태 를 초기 화 합 니 다.

lastAction = MotionEvent.ACTION_DOWN; 
그래서 PullToRefreshListView 의 다음 하강 제스처 응답 권 은 시스템 에 넘 겨 졌 고 사용자 가 PullToRefreshListView 를'위로 스크롤'상태 로 끌 어 내 린 것 을 알 고 상기 조작 을 다시 실행 하여 PullToRefreshListView 를 드 롭 다운 상태 로 들 어 갔다.
5.ListView 가 맨 위로 굴 러 갔 는 지 어떻게 판단 합 니까?
다음 단 계 는 ListView 가'위로 스크롤'상태 에 있 는 지 어떻게 판단 합 니까?이 문 제 는 PullToRefreshListView 의 onScroll 에서 해결 하 겠 습 니 다.

    setOnScrollListener(new OnScrollListener() {
      @Override
      public void onScrollStateChanged(AbsListView view, int scrollState) {
      }
      @Override
      public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        //    view   (    ,         view),    listView     
        if (getChildCount() == 0) {
          isTop = true;
          return;
        }
        if (firstVisibleItem == 0) {
          View firstView = getChildAt(0);
          if (firstView.getTop() + distanceY >= 0) {
            //    view      parent( listView)         0,    listView       
            isTop = true;
            return;
          }
        }
        isTop = false;
      }
    });
PullToRefreshListView 에 OnScrollListener 리 셋 을 설정 하고 onScroll 방법 에서 스크롤 위 치 를 모니터링 합 니 다.구체 적 으로 주석 을 보면 한눈 에 알 수 있 습 니 다.저 는 더 이상 설명 하지 않 겠 습 니 다.
6.드 롭 다운 후 스크롤 백 애니메이션
마지막 으로 손가락 을 떼 고 끝 낼 때 PullToRefreshListView 를 위해 스크롤 백 애니메이션 을 실행 해 야 합 니 다.onTouchEvent 방법 에서 보 았 습 니 다.

    // ......
    else if (lastAction == MotionEvent.ACTION_MOVE && ev.getActionMasked() == MotionEvent.ACTION_UP) {
      //     ,    
      isPulling = false;
      lastAction = -1;
      startAnimating();
      L.d(TAG, "touch up");
    }
    // ...... 
startAnimating 방법의 실현 은 다음 과 같다.

  /**
   *                   
   */
  private void startAnimating() {
    int whereToLoad = dp2px(onLoadCallBack.whereToLoad());
    final boolean toLoad;
    if (distanceY <= whereToLoad) {
      pullCancelAnimator = ValueAnimator.ofFloat(distanceY, 0);
      toLoad = false;
    } else {
      pullCancelAnimator = ValueAnimator.ofFloat(distanceY, whereToLoad);
      toLoad = true;
    }
    pullCancelAnimator.setDuration((long) (DEFAULT_BASE_ANIMATING_TIME_PER_100DP*px2dp(distanceY)/100));
    pullCancelAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
    pullCancelAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
      @Override
      public void onAnimationUpdate(ValueAnimator animation) {
        distanceY = (float) animation.getAnimatedValue();
        ViewCompat.postInvalidateOnAnimation(PullToRefreshListView.this);
      }
    });
    pullCancelAnimator.addListener(new Animator.AnimatorListener() {
      @Override
      public void onAnimationStart(Animator animation) {
      }
      @Override
      public void onAnimationEnd(Animator animation) {
        post(new Runnable() {
          @Override
          public void run() {
            pullCancelAnimator = null;
            if (toLoad) {
              onLoadCallBack.onLoad();
            }
          }
        });
      }
      @Override
      public void onAnimationCancel(Animator animation) {
        post(new Runnable() {
          @Override
          public void run() {
            pullCancelAnimator = null;
            if (toLoad) {
              onLoadCallBack.cancelLoad();
            }
          }
        });
      }
      @Override
      public void onAnimationRepeat(Animator animation) {
      }
    });
    pullCancelAnimator.start();
  }
저 는 ValueAnimator 를 사용 하여 이 스크롤 백 애니메이션 을 실 현 했 습 니 다.그 중에서 ValueAnimator 가 설정 한 리 셋 에서 애니메이션 업데이트 와 애니메이션 종료,애니메이션 취소 에서 OnLoadCallBack 의 3 곡 리 셋 방법 을 각각 호출 하여 PullToRefreshListView 의 드 롭 다운 리 셋 동작 을 실 현 했 습 니 다.onLoad 방법 은 UI 스 레 드 에서 실 행 된 것 을 볼 수 있 습 니 다.따라서 onLoad 방법 에서 시간 이 걸 리 면 배경 스 레 드 에서 작업 해 야 합 니 다.이것 은 우리 앞의 해석 과 대응 합 니 다.
7.개선 과 문제점
(1)우 리 는 onLoad 리 셋 을 비동기 작업 대상 으로 되 돌아 가 는 방법 으로 수정 한 다음 에 PullToRefreshListView 를 드 롭 다운 이 끝 난 후에 이 비동기 작업 을 수행 할 수 있 습 니 다.따라서 우 리 는 cancelLoading 리 셋 이 필요 없 이 PullToRefreshListView 내부 에서 취소 작업 을 할 수 있 습 니 다.이렇게 하면 패키지 성 을 강화 할 수 있 습 니 다.그러나 현재 의 방법 에 비해 자유 도 는 그리 높 지 않다.
(2)스크롤 애니메이션 도 최적화 할 수 있 을 것 같 아 요.구체 적 으로 어떻게 최적화 해 야 할 지 저도 잘 모 르 겠 어 요.여러분,좋 은 생각 이 있 으 시 면 댓 글 창 에서 제안 해 주세요.감사합니다.
(3)드 롭 다운 할 때 멀 티 터치 에 대한 응답 이 완벽 하지 않 습 니 다.받 아들 일 수 있 지만 qq 클 라 이언 트 의 채 팅 목록 처럼 되 지 않 습 니 다.
8.소스 코드
이로써 저 는 드 롭 다운 리 셋 목록 을 어떻게 실현 하 는 지 분 석 했 습 니 다.PullToRefreshListView 의 소스 코드 는 다음 과 같 습 니 다.

import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.AbsListView;
import android.widget.ListView;
import com.ivan.healthcare.healthcare_android.log.L;
/**
 *         listView
 * Created by Ivan on 16/2/14.
 */
public class PullToRefreshListView extends ListView {
  private final String TAG = "PullToRefreshListView";
  private final int DEFAULT_BASE_ANIMATING_TIME_PER_100DP = 150;
  public static final int DEFAULT_WHERE_TO_LOAD = 80;
  private int lastAction = -1;
  private float pullStartY = -1;
  private boolean isTop = true;
  private float distanceY = 0;
  private boolean isPulling = false;
  private ValueAnimator pullCancelAnimator;
  private Context context;
  private Drawable refreshDrawable;
  private OnLoadCallBack onLoadCallBack = new OnLoadCallBack() {
    @Override
    public int whereToLoad() {
      return DEFAULT_WHERE_TO_LOAD;
    }
    @Override
    public void onLoad() {
    }
    @Override
    public void cancelLoad() {
    }
    @Override
    public Drawable refreshDrawable() {
      return null;
    }
  };
  public PullToRefreshListView(Context context) {
    super(context);
    initView(context);
  }
  public PullToRefreshListView(Context context, AttributeSet attrs) {
    super(context, attrs);
    initView(context);
  }
  private void initView(Context context) {
    this.context = context;
    setOnScrollListener(new OnScrollListener() {
      @Override
      public void onScrollStateChanged(AbsListView view, int scrollState) {
      }
      @Override
      public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        //    view   (    ,         view),    listView     
        if (getChildCount() == 0) {
          isTop = true;
          return;
        }
        if (firstVisibleItem == 0) {
          View firstView = getChildAt(0);
          if (firstView.getTop() + distanceY >= 0) {
            //    view      parent( listView)         0,    listView       
            isTop = true;
            return;
          }
        }
        isTop = false;
      }
    });
  }
  @Override
  public boolean onTouchEvent(MotionEvent ev) {
    if (lastAction == -1 && ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
      //      
      lastAction = MotionEvent.ACTION_DOWN;
      cancelAnimating();
      L.d(TAG, "touch down");
    } else if (lastAction == MotionEvent.ACTION_MOVE && ev.getActionMasked() == MotionEvent.ACTION_UP) {
      //     ,    
      isPulling = false;
      lastAction = -1;
      startAnimating();
      L.d(TAG, "touch up");
    } else if (lastAction == MotionEvent.ACTION_DOWN) {
      if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) {
        //          ,    
        if (isTop && !isPulling) {
          // listView              ,    
          pullStartY = ev.getY();
          lastAction = MotionEvent.ACTION_MOVE;
          isPulling = true;
        }
      }
    } else if (lastAction == MotionEvent.ACTION_MOVE) {
      if (isTop) {
        //   
        distanceY = ev.getY() - pullStartY;
        L.d(TAG, distanceY + "");
        if (distanceY > 0) {
          distanceY = (float) (Math.exp(-ev.getY() / pullStartY / 40) * distanceY);
          //            move     ,       
          ev.setAction(MotionEvent.ACTION_DOWN);
        } else {
          distanceY = 0;
          //            listView         ,   move          
          ev.setAction(MotionEvent.ACTION_MOVE);
        }
      } else {
        //           listView listView             ,        ,           
        lastAction = MotionEvent.ACTION_DOWN;
        isPulling = false;
        distanceY = 0;
      }
    }
    return super.onTouchEvent(ev);
  }
  @Override
  protected void dispatchDraw(Canvas canvas) {
    super.dispatchDraw(canvas);
    if (distanceY > 0) {
      if (refreshDrawable == null) {
        refreshDrawable = onLoadCallBack.refreshDrawable();
      }
      if (refreshDrawable == null) {
        canvas.drawColor(Color.GRAY);
      } else {
        int left = getPaddingLeft();
        int top = getPaddingTop();
        refreshDrawable.setBounds(left, top, getWidth()+left, getHeight()+top);
        refreshDrawable.draw(canvas);
      }
      canvas.save();
      canvas.translate(getPaddingLeft(), getPaddingTop() + distanceY);
      for (int i=0;i<getChildCount();i++) {
        View child = getChildAt(i);
        drawChild(canvas, child, getDrawingTime());
      }
      canvas.restore();
    }
  }
  /**
   *                   
   */
  private void startAnimating() {
    int whereToLoad = dp2px(onLoadCallBack.whereToLoad());
    final boolean toLoad;
    if (distanceY <= whereToLoad) {
      pullCancelAnimator = ValueAnimator.ofFloat(distanceY, 0);
      toLoad = false;
    } else {
      pullCancelAnimator = ValueAnimator.ofFloat(distanceY, whereToLoad);
      toLoad = true;
    }
    pullCancelAnimator.setDuration((long) (DEFAULT_BASE_ANIMATING_TIME_PER_100DP*px2dp(distanceY)/100));
    pullCancelAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
    pullCancelAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
      @Override
      public void onAnimationUpdate(ValueAnimator animation) {
        distanceY = (float) animation.getAnimatedValue();
        ViewCompat.postInvalidateOnAnimation(PullToRefreshListView.this);
      }
    });
    pullCancelAnimator.addListener(new Animator.AnimatorListener() {
      @Override
      public void onAnimationStart(Animator animation) {
      }
      @Override
      public void onAnimationEnd(Animator animation) {
        post(new Runnable() {
          @Override
          public void run() {
            pullCancelAnimator = null;
            if (toLoad) {
              onLoadCallBack.onLoad();
            }
          }
        });
      }
      @Override
      public void onAnimationCancel(Animator animation) {
        post(new Runnable() {
          @Override
          public void run() {
            pullCancelAnimator = null;
            if (toLoad) {
              onLoadCallBack.cancelLoad();
            }
          }
        });
      }
      @Override
      public void onAnimationRepeat(Animator animation) {
      }
    });
    pullCancelAnimator.start();
  }
  private void cancelAnimating() {
    if (pullCancelAnimator != null) {
      pullCancelAnimator.cancel();
    }
  }
  private float px2dp(float pxvalue) {
    return (pxvalue - 0.5f) /context.getResources().getDisplayMetrics().density;
  }
  private int dp2px(float dpvalue) {
    return (int) (dpvalue * context.getResources().getDisplayMetrics().density + 0.5f);
  }
  /**
   *        
   */
  public interface OnLoadCallBack {
    /**
     *       listView             
     * @return listView   y   ,in dp
     */
    int whereToLoad();
    /**
     *             
     */
    void onLoad();
    /**
     *     
     */
    void cancelLoad();
    /**
     *        
     * @return   drawable
     */
    Drawable refreshDrawable();
  }
  /**
   *         
   * @param cb   
   */
  public void setOnLoadCallBack(OnLoadCallBack cb) {
    this.onLoadCallBack = cb;
  }
  /**
   *                 ,  listView     
   */
  public void setLoadingFinish() {
    startAnimating();
  }
}
이상 의 안 드 로 이 드 는 드 롭 다운 목록 을 리 셋 하 는 실현 방법 이 있 습 니 다.바로 작은 편집 이 여러분 에 게 공유 하 는 모든 내용 입 니 다.여러분 께 참고 가 되 고 저희 도 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

좋은 웹페이지 즐겨찾기