@Override
  protected void onSizeChanged(int w, int h) {
    super.onSizeChanged(w, h);
    boolean isLandscape = isLandscape();
    int newColumnCount = isLandscape ? mColumnCountLandscape : mColumnCountPortrait;
    if (mColumnCount != newColumnCount) {
      mColumnCount = newColumnCount;

      mColumnWidth = calculateColumnWidth(w);

      mColumnTops = new int[mColumnCount];
      mColumnBottoms = new int[mColumnCount];
      mColumnLefts = new int[mColumnCount];

      mDistanceToTop = 0;

      // rebuild the columns
      initColumnTopsAndBottoms();
      initColumnLefts();

      // if we have data
      if (getCount() > 0 && mPositionData.size() > 0) {
        onColumnSync();
      }

      requestLayout();
    }
  }
  private void offsetGridHeaderFooter(
      final View child,
      final int position,
      final boolean flowDown,
      final int childrenLeft,
      final int childTop) {
    // offset the top and bottom of all our columns
    // if it's the footer we want it below the lowest child bottom
    int gridChildTop;
    int gridChildBottom;

    if (flowDown) {
      gridChildTop = getLowestPositionedBottom();
      gridChildBottom = gridChildTop + getChildHeight(child);
    } else {
      gridChildBottom = getHighestPositionedTop();
      gridChildTop = gridChildBottom - getChildHeight(child);
    }

    for (int i = 0; i < mColumnCount; i++) {
      updateColumnTopIfNeeded(i, gridChildTop);
      updateColumnBottomIfNeeded(i, gridChildBottom);
    }

    super.onOffsetChild(child, position, flowDown, childrenLeft, gridChildTop);
  }
  @Override
  protected void onMeasureChild(final View child, final LayoutParams layoutParams) {
    final int viewType = layoutParams.viewType;
    final int position = layoutParams.position;

    if (viewType == ITEM_VIEW_TYPE_HEADER_OR_FOOTER || viewType == ITEM_VIEW_TYPE_IGNORE) {
      // for headers and weird ignored views
      super.onMeasureChild(child, layoutParams);
    } else {
      if (DBG)
        Log.d(TAG, "onMeasureChild BEFORE position:" + position + " h:" + getMeasuredHeight());
      // measure it to the width of our column.
      int childWidthSpec = MeasureSpec.makeMeasureSpec(mColumnWidth, MeasureSpec.EXACTLY);
      int childHeightSpec;
      if (layoutParams.height > 0) {
        childHeightSpec = MeasureSpec.makeMeasureSpec(layoutParams.height, MeasureSpec.EXACTLY);
      } else {
        childHeightSpec =
            MeasureSpec.makeMeasureSpec(LayoutParams.WRAP_CONTENT, MeasureSpec.UNSPECIFIED);
      }
      child.measure(childWidthSpec, childHeightSpec);
    }

    final int childHeight = getChildHeight(child);
    setPositionHeightRatio(position, childHeight);

    if (DBG) Log.d(TAG, "onMeasureChild AFTER position:" + position + " h:" + childHeight);
  }
  @Override
  protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    if (mColumnCount <= 0) {
      boolean isLandscape = isLandscape();
      mColumnCount = isLandscape ? mColumnCountLandscape : mColumnCountPortrait;
    }

    // our column width is the width of the listview
    // minus it's padding
    // minus the total items margin
    // divided by the number of columns
    mColumnWidth = calculateColumnWidth(getMeasuredWidth());

    if (mColumnTops == null || mColumnTops.length != mColumnCount) {
      mColumnTops = new int[mColumnCount];
      initColumnTops();
    }
    if (mColumnBottoms == null || mColumnBottoms.length != mColumnCount) {
      mColumnBottoms = new int[mColumnCount];
      initColumnBottoms();
    }
    if (mColumnLefts == null || mColumnLefts.length != mColumnCount) {
      mColumnLefts = new int[mColumnCount];
      initColumnLefts();
    }
  }
 @Override
 protected void adjustViewsAfterFillGap(final boolean down) {
   super.adjustViewsAfterFillGap(down);
   // fix vertical gaps when hitting the top after a rotate
   // only when scrolling back up!
   if (!down) {
     alignTops();
   }
 }
 @SuppressWarnings("unchecked")
 @Override
 public void onRestoreInstanceState(Parcelable state) {
   GridListSavedState ss = (GridListSavedState) state;
   mColumnCount = ss.columnCount;
   mColumnTops = ss.columnTops;
   mColumnBottoms = new int[mColumnCount];
   mPositionData = ss.positionData;
   mNeedSync = true;
   super.onRestoreInstanceState(ss);
 }
 @Override
 protected void onChildCreated(final int position, final boolean flowDown) {
   super.onChildCreated(position, flowDown);
   if (!isHeaderOrFooter(position)) {
     // do we already have a column for this position?
     final int column = getChildColumn(position, flowDown);
     setPositionColumn(position, column);
     if (DBG) Log.d(TAG, "onChildCreated position:" + position + " is in column:" + column);
   } else {
     setPositionIsHeaderFooter(position);
   }
 }
  private void offsetGridChild(
      final View child,
      final int position,
      final boolean flowDown,
      final int childrenLeft,
      final int childTop) {
    // stash the bottom and the top if it's higher positioned
    int column = getPositionColumn(position);

    int gridChildTop;
    int gridChildBottom;

    int childTopMargin = getChildTopMargin(position);
    int childBottomMargin = getChildBottomMargin();
    int verticalMargins = childTopMargin + childBottomMargin;

    if (flowDown) {
      gridChildTop = mColumnBottoms[column]; // the next items top is the
      // last items bottom
      gridChildBottom = gridChildTop + (getChildHeight(child) + verticalMargins);
    } else {
      gridChildBottom = mColumnTops[column]; // the bottom of the next
      // column up is our top
      gridChildTop = gridChildBottom - (getChildHeight(child) + verticalMargins);
    }

    if (DBG)
      Log.d(
          TAG,
          "onOffsetChild position:"
              + position
              + " column:"
              + column
              + " childTop:"
              + childTop
              + " gridChildTop:"
              + gridChildTop
              + " gridChildBottom:"
              + gridChildBottom);

    // we also know the column of this view so let's stash it in the
    // view's layout params
    GridLayoutParams layoutParams = (GridLayoutParams) child.getLayoutParams();
    layoutParams.column = column;

    updateColumnBottomIfNeeded(column, gridChildBottom);
    updateColumnTopIfNeeded(column, gridChildTop);

    super.onOffsetChild(child, position, flowDown, childrenLeft, gridChildTop + childTopMargin);
  }
  @Override
  protected void onChildrenDetached(final int start, final int count) {
    super.onChildrenDetached(start, count);
    // go through our remaining views and sync the top and bottom stash.

    // Repair the top and bottom column boundaries from the views we still
    // have
    Arrays.fill(mColumnTops, Integer.MAX_VALUE);
    Arrays.fill(mColumnBottoms, 0);

    for (int i = 0; i < getChildCount(); i++) {
      final View child = getChildAt(i);
      if (child != null) {
        final LayoutParams childParams = (LayoutParams) child.getLayoutParams();
        if (childParams.viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER
            && childParams instanceof GridLayoutParams) {
          GridLayoutParams layoutParams = (GridLayoutParams) childParams;
          int column = layoutParams.column;
          int position = layoutParams.position;
          final int childTop = child.getTop();
          if (childTop < mColumnTops[column]) {
            mColumnTops[column] = childTop - getChildTopMargin(position);
          }
          final int childBottom = child.getBottom();
          if (childBottom > mColumnBottoms[column]) {
            mColumnBottoms[column] = childBottom + getChildBottomMargin();
          }
        } else {
          // the header and footer here
          final int childTop = child.getTop();
          final int childBottom = child.getBottom();

          for (int col = 0; col < mColumnCount; col++) {
            if (childTop < mColumnTops[col]) {
              mColumnTops[col] = childTop;
            }
            if (childBottom > mColumnBottoms[col]) {
              mColumnBottoms[col] = childBottom;
            }
          }
        }
      }
    }
  }
 @Override
 protected void onSizeChanged(final int w, final int h, final int oldw, final int oldh) {
   super.onSizeChanged(w, h, oldw, oldh);
   onSizeChanged(w, h);
 }
 @Override
 protected void offsetChildrenTopAndBottom(final int offset) {
   super.offsetChildrenTopAndBottom(offset);
   offsetAllColumnsTopAndBottom(offset);
   offsetDistanceToTop(offset);
 }
 @Override
 protected void layoutChildren() {
   preLayoutChildren();
   super.layoutChildren();
 }