@Override
  protected synchronized void onLayout(boolean changed, int left, int top, int right, int bottom) {
    super.onLayout(changed, left, top, right, bottom);

    if (mAdapter == null) {
      return;
    }

    if (mDataChanged) {
      int oldCurrentX = mCurrentX;
      initView();
      removeAllViewsInLayout();
      mNextX = oldCurrentX;
      mDataChanged = false;
    }

    if (mScroller.computeScrollOffset()) {
      int scrollx = mScroller.getCurrX();
      mNextX = scrollx;
    }

    if (mNextX < 0) {
      mNextX = 0;
      mScroller.forceFinished(true);
      return;
    }

    if (mNextX > mMaxX) {
      mNextX = mMaxX;
      mScroller.forceFinished(true);
      return;
    }

    int dx = mCurrentX - mNextX;

    removeNonVisibleItems(dx);
    fillList(dx);
    positionItems(dx);

    mCurrentX = mNextX;

    if (!mScroller.isFinished()) {
      post(
          new Runnable() {
            @Override
            public void run() {
              requestLayout();
            }
          });
    }
  }
  @Override
  protected void onLayout(boolean changed, int l, int t, int r, int b) {
    super.onLayout(changed, l, t, r, b);

    for (int i = 0; i < getChildCount(); i++) {
      boundsRect.set(0, 0, getWidth(), getHeight());

      View view = getChildAt(i);
      int w, h;
      w = view.getMeasuredWidth();
      h = view.getMeasuredHeight();

      Gravity.apply(mGravity, w, h, boundsRect, childRect);
      view.layout(childRect.left, childRect.top, childRect.right, childRect.bottom);
    }
  }
  /** {@inheritDoc} */
  @Override
  protected void onLayout(boolean changed, int left, int top, int right, int bottom) {

    super.onLayout(changed, left, top, right, bottom);

    CFUtil.Log("onLayout");

    layoutChildren();

    // scrollTo(getScrollX(), getScrollY());

    //        final int parentLeft = getPaddingLeftWithForeground();
    //        final int parentRight = right - left - getPaddingRightWithForeground();
    //
    //        final int parentTop = getPaddingTopWithForeground();
    //        final int parentBottom = bottom - top - getPaddingBottomWithForeground();
    //
    //        mForegroundBoundsChanged = true;
    //
    //        for (int i = 0; i < count; i++) {
    //            final View child = getChildAt(i);
    //            if (child.getVisibility() != GONE) {
    //                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
    //
    //                final int width = child.getMeasuredWidth();
    //                final int height = child.getMeasuredHeight();
    //
    //                int childLeft;
    //                int childTop;
    //
    //                int gravity = lp.gravity;
    //                if (gravity == -1) {
    //                    gravity = DEFAULT_CHILD_GRAVITY;
    //                }
    //
    //                final int layoutDirection = getLayoutDirection();
    //                final int absoluteGravity = Gravity.getAbsoluteGravity(gravity,
    // layoutDirection);
    //                final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
    //
    //                switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
    //                    case Gravity.LEFT:
    //                        childLeft = parentLeft + lp.leftMargin;
    //                        break;
    //                    case Gravity.CENTER_HORIZONTAL:
    //                        childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
    //                        lp.leftMargin - lp.rightMargin;
    //                        break;
    //                    case Gravity.RIGHT:
    //                        childLeft = parentRight - width - lp.rightMargin;
    //                        break;
    //                    default:
    //                        childLeft = parentLeft + lp.leftMargin;
    //                }
    //
    //                switch (verticalGravity) {
    //                    case Gravity.TOP:
    //                        childTop = parentTop + lp.topMargin;
    //                        break;
    //                    case Gravity.CENTER_VERTICAL:
    //                        childTop = parentTop + (parentBottom - parentTop - height) / 2 +
    //                        lp.topMargin - lp.bottomMargin;
    //                        break;
    //                    case Gravity.BOTTOM:
    //                        childTop = parentBottom - height - lp.bottomMargin;
    //                        break;
    //                    default:
    //                        childTop = parentTop + lp.topMargin;
    //                }
    //
    //                child.layout(childLeft, childTop, childLeft + width, childTop + height);
    //            }
    //        }
  }
  @Override
  protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    Log.i(TAG, "onLayout");
    if (!mUserInteracting) {
      return;
    }
    super.onLayout(changed, left, top, right, bottom);

    View cv = mChildViews.get(mCurrent);
    Point cvOffset;

    if (!mResetLayout) {
      // Move to next or previous if current is sufficiently off center
      if (cv != null) {
        cvOffset = subScreenSizeOffset(cv);
        // cv.getRight() may be out of date with the current scale
        // so add left to the measured width for the correct position
        if (cv.getLeft() + cv.getMeasuredWidth() + cvOffset.x + GAP / 2 + mXScroll < getWidth() / 2
            && mCurrent + 1 < mAdapter.getCount()) {
          postUnsettle(cv);
          // post to invoke test for end of animation
          // where we must set hq area for the new current view
          post(this);

          mCurrent++;
          onMoveToChild(cv, mCurrent);
        }

        if (cv.getLeft() - cvOffset.x - GAP / 2 + mXScroll >= getWidth() / 2 && mCurrent > 0) {
          postUnsettle(cv);
          // post to invoke test for end of animation
          // where we must set hq area for the new current view
          post(this);

          mCurrent--;
          onMoveToChild(cv, mCurrent);
        }
      }

      // Remove not needed children and hold them for reuse
      int numChildren = mChildViews.size();
      int childIndices[] = new int[numChildren];
      for (int i = 0; i < numChildren; i++) childIndices[i] = mChildViews.keyAt(i);

      for (int i = 0; i < numChildren; i++) {
        int ai = childIndices[i];
        if (ai < mCurrent - 1 || ai > mCurrent + 1) {
          View v = mChildViews.get(ai);
          if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            onNotInUse(v);
          }
          mViewCache.add(v);
          removeViewInLayout(v);
          mChildViews.remove(ai);
        }
      }
    } else {
      mResetLayout = false;
      mXScroll = mYScroll = 0;

      // Remove all children and hold them for reuse
      int numChildren = mChildViews.size();
      for (int i = 0; i < numChildren; i++) {
        View v = mChildViews.valueAt(i);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
          onNotInUse(v);
        }
        mViewCache.add(v);
        removeViewInLayout(v);
      }
      mChildViews.clear();
      // post to ensure generation of hq area
      post(this);
    }

    // Ensure current view is present
    int cvLeft, cvRight, cvTop, cvBottom;
    boolean notPresent = (mChildViews.get(mCurrent) == null);
    cv = getOrCreateChild(mCurrent);
    // When the view is sub-screen-size in either dimension we
    // offset it to center within the screen area, and to keep
    // the views spaced out
    cvOffset = subScreenSizeOffset(cv);
    if (notPresent) {
      // Main item not already present. Just place it top left
      cvLeft = cvOffset.x;
      cvTop = cvOffset.y;
    } else {
      // Main item already present. Adjust by scroll offsets
      cvLeft = cv.getLeft() + mXScroll;
      cvTop = cv.getTop() + mYScroll;
    }
    // Scroll values have been accounted for
    mXScroll = mYScroll = 0;
    cvRight = cvLeft + cv.getMeasuredWidth();
    cvBottom = cvTop + cv.getMeasuredHeight();

    if (!mUserInteracting && mScroller.isFinished()) {
      Point corr = getCorrection(getScrollBounds(cvLeft, cvTop, cvRight, cvBottom));
      cvRight += corr.x;
      cvLeft += corr.x;
      cvTop += corr.y;
      cvBottom += corr.y;
    } else if (cv.getMeasuredHeight() <= getHeight()) {
      // When the current view is as small as the screen in height, clamp
      // it vertically
      Point corr = getCorrection(getScrollBounds(cvLeft, cvTop, cvRight, cvBottom));
      cvTop += corr.y;
      cvBottom += corr.y;
    }

    cv.layout(cvLeft, cvTop, cvRight, cvBottom);

    if (mCurrent > 0) {
      View lv = getOrCreateChild(mCurrent - 1);
      Point leftOffset = subScreenSizeOffset(lv);
      int gap = leftOffset.x + GAP + cvOffset.x;
      lv.layout(
          cvLeft - lv.getMeasuredWidth() - gap,
          (cvBottom + cvTop - lv.getMeasuredHeight()) / 2,
          cvLeft - gap,
          (cvBottom + cvTop + lv.getMeasuredHeight()) / 2);
    }

    if (mCurrent + 1 < mAdapter.getCount()) {
      View rv = getOrCreateChild(mCurrent + 1);
      Point rightOffset = subScreenSizeOffset(rv);
      int gap = cvOffset.x + GAP + rightOffset.x;
      rv.layout(
          cvRight + gap,
          (cvBottom + cvTop - rv.getMeasuredHeight()) / 2,
          cvRight + rv.getMeasuredWidth() + gap,
          (cvBottom + cvTop + rv.getMeasuredHeight()) / 2);
    }

    invalidate();
  }
  @SuppressLint("WrongCall")
  @Override
  protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    super.onLayout(changed, left, top, right, bottom);

    if (mAdapter == null) {
      return;
    }

    // Force the OS to redraw this view
    invalidate();

    // If the data changed then reset everything and render from scratch at the same offset as last
    // time
    if (mDataChanged) {
      int oldCurrentX = mCurrentX;
      initView();
      removeAllViewsInLayout();
      mNextX = oldCurrentX;
      mDataChanged = false;
    }

    // If restoring from a rotation
    if (mRestoreX != null) {
      mNextX = mRestoreX;
      mRestoreX = null;
    }

    // If in a fling
    if (mFlingTracker.computeScrollOffset()) {
      // Compute the next position
      mNextX = mFlingTracker.getCurrX();
    }

    // Prevent scrolling past 0 so you can't scroll past the end of the list to the left
    if (mNextX < 0) {
      mNextX = 0;

      // Show an edge effect absorbing the current velocity
      if (mEdgeGlowLeft.isFinished()) {
        mEdgeGlowLeft.onAbsorb((int) determineFlingAbsorbVelocity());
      }

      mFlingTracker.forceFinished(true);
      setCurrentScrollState(OnScrollStateChangedListener.ScrollState.SCROLL_STATE_IDLE);
    } else if (mNextX > mMaxX) {
      // Clip the maximum scroll position at mMaxX so you can't scroll past the end of the list to
      // the right
      mNextX = mMaxX;

      // Show an edge effect absorbing the current velocity
      if (mEdgeGlowRight.isFinished()) {
        mEdgeGlowRight.onAbsorb((int) determineFlingAbsorbVelocity());
      }

      mFlingTracker.forceFinished(true);
      setCurrentScrollState(OnScrollStateChangedListener.ScrollState.SCROLL_STATE_IDLE);
    }

    // Calculate our delta from the last time the view was drawn
    int dx = mCurrentX - mNextX;
    removeNonVisibleChildren(dx);
    fillList(dx);
    positionChildren(dx);

    // Since the view has now been drawn, update our current position
    mCurrentX = mNextX;

    // If we have scrolled enough to lay out all views, then determine the maximum scroll position
    // now
    if (determineMaxX()) {
      // Redo the layout pass since we now know the maximum scroll position
      onLayout(changed, left, top, right, bottom);
      return;
    }

    // If the fling has finished
    if (mFlingTracker.isFinished()) {
      // If the fling just ended
      if (mCurrentScrollState == OnScrollStateChangedListener.ScrollState.SCROLL_STATE_FLING) {
        setCurrentScrollState(OnScrollStateChangedListener.ScrollState.SCROLL_STATE_IDLE);
      }
    } else {
      // Still in a fling so schedule the next frame
      ViewCompat.postOnAnimation(this, mDelayedLayout);
    }
  }