private void setHeaderPadding(int padding) {
    headerPadding = padding;

    MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) header.getLayoutParams();
    mlp.setMargins(0, Math.round(padding), 0, 0);
    header.setLayoutParams(mlp);
  }
  private void init() {
    setVerticalFadingEdgeEnabled(false);

    headerContainer =
        (LinearLayout) LayoutInflater.from(getContext()).inflate(R.layout.ptr_header, null);
    header = (RelativeLayout) headerContainer.findViewById(R.id.ptr_id_header);
    text = (TextView) header.findViewById(R.id.ptr_id_text);
    lastUpdatedTextView = (TextView) header.findViewById(R.id.ptr_id_last_updated);
    image = (ImageView) header.findViewById(R.id.ptr_id_image);
    spinner = (ProgressBar) header.findViewById(R.id.ptr_id_spinner);

    pullToRefreshText = getContext().getString(R.string.ptr_pull_to_refresh);
    releaseToRefreshText = getContext().getString(R.string.ptr_release_to_refresh);
    refreshingText = getContext().getString(R.string.ptr_refreshing);
    lastUpdatedText = getContext().getString(R.string.ptr_last_updated);

    flipAnimation =
        new RotateAnimation(
            0,
            -180,
            RotateAnimation.RELATIVE_TO_SELF,
            0.5f,
            RotateAnimation.RELATIVE_TO_SELF,
            0.5f);
    flipAnimation.setInterpolator(new LinearInterpolator());
    flipAnimation.setDuration(ROTATE_ARROW_ANIMATION_DURATION);
    flipAnimation.setFillAfter(true);

    reverseFlipAnimation =
        new RotateAnimation(
            -180,
            0,
            RotateAnimation.RELATIVE_TO_SELF,
            0.5f,
            RotateAnimation.RELATIVE_TO_SELF,
            0.5f);
    reverseFlipAnimation.setInterpolator(new LinearInterpolator());
    reverseFlipAnimation.setDuration(ROTATE_ARROW_ANIMATION_DURATION);
    reverseFlipAnimation.setFillAfter(true);

    addHeaderView(headerContainer);
    setState(State.PULL_TO_REFRESH);
    scrollbarEnabled = isVerticalScrollBarEnabled();

    ViewTreeObserver vto = header.getViewTreeObserver();
    vto.addOnGlobalLayoutListener(new PTROnGlobalLayoutListener());

    super.setOnItemClickListener(new PTROnItemClickListener());
    super.setOnItemLongClickListener(new PTROnItemLongClickListener());
  }
  private void bounceBackHeader() {
    int yTranslate =
        state == State.REFRESHING
            ? header.getHeight() - headerContainer.getHeight()
            : -headerContainer.getHeight() - headerContainer.getTop();

    TranslateAnimation bounceAnimation =
        new TranslateAnimation(
            TranslateAnimation.ABSOLUTE,
            0,
            TranslateAnimation.ABSOLUTE,
            0,
            TranslateAnimation.ABSOLUTE,
            0,
            TranslateAnimation.ABSOLUTE,
            yTranslate);

    bounceAnimation.setDuration(BOUNCE_ANIMATION_DURATION);
    bounceAnimation.setFillEnabled(true);
    bounceAnimation.setFillAfter(false);
    bounceAnimation.setFillBefore(true);
    bounceAnimation.setInterpolator(new OvershootInterpolator(BOUNCE_OVERSHOOT_TENSION));
    bounceAnimation.setAnimationListener(new HeaderAnimationListener(yTranslate));

    startAnimation(bounceAnimation);
  }
  private void resetHeader() {
    if (getFirstVisiblePosition() > 0) {
      setHeaderPadding(-header.getHeight());
      setState(State.PULL_TO_REFRESH);
      return;
    }

    if (getAnimation() != null && !getAnimation().hasEnded()) {
      bounceBackHeader = true;
    } else {
      bounceBackHeader();
    }
  }
  @Override
  public boolean onTouchEvent(MotionEvent event) {
    if (lockScrollWhileRefreshing
        && (state == State.REFRESHING || getAnimation() != null && !getAnimation().hasEnded())) {
      return true;
    }

    switch (event.getAction()) {
      case MotionEvent.ACTION_DOWN:
        if (getFirstVisiblePosition() == 0) previousY = event.getY();
        else previousY = -1;
        break;

      case MotionEvent.ACTION_UP:
        if (previousY != -1
            && (state == State.RELEASE_TO_REFRESH || getFirstVisiblePosition() == 0)) {
          switch (state) {
            case RELEASE_TO_REFRESH:
              setState(State.REFRESHING);
              bounceBackHeader();
              break;
            case PULL_TO_REFRESH:
              resetHeader();
              break;
            default:
              break;
          }
        }
        break;

      case MotionEvent.ACTION_MOVE:
        if (previousY != -1) {
          float y = event.getY();
          float diff = y - previousY;
          if (diff > 0) diff /= PULL_RESISTANCE;
          previousY = y;

          int newHeaderPadding = Math.max(Math.round(headerPadding + diff), -header.getHeight());

          if (newHeaderPadding != headerPadding && state != State.REFRESHING) {
            setHeaderPadding(newHeaderPadding);

            if (state == State.PULL_TO_REFRESH && headerPadding > 0) {
              setState(State.RELEASE_TO_REFRESH);

              image.clearAnimation();
              image.startAnimation(flipAnimation);
            } else if (state == State.RELEASE_TO_REFRESH && headerPadding < 0) {
              setState(State.PULL_TO_REFRESH);

              image.clearAnimation();
              image.startAnimation(reverseFlipAnimation);
            }

            return true;
          }
        }

        break;
    }

    return super.onTouchEvent(event);
  }