@Override
 public void setSelector(Drawable sel) {
   super.setSelector(sel);
   if (frame != null) {
     frame.setSelector(sel);
   }
 }
 @Override
 public void setVisibility(int visibility) {
   if (frame != null) {
     frame.setVisibility(visibility);
   }
   super.setVisibility(visibility);
 }
 @Override
 public void setSelectionFromTop(int position, int offset) {
   if (!isCalledFromSuper()) {
     if (adapter == null) {
       positionToSetWhenAdapterIsReady = position;
       offsetToSetWhenAdapterIsReady = offset;
       return;
     }
     if (areHeadersSticky) {
       if (frame != null && frame.hasHeader()) {
         offset += frame.getHeaderHeight();
       }
     }
     position = adapter.translateAdapterPosition(position);
   }
   super.setSelectionFromTop(position, offset);
 }
 @Override
 public void onClick(View v) {
   if (frame.isHeader(v)) {
     if (onHeaderClickListener != null) {
       onHeaderClickListener.onHeaderClick(this, v, headerPosition, currentHeaderId, true);
     }
   }
 }
 @Override
 public void setDrawSelectorOnTop(boolean onTop) {
   super.setDrawSelectorOnTop(onTop);
   drawSelectorOnTop = onTop;
   if (frame != null) {
     frame.setDrawSelectorOnTop(drawSelectorOnTop);
   }
 }
  @SuppressWarnings("deprecation")
  @Override
  protected void onAttachedToWindow() {
    super.onAttachedToWindow();
    if (frame == null) {
      ViewGroup parent = ((ViewGroup) getParent());
      int listIndex = parent.indexOfChild(this);
      parent.removeView(this);

      int visibility = getVisibility();
      setVisibility(View.VISIBLE);

      frame = new StickyListHeadersListViewWrapper(getContext());
      frame.setSelector(getSelector());
      frame.setDrawSelectorOnTop(drawSelectorOnTop);
      frame.setVisibility(visibility);

      ViewGroup.MarginLayoutParams p = (MarginLayoutParams) getLayoutParams();
      if (clippingToPadding) {
        frame.setPadding(0, getPaddingTop(), 0, getPaddingBottom());
        setPadding(getPaddingLeft(), 0, getPaddingRight(), 0);
      }

      ViewGroup.LayoutParams params =
          new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
      setLayoutParams(params);

      frame.addView(this);
      frame.setBackgroundDrawable(getBackground());
      super.setBackgroundDrawable(null);

      frame.setLayoutParams(p);
      parent.addView(frame, listIndex);
    }
  }
 @Override
 @Deprecated
 public void setBackgroundDrawable(Drawable background) {
   if (frame != null) {
     frame.setBackgroundDrawable(background);
   } else {
     super.setBackgroundDrawable(background);
   }
 }
 @Override
 public void smoothScrollToPositionFromTop(int position, int offset, int duration) {
   if (!isCalledFromSuper()) {
     if (adapter == null) {
       positionToSetWhenAdapterIsReady = position;
       offsetToSetWhenAdapterIsReady = offset;
       return;
     }
     if (areHeadersSticky) {
       if (frame != null && frame.hasHeader()) {
         offset += frame.getHeaderHeight();
       }
     }
     position = adapter.translateAdapterPosition(position);
   }
   try {
     super.smoothScrollToPositionFromTop(position, offset, duration);
   } catch (NoSuchMethodError e) {
     // droid_utils: API < 11 TODO: update lib to fix this properly
   }
 }
  @Override
  protected void dispatchDraw(Canvas canvas) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO) {
      post(
          new Runnable() {

            @Override
            public void run() {
              scrollChanged(StickyListHeadersListView.super.getFirstVisiblePosition());
            }
          });
    }
    if (!drawingListUnderStickyHeader) {
      canvas.clipRect(
          0, Math.max(frame.getHeaderBottomPosition(), 0), canvas.getWidth(), canvas.getHeight());
    }
    super.dispatchDraw(canvas);
  }
  @Override
  public void setAdapter(ListAdapter adapter) {
    if (this.isInEditMode()) {
      super.setAdapter(adapter);
      return;
    }

    if (!clipToPaddingHasBeenSet) {
      clippingToPadding = true;
    }
    if (adapter != null && !(adapter instanceof StickyListHeadersAdapter)) {
      throw new IllegalArgumentException("Adapter must implement StickyListHeadersAdapter");
    }

    if (this.adapter != null) {
      this.adapter.unregisterInternalDataSetObserver(dataSetChangedObserver);
      this.adapter = null;
    }

    if (adapter != null) {
      if (adapter instanceof SectionIndexer) {
        this.adapter =
            new StickyListHeadersSectionIndexerAdapterWrapper(
                getContext(), (StickyListHeadersAdapter) adapter);
      } else {
        this.adapter =
            new StickyListHeadersAdapterWrapper(getContext(), (StickyListHeadersAdapter) adapter);
      }
      this.adapter.setDivider(divider);
      this.adapter.setDividerHeight(dividerHeight);
      this.adapter.registerInternalDataSetObserver(dataSetChangedObserver);

      setSelectionFromTop(positionToSetWhenAdapterIsReady, offsetToSetWhenAdapterIsReady);
    }

    currentHeaderId = null;
    if (frame != null) {
      frame.removeHeader();
    }
    updateHeaderVisibilities();
    invalidate();

    super.setAdapter(this.adapter);
  }
 @Override
 public void onInvalidated() {
   currentHeaderId = null;
   frame.removeHeader();
 }
  private void scrollChanged(int firstVisibleItem) {
    if (adapter == null || frame == null) {
      return;
    }

    int adapterCount = adapter.getCount();
    if (adapterCount == 0 || !areHeadersSticky) {
      frame.removeHeader();
      return;
    }

    final int listViewHeaderCount = getHeaderViewsCount();
    firstVisibleItem = getFixedFirstVisibleItem(firstVisibleItem) - listViewHeaderCount;

    if (firstVisibleItem < 0 || firstVisibleItem > adapterCount - 1) {
      if (currentHeaderId != null || dataChanged) {
        currentHeaderId = null;
        frame.removeHeader();
        updateHeaderVisibilities();
        invalidate();
        dataChanged = false;
      }
      return;
    }

    boolean headerHasChanged = false;
    long newHeaderId = adapter.getHeaderId(firstVisibleItem);
    if (currentHeaderId == null || currentHeaderId != newHeaderId) {
      headerPosition = firstVisibleItem;
      View header = adapter.getHeaderView(headerPosition, frame.removeHeader(), frame);
      header.setOnClickListener(this);
      frame.setHeader(header);
      headerHasChanged = true;
    }
    currentHeaderId = newHeaderId;

    int childCount = getChildCount();

    if (childCount > 0) {
      View viewToWatch = null;
      int watchingChildDistance = Integer.MAX_VALUE;
      boolean viewToWatchIsFooter = false;

      for (int i = 0; i < childCount; i++) {
        View child = getChildAt(i);
        boolean childIsFooter = footerViews != null && footerViews.contains(child);

        int childDistance;
        if (clippingToPadding) {
          childDistance = child.getTop() - getPaddingTop();
        } else {
          childDistance = child.getTop();
        }

        if (childDistance < 0) {
          continue;
        }

        if (viewToWatch == null
            || (!viewToWatchIsFooter && !adapter.isHeader(viewToWatch))
            || ((childIsFooter || adapter.isHeader(child))
                && childDistance < watchingChildDistance)) {
          viewToWatch = child;
          viewToWatchIsFooter = childIsFooter;
          watchingChildDistance = childDistance;
        }
      }

      int headerHeight = frame.getHeaderHeight();
      int headerBottomPosition = 0;
      if (viewToWatch != null && (viewToWatchIsFooter || adapter.isHeader(viewToWatch))) {

        if (firstVisibleItem == listViewHeaderCount
            && getChildAt(0).getTop() > 0
            && !clippingToPadding) {
          headerBottomPosition = 0;
        } else {
          if (clippingToPadding) {
            headerBottomPosition = Math.min(viewToWatch.getTop(), headerHeight + getPaddingTop());
            headerBottomPosition =
                headerBottomPosition < getPaddingTop()
                    ? headerHeight + getPaddingTop()
                    : headerBottomPosition;
          } else {
            headerBottomPosition = Math.min(viewToWatch.getTop(), headerHeight);
            headerBottomPosition = headerBottomPosition < 0 ? headerHeight : headerBottomPosition;
          }
        }
      } else {
        headerBottomPosition = headerHeight;
        if (clippingToPadding) {
          headerBottomPosition += getPaddingTop();
        }
      }
      if (frame.getHeaderBottomPosition() != headerBottomPosition || headerHasChanged) {
        frame.setHeaderBottomPosition(headerBottomPosition);
      }
      updateHeaderVisibilities();
    }
  }