Beispiel #1
0
  private Bitmap extentSnapshot(int doc, int gs, int spaceAround, boolean transparent) {
    final LogHelper log = new LogHelper();
    final Rect extent = getDisplayExtent();

    if (!extent.isEmpty()) {
      extent.inset(-spaceAround, -spaceAround);
      extent.intersect(0, 0, mView.getView().getWidth(), mView.getView().getHeight());
    }
    if (extent.isEmpty()) {
      return null;
    }

    final Bitmap viewBitmap = mView.snapshot(doc, gs, transparent);
    if (viewBitmap == null) {
      return null;
    }

    if (extent.width() == mView.getView().getWidth()
        && extent.height() == mView.getView().getHeight()) {
      log.r(viewBitmap.getByteCount());
      return viewBitmap;
    }

    final Bitmap realBitmap =
        Bitmap.createBitmap(viewBitmap, extent.left, extent.top, extent.width(), extent.height());

    viewBitmap.recycle();
    log.r(realBitmap.getByteCount());
    return realBitmap;
  }
  @Override
  protected void dispatchDraw(Canvas canvas) {
    View childView = getChildAt(mCurrentSelection);
    TextView childTextView = null;
    if (childView instanceof TextView) {
      childTextView = (TextView) childView;
    }

    if (childTextView != null
        && !TextUtils.isEmpty(childTextView.getText())
        && getVisibility() == View.VISIBLE
        && childTextView.getVisibility() == View.VISIBLE) {
      Rect childRect = new Rect();
      childTextView.getLocalVisibleRect(childRect);

      float childViewX = childTextView.getX() + childTextView.getWidth() / 2;
      float childViewY = childTextView.getY() + childTextView.getHeight() / 2;

      int top = (int) (childViewY - mSelectedRect.height() / 2);
      int left = (int) (childViewX - mSelectedRect.width() / 2);

      mSelectedRect.set(left, top, left + mSelectedRect.width(), top + mSelectedRect.height());

      if (isShowSelected && !mSelectedRect.isEmpty()) {
        final Drawable selectedBg = mSelectedBg;
        selectedBg.setBounds(mSelectedRect);
        selectedBg.draw(canvas);
      }
    }

    super.dispatchDraw(canvas);
  }
  /**
   * Computes whether the specified {@link android.graphics.Rect} intersects with the visible
   * portion of its parent {@link android.view.View}. Modifies {@code localRect} to contain only the
   * visible portion.
   *
   * @param localRect A rectangle in local (parent) coordinates.
   * @return Whether the specified {@link android.graphics.Rect} is visible on the screen.
   */
  private boolean intersectVisibleToUser(Rect localRect) {
    // Missing or empty bounds mean this view is not visible.
    if ((localRect == null) || localRect.isEmpty()) {
      return false;
    }

    // Attached to invisible window means this view is not visible.
    if (mParentView.getWindowVisibility() != View.VISIBLE) {
      return false;
    }

    // An invisible predecessor or one with alpha zero means
    // that this view is not visible to the user.
    Object current = this;
    while (current instanceof View) {
      final View view = (View) current;
      // We have attach info so this view is attached and there is no
      // need to check whether we reach to ViewRootImpl on the way up.
      if ((view.getAlpha() <= 0) || (view.getVisibility() != View.VISIBLE)) {
        return false;
      }
      current = view.getParent();
    }

    // If no portion of the parent is visible, this view is not visible.
    if (!mParentView.getLocalVisibleRect(mTempVisibleRect)) {
      return false;
    }

    // Check if the view intersects the visible portion of the parent.
    return localRect.intersect(mTempVisibleRect);
  }
  public void commOnTouchEvent(MotionEvent ev) {
    int action = ev.getAction();
    switch (action) {
      case MotionEvent.ACTION_DOWN:
        y = ev.getY();
        break;
      case MotionEvent.ACTION_UP:
        if (isNeedAnimation()) {
          animation();
        }
        break;
      case MotionEvent.ACTION_MOVE:
        final float preY = y;
        float nowY = ev.getY();
        int deltaY = (int) (preY - nowY) / size;

        y = nowY;
        if (isNeedMove()) {
          if (normal.isEmpty()) {
            normal.set(inner.getLeft(), inner.getTop(), inner.getRight(), inner.getBottom());
            return;
          }
          int yy = inner.getTop() - deltaY;

          inner.layout(inner.getLeft(), yy, inner.getRight(), inner.getBottom() - deltaY);
        }
        break;
      default:
        break;
    }
  }
 @Override
 public void draw(Canvas canvas) {
   if (null != mBitmap && !mBitmap.isRecycled()) {
     final Rect bounds = getBounds();
     if (!bounds.isEmpty()) {
       canvas.drawBitmap(mBitmap, null, bounds, mPaint);
     } else {
       canvas.drawBitmap(mBitmap, 0f, 0f, mPaint);
     }
   }
 }
