Exemple #1
0
  /**
   * Set the progress of the CircularSeekBar. If the progress is the same, then any listener will
   * not receive a onProgressChanged event.
   *
   * @param progress The progress to set the CircularSeekBar to.
   */
  public void setProgress(int progress) {
    if (mProgress != progress) {
      mProgress = progress;
      if (mOnCircularSeekBarChangeListener != null) {
        mOnCircularSeekBarChangeListener.onProgressChanged(this, progress, false);
      }

      recalculateAll();
      invalidate();
    }
  }
Exemple #2
0
  /**
   * Set the max of the CircularSeekBar. If the new max is less than the current progress, then the
   * progress will be set to zero. If the progress is changed as a result, then any listener will
   * receive a onProgressChanged event.
   *
   * @param max The new max for the CircularSeekBar.
   */
  public void setMax(int max) {
    if (!(max <= 0)) { // Check to make sure it's greater than zero
      if (max <= mProgress) {
        mProgress = 0; // If the new max is less than current progress, set progress to zero
        if (mOnCircularSeekBarChangeListener != null) {
          mOnCircularSeekBarChangeListener.onProgressChanged(this, mProgress, false);
        }
      }
      mMax = max;

      recalculateAll();
      invalidate();
    }
  }
Exemple #3
0
  @Override
  public boolean onTouchEvent(MotionEvent event) {
    if (!isTouchEnabled) {
      return false;
    }

    // Convert coordinates to our internal coordinate system
    float x = event.getX() - getWidth() / 2;
    float y = event.getY() - getHeight() / 2;

    // Get the distance from the center of the circle in terms of x and y
    float distanceX = mCircleRectF.centerX() - x;
    float distanceY = mCircleRectF.centerY() - y;

    // Get the distance from the center of the circle in terms of a radius
    float touchEventRadius = (float) Math.sqrt((Math.pow(distanceX, 2) + Math.pow(distanceY, 2)));

    float minimumTouchTarget =
        MIN_TOUCH_TARGET_DP * DPTOPX_SCALE; // Convert minimum touch target into px
    float additionalRadius; // Either uses the minimumTouchTarget size or larger if the ring/pointer
    // is larger

    if (mCircleStrokeWidth
        < minimumTouchTarget) { // If the width is less than the minimumTouchTarget, use the
      // minimumTouchTarget
      additionalRadius = minimumTouchTarget / 2;
    } else {
      additionalRadius = mCircleStrokeWidth / 2; // Otherwise use the width
    }
    float outerRadius =
        Math.max(mCircleHeight, mCircleWidth)
            + additionalRadius; // Max outer radius of the circle, including the minimumTouchTarget
    // or wheel width
    float innerRadius =
        Math.min(mCircleHeight, mCircleWidth)
            - additionalRadius; // Min inner radius of the circle, including the minimumTouchTarget
    // or wheel width

    if (mPointerRadius
        < (minimumTouchTarget
            / 2)) { // If the pointer radius is less than the minimumTouchTarget, use the
      // minimumTouchTarget
      additionalRadius = minimumTouchTarget / 2;
    } else {
      additionalRadius = mPointerRadius; // Otherwise use the radius
    }

    float touchAngle;
    touchAngle = (float) ((Math.atan2(y, x) / Math.PI * 180) % 360); // Verified
    touchAngle = (touchAngle < 0 ? 360 + touchAngle : touchAngle); // Verified

    cwDistanceFromStart = touchAngle - mStartAngle; // Verified
    cwDistanceFromStart =
        (cwDistanceFromStart < 0 ? 360f + cwDistanceFromStart : cwDistanceFromStart); // Verified
    ccwDistanceFromStart = 360f - cwDistanceFromStart; // Verified

    cwDistanceFromEnd = touchAngle - mEndAngle; // Verified
    cwDistanceFromEnd =
        (cwDistanceFromEnd < 0 ? 360f + cwDistanceFromEnd : cwDistanceFromEnd); // Verified
    ccwDistanceFromEnd = 360f - cwDistanceFromEnd; // Verified

    switch (event.getAction()) {
      case MotionEvent.ACTION_DOWN:
        // These are only used for ACTION_DOWN for handling if the pointer was the part that was
        // touched
        float pointerRadiusDegrees =
            (float) ((mPointerRadius * 180) / (Math.PI * Math.max(mCircleHeight, mCircleWidth)));
        cwDistanceFromPointer = touchAngle - mPointerPosition;
        cwDistanceFromPointer =
            (cwDistanceFromPointer < 0 ? 360f + cwDistanceFromPointer : cwDistanceFromPointer);
        ccwDistanceFromPointer = 360f - cwDistanceFromPointer;
        // This is for if the first touch is on the actual pointer.
        if (((touchEventRadius >= innerRadius) && (touchEventRadius <= outerRadius))
            && ((cwDistanceFromPointer <= pointerRadiusDegrees)
                || (ccwDistanceFromPointer <= pointerRadiusDegrees))) {
          setProgressBasedOnAngle(mPointerPosition);
          lastCWDistanceFromStart = cwDistanceFromStart;
          mIsMovingCW = true;
          mPointerHaloPaint.setAlpha(mPointerAlphaOnTouch);
          mPointerHaloPaint.setColor(mPointerHaloColorOnTouch);
          recalculateAll();
          invalidate();
          if (mOnCircularSeekBarChangeListener != null) {
            mOnCircularSeekBarChangeListener.onStartTrackingTouch(this);
          }
          mUserIsMovingPointer = true;
          lockAtEnd = false;
          lockAtStart = false;
        } else if (cwDistanceFromStart
            > mTotalCircleDegrees) { // If the user is touching outside of the start AND end
          mUserIsMovingPointer = false;
          return false;
        } else if ((touchEventRadius >= innerRadius)
            && (touchEventRadius <= outerRadius)) { // If the user is touching near the circle
          setProgressBasedOnAngle(touchAngle);
          lastCWDistanceFromStart = cwDistanceFromStart;
          mIsMovingCW = true;
          mPointerHaloPaint.setAlpha(mPointerAlphaOnTouch);
          mPointerHaloPaint.setColor(mPointerHaloColorOnTouch);
          recalculateAll();
          invalidate();
          if (mOnCircularSeekBarChangeListener != null) {
            mOnCircularSeekBarChangeListener.onStartTrackingTouch(this);
            mOnCircularSeekBarChangeListener.onProgressChanged(this, mProgress, true);
          }
          mUserIsMovingPointer = true;
          lockAtEnd = false;
          lockAtStart = false;
        } else { // If the user is not touching near the circle
          mUserIsMovingPointer = false;
          return false;
        }
        break;
      case MotionEvent.ACTION_MOVE:
        if (mUserIsMovingPointer) {
          if (lastCWDistanceFromStart < cwDistanceFromStart) {
            if ((cwDistanceFromStart - lastCWDistanceFromStart) > 180f && !mIsMovingCW) {
              lockAtStart = true;
              lockAtEnd = false;
            } else {
              mIsMovingCW = true;
            }
          } else {
            if ((lastCWDistanceFromStart - cwDistanceFromStart) > 180f && mIsMovingCW) {
              lockAtEnd = true;
              lockAtStart = false;
            } else {
              mIsMovingCW = false;
            }
          }

          if (lockAtStart && mIsMovingCW) {
            lockAtStart = false;
          }
          if (lockAtEnd && !mIsMovingCW) {
            lockAtEnd = false;
          }
          if (lockAtStart && !mIsMovingCW && (ccwDistanceFromStart > 90)) {
            lockAtStart = false;
          }
          if (lockAtEnd && mIsMovingCW && (cwDistanceFromEnd > 90)) {
            lockAtEnd = false;
          }
          // Fix for passing the end of a semi-circle quickly
          if (!lockAtEnd
              && cwDistanceFromStart > mTotalCircleDegrees
              && mIsMovingCW
              && lastCWDistanceFromStart < mTotalCircleDegrees) {
            lockAtEnd = true;
          }

          if (lockAtStart && lockEnabled) {
            // TODO: Add a check if mProgress is already 0, in which case don't call the listener
            mProgress = 0;
            recalculateAll();
            invalidate();
            if (mOnCircularSeekBarChangeListener != null) {
              mOnCircularSeekBarChangeListener.onProgressChanged(this, mProgress, true);
            }

          } else if (lockAtEnd && lockEnabled) {
            mProgress = mMax;
            recalculateAll();
            invalidate();
            if (mOnCircularSeekBarChangeListener != null) {
              mOnCircularSeekBarChangeListener.onProgressChanged(this, mProgress, true);
            }
          } else if ((mMoveOutsideCircle) || (touchEventRadius <= outerRadius)) {
            if (!(cwDistanceFromStart > mTotalCircleDegrees)) {
              setProgressBasedOnAngle(touchAngle);
            }
            recalculateAll();
            invalidate();
            if (mOnCircularSeekBarChangeListener != null) {
              mOnCircularSeekBarChangeListener.onProgressChanged(this, mProgress, true);
            }
          } else {
            break;
          }

          lastCWDistanceFromStart = cwDistanceFromStart;
        } else {
          return false;
        }

        break;
      case MotionEvent.ACTION_UP:
        mPointerHaloPaint.setAlpha(mPointerAlpha);
        mPointerHaloPaint.setColor(mPointerHaloColor);
        if (mUserIsMovingPointer) {
          mUserIsMovingPointer = false;
          invalidate();
          if (mOnCircularSeekBarChangeListener != null) {
            mOnCircularSeekBarChangeListener.onStopTrackingTouch(this);
          }
        } else {
          return false;
        }
        break;
      case MotionEvent
          .ACTION_CANCEL: // Used when the parent view intercepts touches for things like scrolling
        mPointerHaloPaint.setAlpha(mPointerAlpha);
        mPointerHaloPaint.setColor(mPointerHaloColor);
        mUserIsMovingPointer = false;
        invalidate();
        break;
    }

    if (event.getAction() == MotionEvent.ACTION_MOVE && getParent() != null) {
      getParent().requestDisallowInterceptTouchEvent(true);
    }

    return true;
  }