Beispiel #6
0
 public static Rect preInvalidate(View view, Rect dirty) {
   if (dirty.isEmpty()) return dirty;
   Matrix m = getViewMatrix(view);
   if (m != null) {
     if (dirty != m_tempRect) {
       m_tempRect.set(dirty);
       dirty = m_tempRect;
     }
     transformFrame(m_tempRect, m);
   }
   return dirty;
 }
  public void commOnTouchEvent(MotionEvent ev) {
    int action = ev.getAction();
    switch (action) {
      case MotionEvent.ACTION_DOWN:
        y = ev.getY();
        break;
      case MotionEvent.ACTION_UP:
        if (isNeedAnimation()) {
          animation();
        }

        // 加入额外代码
        if (mAutoScrollViewPager != null) {
          mAutoScrollViewPager.startAutoScroll();
        }

        y = DEFAULT_POSITION;
        break;

        /**
         * 排除第一次移动计算,因为第一次无法得知y左边,在MotionEvent.ACTION_DOWN中获取不到,
         * 因为此时是MyScrollView的Tocuh时间传递到了ListView的孩子item上面。所以从第二次开始计算
         * 然而我们也要进行初始化,就是第一次移动的时候让滑动距离归零,之后记录准确了就正常执行
         */
      case MotionEvent.ACTION_MOVE:
        float preY = y;
        float nowY = ev.getY();
        if (isDefaultPosition(y)) {
          preY = nowY;
        }
        int deltaY = (int) (preY - nowY);
        scrollBy(0, deltaY);
        y = nowY;
        // 当滚动到最上或者最下时就不会再滚动,这时移动布局
        if (isNeedMove()) {
          if (normal.isEmpty()) {
            // 保存正常的布局位置
            normal.set(inner.getLeft(), inner.getTop(), inner.getRight(), inner.getBottom());
          }
          // 移动布局
          inner.layout(
              inner.getLeft(),
              inner.getTop() - deltaY / 2,
              inner.getRight(),
              inner.getBottom() - deltaY / 2);
        }
        break;

      default:
        break;
    }
  }
  private AccessibilityNodeInfoCompat populateNodeForItemInternal(
      T item, AccessibilityNodeInfoCompat node) {
    final int virtualDescendantId = getIdForItem(item);

    // Ensure the client has good defaults.
    node.setEnabled(true);

    // Allow the client to populate the node.
    populateNodeForItem(item, node);

    if (TextUtils.isEmpty(node.getText()) && TextUtils.isEmpty(node.getContentDescription())) {
      throw new RuntimeException(
          "You must add text or a content description in populateNodeForItem()");
    }

    // Don't allow the client to override these properties.
    node.setPackageName(mParentView.getContext().getPackageName());
    node.setClassName(item.getClass().getName());
    node.setParent(mParentView);
    node.setSource(mParentView, virtualDescendantId);

    if (mFocusedItemId == virtualDescendantId) {
      node.addAction(AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
    } else {
      node.addAction(AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS);
    }

    node.getBoundsInParent(mTempParentRect);
    if (mTempParentRect.isEmpty()) {
      throw new RuntimeException("You must set parent bounds in populateNodeForItem()");
    }

    // Set the visibility based on the parent bound.
    if (intersectVisibleToUser(mTempParentRect)) {
      node.setVisibleToUser(true);
      node.setBoundsInParent(mTempParentRect);
    }

    // Calculate screen-relative bound.
    mParentView.getLocationOnScreen(mTempGlobalRect);
    final int offsetX = mTempGlobalRect[0];
    final int offsetY = mTempGlobalRect[1];
    mTempScreenRect.set(mTempParentRect);
    mTempScreenRect.offset(offsetX, offsetY);
    node.setBoundsInScreen(mTempScreenRect);

    return node;
  }
    private void handleFunctorStatus(View.AttachInfo attachInfo, int status) {
      // If the draw flag is set, functors will be invoked while executing
      // the tree of display lists
      if ((status & DisplayList.STATUS_DRAW) != 0) {
        if (mRedrawClip.isEmpty()) {
          attachInfo.mViewRootImpl.invalidate();
        } else {
          attachInfo.mViewRootImpl.invalidateChildInParent(null, mRedrawClip);
          mRedrawClip.setEmpty();
        }
      }

      if ((status & DisplayList.STATUS_INVOKE) != 0) {
        scheduleFunctors(attachInfo, true);
      }
    }
 private AccessibilityNodeInfoCompat createNodeForChild(int i) {
   AccessibilityNodeInfoCompat accessibilitynodeinfocompat = AccessibilityNodeInfoCompat.obtain();
   accessibilitynodeinfocompat.setEnabled(true);
   accessibilitynodeinfocompat.setClassName(DEFAULT_CLASS_NAME);
   onPopulateNodeForVirtualView(i, accessibilitynodeinfocompat);
   if (accessibilitynodeinfocompat.getText() == null
       && accessibilitynodeinfocompat.getContentDescription() == null) {
     throw new RuntimeException(
         "Callbacks must add text or a content description in populateNodeForVirtualViewId()");
   }
   accessibilitynodeinfocompat.getBoundsInParent(mTempParentRect);
   if (mTempParentRect.isEmpty()) {
     throw new RuntimeException(
         "Callbacks must set parent bounds in populateNodeForVirtualViewId()");
   }
   int j = accessibilitynodeinfocompat.getActions();
   if ((j & 0x40) != 0) {
     throw new RuntimeException(
         "Callbacks must not add ACTION_ACCESSIBILITY_FOCUS in populateNodeForVirtualViewId()");
   }
   if ((j & 0x80) != 0) {
     throw new RuntimeException(
         "Callbacks must not add ACTION_CLEAR_ACCESSIBILITY_FOCUS in populateNodeForVirtualViewId()");
   }
   accessibilitynodeinfocompat.setPackageName(mView.getContext().getPackageName());
   accessibilitynodeinfocompat.setSource(mView, i);
   accessibilitynodeinfocompat.setParent(mView);
   if (mFocusedVirtualViewId == i) {
     accessibilitynodeinfocompat.setAccessibilityFocused(true);
     accessibilitynodeinfocompat.addAction(128);
   } else {
     accessibilitynodeinfocompat.setAccessibilityFocused(false);
     accessibilitynodeinfocompat.addAction(64);
   }
   if (intersectVisibleToUser(mTempParentRect)) {
     accessibilitynodeinfocompat.setVisibleToUser(true);
     accessibilitynodeinfocompat.setBoundsInParent(mTempParentRect);
   }
   mView.getLocationOnScreen(mTempGlobalRect);
   i = mTempGlobalRect[0];
   j = mTempGlobalRect[1];
   mTempScreenRect.set(mTempParentRect);
   mTempScreenRect.offset(i, j);
   accessibilitynodeinfocompat.setBoundsInScreen(mTempScreenRect);
   return accessibilitynodeinfocompat;
 }
 private void updatePath() {
   if (bounds.isEmpty()) return;
   path = null;
   RectF outerRect = TiUIHelper.insetRect(boundsF, mPadding);
   if (radius != null) {
     path = new Path();
     path.setFillType(FillType.EVEN_ODD);
     if (pathWidth > 0) {
       path.addRoundRect(outerRect, radius, Direction.CW);
       float padding = 0;
       float maxPadding = 0;
       RectF innerRect = new RectF();
       maxPadding = Math.min(bounds.width() / 2, bounds.height() / 2);
       padding = Math.min(pathWidth, maxPadding);
       innerRect.set(
           outerRect.left + padding,
           outerRect.top + padding,
           outerRect.right - padding,
           outerRect.bottom - padding);
       path.addRoundRect(innerRect, innerRadiusFromPadding(outerRect, padding), Direction.CCW);
     } else {
       // adjustment not see background under border because of antialias
       path.addRoundRect(TiUIHelper.insetRect(outerRect, 0.3f), radius, Direction.CW);
     }
   } else {
     if (pathWidth > 0) {
       path = new Path();
       path.setFillType(FillType.EVEN_ODD);
       path.addRect(outerRect, Direction.CW);
       int padding = 0;
       int maxPadding = 0;
       RectF innerRect = new RectF();
       maxPadding = (int) Math.min(bounds.width() / 2, bounds.height() / 2);
       padding = (int) Math.min(pathWidth, maxPadding);
       innerRect.set(
           outerRect.left + padding,
           outerRect.top + padding,
           outerRect.right - padding,
           outerRect.bottom - padding);
       path.addRect(innerRect, Direction.CCW);
     }
   }
 }
      /** NOTE: This has to be called within a surface transaction. */
      public void drawIfNeeded() {
        synchronized (mWindowManagerService.mWindowMap) {
          if (!mInvalidated) {
            return;
          }
          mInvalidated = false;
          Canvas canvas = null;
          try {
            // Empty dirty rectangle means unspecified.
            if (mDirtyRect.isEmpty()) {
              mBounds.getBounds(mDirtyRect);
            }
            mDirtyRect.inset(-mHalfBorderWidth, -mHalfBorderWidth);
            canvas = mSurface.lockCanvas(mDirtyRect);
            if (DEBUG_VIEWPORT_WINDOW) {
              Slog.i(LOG_TAG, "Dirty rect: " + mDirtyRect);
            }
          } catch (IllegalArgumentException iae) {
            /* ignore */
          } catch (Surface.OutOfResourcesException oore) {
            /* ignore */
          }
          if (canvas == null) {
            return;
          }
          if (DEBUG_VIEWPORT_WINDOW) {
            Slog.i(LOG_TAG, "Bounds: " + mBounds);
          }
          canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
          mPaint.setAlpha(mAlpha);
          Path path = mBounds.getBoundaryPath();
          canvas.drawPath(path, mPaint);

          mSurface.unlockCanvasAndPost(canvas);

          if (mAlpha > 0) {
            mSurfaceControl.show();
          } else {
            mSurfaceControl.hide();
          }
        }
      }
  @Override
  public void getOutline(@NonNull Outline outline) {
    final Rect bounds = getBounds();
    if (bounds.isEmpty()) return;

    if (mNinePatchState != null) {
      NinePatch.InsetStruct insets = mNinePatchState.getBitmap().getNinePatchInsets();
      if (insets != null) {
        final Rect outlineInsets = insets.outlineRect;
        outline.setRoundRect(
            bounds.left + outlineInsets.left,
            bounds.top + outlineInsets.top,
            bounds.right - outlineInsets.right,
            bounds.bottom - outlineInsets.bottom,
            insets.outlineRadius);
        outline.setAlpha(insets.outlineAlpha * (getAlpha() / 255.0f));
        return;
      }
    }
    super.getOutline(outline);
  }
Beispiel #14
0
  @Implementation
  public boolean getGlobalVisibleRect(Rect rect, Point globalOffset) {
    if (globalVisibleRect == null) {
      /*
       * The global visible rect is not initialized. The value is not reliable as Robolectric does
       * not perform layouts in most cases. Use a substitute concept of visibility if no rect
       * had been set explicitly.
       */
      rect.setEmpty();
      return realView.isShown();
    }

    if (!globalVisibleRect.isEmpty()) {
      rect.set(globalVisibleRect);
      if (globalOffset != null) {
        rect.offset(-globalOffset.x, -globalOffset.y);
      }
      return true;
    }
    rect.setEmpty();
    return false;
  }
  private boolean intersectVisibleToUser(Rect rect) {
    if (rect == null || rect.isEmpty()) {
      return false;
    }
    if (mView.getWindowVisibility() != 0) {
      return false;
    }
    Object obj;
    for (obj = mView.getParent(); obj instanceof View; obj = ((View) (obj)).getParent()) {
      obj = (View) obj;
      if (ViewCompat.getAlpha(((View) (obj))) <= 0.0F || ((View) (obj)).getVisibility() != 0) {
        return false;
      }
    }

    if (obj == null) {
      return false;
    }
    if (!mView.getLocalVisibleRect(mTempVisibleRect)) {
      return false;
    } else {
      return rect.intersect(mTempVisibleRect);
    }
  }
  @Override
  public boolean onTouchEvent(MotionEvent ev) {
    int action = ev.getAction();
    switch (action) {
      case MotionEvent.ACTION_DOWN:
        mDownY = ev.getY();
        super.onTouchEvent(ev);
        break;
      case MotionEvent.ACTION_MOVE:
        if (mCurrentOffset <= 0) {
          isNeedMove();
          if (!isTouchOne) {
            startY = ev.getY();
            isTouchOne = true;
          }
          distance = ev.getY() - startY;
          if (distance > 0) {
            isBig = true;
            setT((int) -distance / 4);
          }
        } else {
          final float preY = mDownY == 0 ? ev.getY() : mDownY;
          float currentY = ev.getY();
          int deltaY = (int) (preY - currentY);
          // 滚动
          mDownY = currentY;
          // 当滚动到最上或者最下时就不会再滚动,这时移动布局
          if (isNeedMove()) {
            if (normal.isEmpty()) {
              Log.e(
                  "normal",
                  mFirstChild.getLeft()
                      + "  "
                      + mFirstChild.getTop()
                      + " i "
                      + mFirstChild.getBottom()
                      + "  "
                      + mFirstChild.getRight()
                      + "   "
                      + mFirstChild.getHeight());
              // 保存正常的布局位置
              normal.set(
                  mFirstChild.getLeft(),
                  mFirstChild.getTop(),
                  mFirstChild.getRight(),
                  mFirstChild.getBottom());
            }
            // 移动布局(关键)
            mFirstChild.layout(
                mFirstChild.getLeft(),
                mFirstChild.getTop() - deltaY / 4,
                mFirstChild.getRight(),
                mFirstChild.getBottom() - deltaY / 4);
            //                        mFirstChild.scrollBy(0,deltaY);
          } else {
            super.onTouchEvent(ev);
          }
        }
        break;
      case MotionEvent.ACTION_UP:
        if (isBig) {
          reset();
          isBig = false;
        }
        isTouchOne = false;

        mDownY = 0;
        if (isNeedAnimation()) {
          animation();
        }
        super.onTouchEvent(ev);
        break;
    }
    return super.onTouchEvent(ev);
  }
  @Override
  public boolean onTouchEvent(MotionEvent event) {
    if (!isEnabled()) return false;

    final View parent = (View) getParent();
    switch (event.getAction()) {
      case MotionEvent.ACTION_DOWN:
        {

          // only start tracking when in sweet spot
          final boolean acceptDrag;
          final boolean acceptLabel;
          if (mFollowAxis == VERTICAL) {
            acceptDrag = event.getX() > getWidth() - (mSweepPadding.right * 8);
            acceptLabel = mLabelLayout != null ? event.getX() < mLabelLayout.getWidth() : false;
          } else {
            acceptDrag = event.getY() > getHeight() - (mSweepPadding.bottom * 8);
            acceptLabel = mLabelLayout != null ? event.getY() < mLabelLayout.getHeight() : false;
          }

          final MotionEvent eventInParent = event.copy();
          eventInParent.offsetLocation(getLeft(), getTop());

          // ignore event when closer to a neighbor
          for (ChartSweepView neighbor : mNeighbors) {
            if (isTouchCloserTo(eventInParent, neighbor)) {
              return false;
            }
          }

          if (acceptDrag) {
            if (mFollowAxis == VERTICAL) {
              mTrackingStart = getTop() - mMargins.top;
            } else {
              mTrackingStart = getLeft() - mMargins.left;
            }
            mTracking = event.copy();
            mTouchMode = MODE_DRAG;

            // starting drag should activate entire chart
            if (!parent.isActivated()) {
              parent.setActivated(true);
            }

            return true;
          } else if (acceptLabel) {
            mTouchMode = MODE_LABEL;
            return true;
          } else {
            mTouchMode = MODE_NONE;
            return false;
          }
        }
      case MotionEvent.ACTION_MOVE:
        {
          if (mTouchMode == MODE_LABEL) {
            return true;
          }

          getParent().requestDisallowInterceptTouchEvent(true);

          // content area of parent
          final Rect parentContent = getParentContentRect();
          final Rect clampRect = computeClampRect(parentContent);
          if (clampRect.isEmpty()) return true;

          long value;
          if (mFollowAxis == VERTICAL) {
            final float currentTargetY = getTop() - mMargins.top;
            final float requestedTargetY = mTrackingStart + (event.getRawY() - mTracking.getRawY());
            final float clampedTargetY =
                MathUtils.constrain(requestedTargetY, clampRect.top, clampRect.bottom);
            setTranslationY(clampedTargetY - currentTargetY);

            value = mAxis.convertToValue(clampedTargetY - parentContent.top);
          } else {
            final float currentTargetX = getLeft() - mMargins.left;
            final float requestedTargetX = mTrackingStart + (event.getRawX() - mTracking.getRawX());
            final float clampedTargetX =
                MathUtils.constrain(requestedTargetX, clampRect.left, clampRect.right);
            setTranslationX(clampedTargetX - currentTargetX);

            value = mAxis.convertToValue(clampedTargetX - parentContent.left);
          }

          // round value from drag to nearest increment
          value -= value % mDragInterval;
          setValue(value);

          dispatchOnSweep(false);
          return true;
        }
      case MotionEvent.ACTION_UP:
        {
          if (mTouchMode == MODE_LABEL) {
            performClick();
          } else if (mTouchMode == MODE_DRAG) {
            mTrackingStart = 0;
            mTracking = null;
            mValue = mLabelValue;
            dispatchOnSweep(true);
            setTranslationX(0);
            setTranslationY(0);
            requestLayout();
          }

          mTouchMode = MODE_NONE;
          return true;
        }
      default:
        {
          return false;
        }
    }
  }
Beispiel #18
0
  /**
   * 执行移动动画
   *
   * @param event
   */
  private void doActionMove(MotionEvent event) {
    // 当滚动到顶部时,将状态设置为正常,避免先向上拖动再向下拖动到顶端后首次触摸不响应的问题
    if (getScrollY() == 0) {
      mState = State.NORMAL;

      // 滑动经过顶部初始位置时,修正Touch down的坐标为当前Touch点的坐标
      if (isTop) {
        isTop = false;
        mTouchDownY = event.getY();
      }
    }

    float deltaY = event.getY() - mTouchDownY;

    // 对于首次Touch操作要判断方位:UP OR DOWN
    if (deltaY < 0 && mState == State.NORMAL) {
      mState = State.UP;
    } else if (deltaY > 0 && mState == State.NORMAL) {
      mState = State.DOWN;
    }

    if (mState == State.UP) {
      deltaY = deltaY < 0 ? deltaY : 0;

      isMoving = false;
      mEnableTouch = false;

    } else if (mState == State.DOWN) {
      if (getScrollY() <= deltaY) {
        mEnableTouch = true;
        isMoving = true;
      }
      deltaY = deltaY < 0 ? 0 : deltaY;
    }

    if (isMoving) {
      // 初始化content view矩形
      if (mContentRect.isEmpty()) {
        // 保存正常的布局位置
        mContentRect.set(
            mContentView.getLeft(),
            mContentView.getTop(),
            mContentView.getRight(),
            mContentView.getBottom());
      }

      // 计算header移动距离(手势移动的距离*阻尼系数*0.5)
      float headerMoveHeight = deltaY * 0.5f * SCROLL_RATIO;
      mCurrentTop = (int) (mInitTop + headerMoveHeight);
      mCurrentBottom = (int) (mInitBottom + headerMoveHeight);

      // 计算content移动距离(手势移动的距离*阻尼系数)
      float contentMoveHeight = deltaY * SCROLL_RATIO;

      // 修正content移动的距离,避免超过header的底边缘
      int headerBottom = mCurrentBottom - mHeaderVisibleHeight;
      int top = (int) (mContentRect.top + contentMoveHeight);
      int bottom = (int) (mContentRect.bottom + contentMoveHeight);

      if (top <= headerBottom) {
        // 移动content view
        mContentView.layout(mContentRect.left, top, mContentRect.right, bottom);

        // 移动header view
        mHeader.layout(mHeader.getLeft(), mCurrentTop, mHeader.getRight(), mCurrentBottom);
      }
    }
  }
Beispiel #19
0
 /** 是否需要开启动画 */
 private boolean isNeedAnimation() {
   return !mContentRect.isEmpty() && isMoving;
 }
  private void updateGeometry() {
    final Rect b = getBounds();

    mStrokePath.reset();
    mFillPath.reset();
    mIndicatorFillPath.reset();
    mIndicatorStrokePath.reset();

    if (b.isEmpty()) {
      return;
    }

    final boolean isHorizontal =
        mIndicatorDirection == IndicatorDirection.TOP
            || mIndicatorDirection == IndicatorDirection.BOTTOM;
    final int innerCornerWidth = mCornerRadius - mStrokeWidth;
    final float sW = mStrokeWidth;
    final float iW = mIndicatorWidth;
    final float cR = mCornerRadius;
    final float iH = mIndicatorHeight;
    final float iVISO = iW * (iH + sW) / 2f / iH - iW / 2f;

    mIndicatorVerticalInnerStrokeOffset = iVISO;

    if (mIndicatorHeight > 0) {
      mIndicatorVerticalStrokeWidth = (float) (sW / Math.cos(Math.atan(iW / 2f / iH)));
    }

    final float iVSW = mIndicatorVerticalStrokeWidth;

    final RectF outerRect = new RectF(b.left, b.top, b.right, b.bottom);
    final RectF innerRect = new RectF(b.left + sW, b.top + sW, b.right - sW, b.bottom - sW);
    float neededSpace = iW + Math.max(iVISO + Math.max(sW, cR), iVSW + cR) * 2;

    // correct bubble stroke and fill rectangle so the indicator fits besides
    if (mIndicatorWidth > 0) {
      mIndicatorHorizontalStrokeWidth = 2f * iVSW * iH / iW;
      float addOffset = iH + mIndicatorHorizontalStrokeWidth;

      switch (mIndicatorDirection) {
        case LEFT:
          outerRect.left += addOffset;
          innerRect.left += addOffset;
          break;

        case RIGHT:
          outerRect.right -= addOffset;
          innerRect.right -= addOffset;
          break;

        case TOP:
          outerRect.top += addOffset;
          innerRect.top += addOffset;
          break;

        case BOTTOM:
          outerRect.bottom -= addOffset;
          innerRect.bottom -= addOffset;
          break;
      }
    }

    // only create indicator paths if there is room for it since the bounds might be to little
    // and/or the corners might need the room so there's not enough room for a indicator
    if (mIndicatorHeight > 0
        && mIndicatorWidth > 0
        && (!isHorizontal && b.height() >= neededSpace
            || isHorizontal && b.width() >= neededSpace)) {
      // would throw an exception on indicator width == 0

      // create indicator stroke and fill path based on the given direction
      switch (mIndicatorDirection) {
        case LEFT:
          updateLeftIndicator(outerRect, innerRect);
          break;

        case RIGHT:
          // outerRect.right -= mIndicatorHeight;
          // innerRect.right -= mIndicatorHeight;
          //
          // mIndicatorFillPath.moveTo(outerRect.right, outerRect.top + mStrokeWidth);
          // mIndicatorFillPath.lineTo(outerRect.right + mIndicatorHeight,
          // outerRect.top + mStrokeWidth + mIndicatorWidth / 2f);
          // mIndicatorFillPath.lineTo(outerRect.right,
          // outerRect.top + mStrokeWidth + mIndicatorWidth);
          // mIndicatorFillPath.close();

          break;

        case TOP:
          // outerRect.top += mIndicatorHeight;
          // innerRect.top += mIndicatorHeight;
          //
          // mIndicatorFillPath.moveTo(outerRect.left + mStrokeWidth, outerRect.top);
          // mIndicatorFillPath.lineTo(outerRect.left + mStrokeWidth + mIndicatorWidth / 2f,
          // outerRect.top - mIndicatorHeight);
          // mIndicatorFillPath.lineTo(outerRect.left + mStrokeWidth + mIndicatorWidth,
          // outerRect.top);
          // mIndicatorFillPath.close();
          //
          // mIndicatorFillPath.setLastPoint(outerRect.left, outerRect.top);
          // mIndicatorFillPath.lineTo(outerRect.left + mIndicatorHeight,
          // outerRect.top + mStrokeWidth + mIndicatorWidth / 2f);
          // mIndicatorFillPath.close();

          break;

        case BOTTOM:
          // outerRect.bottom -= mIndicatorHeight;
          // innerRect.bottom -= mIndicatorHeight;
          //
          // mIndicatorFillPath.moveTo(outerRect.left + mStrokeWidth, outerRect.bottom);
          // mIndicatorFillPath.lineTo(outerRect.left + mStrokeWidth + mIndicatorWidth / 2f,
          // outerRect.bottom + mIndicatorHeight);
          // mIndicatorFillPath.lineTo(outerRect.left + mStrokeWidth + mIndicatorWidth,
          // outerRect.bottom);
          // mIndicatorFillPath.close();

          break;
      }
    }

    // that's the main fill and stroke path of the bubble
    if (mCornerRadius >= 1) {
      // create bubble rect with round corners
      float[] outerRadius = new float[8];
      Arrays.fill(outerRadius, mCornerRadius);

      mStrokePath.addRoundRect(outerRect, outerRadius, Direction.CW);

      if (innerCornerWidth >= 1) {
        float[] innerRadius = new float[8];
        Arrays.fill(innerRadius, innerCornerWidth);

        if (mStrokeWidth >= 1) {
          mStrokePath.addRoundRect(innerRect, innerRadius, Direction.CCW);
        }

        mFillPath.addRoundRect(innerRect, innerRadius, Direction.CW);
      } else {
        if (mStrokeWidth >= 1) {
          mStrokePath.addRect(innerRect, Direction.CCW);
        }

        mFillPath.addRect(innerRect, Direction.CW);
      }
    } else {
      // create bubble without round corners
      if (mStrokeWidth >= 1) {
        mStrokePath.addRect(outerRect, Direction.CW);
        mStrokePath.addRect(innerRect, Direction.CCW);
      }

      mFillPath.addRect(innerRect, Direction.CW);
    }
  }
Beispiel #21
0
  @Override
  public void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    onDrawExcelBackgroud(canvas);
    if (colWidth == null || rowHeight == null) return;
    if (showLinkedLine) onDrawLinkedLines(canvas);
    for (Cell cell : cells) {

      Rect rect = getCellClipRect(cell);
      if (rect != null) {
        canvas.save();
        canvas.clipRect(rect);
        onDrawCell(canvas, cell);
        canvas.restore();
      }
    }

    // 根据合并单元格设置裁剪区域
    canvas.save();
    canvas.clipRect(0, 0, this.getWidth(), this.getHeight());
    int fixMergedIndex = 0;
    for (Cell cell : mergedCells) {

      int tx = offX, ty = offY;
      if (cell.fixedX) tx = 0;
      if (cell.fixedY) ty = 0;
      int x0 = tx + cellX[cell.col];
      int y0 = ty + cellY[cell.row];
      int x1 = tx + cellX[cell.col + cell.width];
      int y1 = ty + cellY[cell.row + cell.height];

      if (x0 > this.getWidth() || y0 > this.getHeight()) continue;
      if (x1 < 0 || y1 < 0) continue;
      canvas.clipRect(x0 + 1, y0 + 1, x1 - 1, y1 - 1, Op.DIFFERENCE);
      if (cell.fixedX || cell.fixedY) {
        if (fixMergedIndex >= fixMerged.size()) fixMerged.add(new Rect(0, 0, -1, -1));
        Rect r = fixMerged.get(fixMergedIndex);
        r.set(x0 + 1, y0 + 1, x1 - 1, y1 - 1);
        fixMergedIndex++;
      }
    }

    if (fixedRow > 0 && fixedRow < cellY.length) {
      if (Build.VERSION.SDK_INT > 11) // 2.3个别版本使用Op.UNION后会导致绘制到上层View
      canvas.clipRect(0, 0, this.getWidth(), cellY[fixedRow], Op.UNION);
    }
    if (fixedCol > 0 && fixedCol < cellX.length) {
      if (Build.VERSION.SDK_INT > 11) // 2.3个别版本使用Op.UNION后会导致绘制到上层View
      canvas.clipRect(0, 0, cellX[fixedCol], this.getHeight(), Op.UNION);
    }
    for (int i = 0; i < fixMergedIndex; i++) {
      Rect r = fixMerged.get(i);
      if (r.isEmpty() || r.left > this.getWidth() || r.top > this.getHeight()) continue;
      if (r.right < 0 || r.bottom < 0) continue;
      canvas.clipRect(r.left, r.top, r.right, r.bottom, Op.DIFFERENCE);
    }

    painter.setStrokeWidth(1);
    int tempFirst = -1;
    int tempLast = rowHeight.length;
    if (this.horizontalDivideLineColor != null) painter.setColor(horizontalDivideLineColor);

    // 绘制分割线
    for (int i = 0; i < cellY.length; i++) {
      if (offY + cellY[i] >= 0 && tempFirst == -1) // 记录第一个可见行
      tempFirst = i;
      if (i <= this.fixedRow) {
        if (horizontalDivideLineColor != null && horizontalDivideLineColor != Color.TRANSPARENT)
          canvas.drawLine(offX, cellY[i], offX + worldWidth, cellY[i], painter);
      } else {
        int minY = 0;
        if (this.fixedRow > 0) minY = cellY[fixedRow];
        if (offY + cellY[i] < minY) continue;
        if (offY + cellY[i] > this.getHeight()) break;
        tempLast = i; // 记录最后一个可见行
        if (horizontalDivideLineColor != null && horizontalDivideLineColor != Color.TRANSPARENT)
          canvas.drawLine(offX, offY + cellY[i], offX + worldWidth, offY + cellY[i], painter);
      }
    }
    this.firstVisibleRow = tempFirst;
    this.lastVisibleRow = tempLast;
    tempFirst = -1;
    tempLast = this.colWidth.length - 1;
    if (this.verticalDivideLineColor != null) painter.setColor(verticalDivideLineColor);
    for (int i = 0; i < cellX.length; i++) {
      if (offX + cellX[i] >= 0 && tempFirst == -1) tempFirst = i;
      if (i <= this.fixedCol) {
        if (this.verticalDivideLineColor != null && verticalDivideLineColor != Color.TRANSPARENT)
          canvas.drawLine(cellX[i], offY, cellX[i], offY + worldHeight, painter);
      } else {
        int minX = 0;
        if (this.fixedCol > 0) minX = cellX[fixedCol];
        if (offX + cellX[i] < minX) continue;
        if (offX + cellX[i] > this.getWidth()) break;
        tempLast = i;
        if (verticalDivideLineColor != null && verticalDivideLineColor != Color.TRANSPARENT)
          canvas.drawLine(offX + cellX[i], offY, offX + cellX[i], offY + worldHeight, painter);
      }
    }
    this.firstVisibleCol = tempFirst;
    this.lastVisibleCol = tempLast;
    canvas.restore();
  }
Beispiel #22
0
  private void fixCurrentRectBounds() {
    if (mCurrentRect == null || mCurrentRect.isEmpty()) return;

    if (mCurrentRect.left < 0) mCurrentRect.offset(-mCurrentRect.left, 0);
    if (mCurrentRect.top < 0) mCurrentRect.offset(0, -mCurrentRect.top);
  }
 // 是否需要开启动画
 public boolean isNeedAnimation() {
   return !normal.isEmpty();
 }