@Override
  protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    int thumbWidth = mThumb.getIntrinsicWidth();
    int thumbHeight = mThumb.getIntrinsicHeight();
    int addedThumb = mAddedTouchBounds;
    int halfThumb = thumbWidth / 2;
    int paddingLeft = getPaddingLeft() + addedThumb;
    int paddingRight = getPaddingRight();
    int bottom = getHeight() - getPaddingBottom() - addedThumb;
    mThumb.setBounds(paddingLeft, bottom - thumbHeight, paddingLeft + thumbWidth, bottom);
    int trackHeight = Math.max(mTrackHeight / 2, 1);
    mTrack.setBounds(
        paddingLeft + halfThumb,
        bottom - halfThumb - trackHeight,
        getWidth() - halfThumb - paddingRight - addedThumb,
        bottom - halfThumb + trackHeight);
    int scrubberHeight = Math.max(mScrubberHeight / 2, 2);
    mScrubber.setBounds(
        paddingLeft + halfThumb,
        bottom - halfThumb - scrubberHeight,
        paddingLeft + halfThumb,
        bottom - halfThumb + scrubberHeight);

    // Update the thumb position after size changed
    updateThumbPosFromCurrentProgress();
  }
  private void updateThumbPos(int posX) {
    int thumbWidth = mThumb.getIntrinsicWidth();
    int halfThumb = thumbWidth / 2;
    int start;
    if (isRtl()) {
      start = getWidth() - getPaddingRight() - mAddedTouchBounds;
      posX = start - posX - thumbWidth;
    } else {
      start = getPaddingLeft() + mAddedTouchBounds;
      posX = start + posX;
    }
    mThumb.copyBounds(mInvalidateRect);
    mThumb.setBounds(posX, mInvalidateRect.top, posX + thumbWidth, mInvalidateRect.bottom);
    if (isRtl()) {
      mScrubber.getBounds().right = start - halfThumb;
      mScrubber.getBounds().left = posX + halfThumb;
    } else {
      mScrubber.getBounds().left = start + halfThumb;
      mScrubber.getBounds().right = posX + halfThumb;
    }
    final Rect finalBounds = mTempRect;
    mThumb.copyBounds(finalBounds);
    if (!isInEditMode()) {
      mIndicator.move(finalBounds.centerX());
    }

    mInvalidateRect.inset(-mAddedTouchBounds, -mAddedTouchBounds);
    finalBounds.inset(-mAddedTouchBounds, -mAddedTouchBounds);
    mInvalidateRect.union(finalBounds);
    SeekBarCompat.setHotspotBounds(
        mRipple, finalBounds.left, finalBounds.top, finalBounds.right, finalBounds.bottom);
    invalidate(mInvalidateRect);
  }
 @Override
 protected synchronized void onDraw(Canvas canvas) {
   if (!isLollipopOrGreater) {
     mRipple.draw(canvas);
   }
   super.onDraw(canvas);
   mTrack.draw(canvas);
   mScrubber.draw(canvas);
   mThumb.draw(canvas);
 }
 private void updateFromDrawableState() {
   int[] state = getDrawableState();
   boolean focused = false;
   boolean pressed = false;
   for (int i : state) {
     if (i == FOCUSED_STATE) {
       focused = true;
     } else if (i == PRESSED_STATE) {
       pressed = true;
     }
   }
   if (isEnabled() && (focused || pressed) && mIndicatorPopupEnabled) {
     // We want to add a small delay here to avoid
     // poping in/out on simple taps
     removeCallbacks(mShowIndicatorRunnable);
     postDelayed(mShowIndicatorRunnable, INDICATOR_DELAY_FOR_TAPS);
   } else {
     hideFloater();
   }
   mThumb.setState(state);
   mTrack.setState(state);
   mScrubber.setState(state);
   mRipple.setState(state);
 }
 /**
  * Sets the color of the seekbar scrubber
  *
  * @param colorStateList The ColorStateList the track will be changed to
  */
 public void setTrackColor(@NonNull ColorStateList colorStateList) {
   mTrack.setColorStateList(colorStateList);
 }
 /**
  * Sets the color of the seekbar scrubber
  *
  * @param color The color the track will be changed to
  */
 public void setTrackColor(int color) {
   mTrack.setColorStateList(ColorStateList.valueOf(color));
 }
 /**
  * Sets the color of the seekbar scrubber
  *
  * @param colorStateList The ColorStateList the track scrubber will be changed to
  */
 public void setScrubberColor(@NonNull ColorStateList colorStateList) {
   mScrubber.setColorStateList(colorStateList);
 }
 /**
  * Sets the color of the seekbar scrubber
  *
  * @param color The color the track scrubber will be changed to
  */
 public void setScrubberColor(int color) {
   mScrubber.setColorStateList(ColorStateList.valueOf(color));
 }
  public DiscreteSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    setFocusable(true);
    setWillNotDraw(false);

    mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
    float density = context.getResources().getDisplayMetrics().density;
    mTrackHeight = (int) (1 * density);
    mScrubberHeight = (int) (4 * density);
    int thumbSize = (int) (density * ThumbDrawable.DEFAULT_SIZE_DP);

    // Extra pixels for a touch area of 48dp
    int touchBounds = (int) (density * 32);
    mAddedTouchBounds = (touchBounds - thumbSize) / 2;

    TypedArray a =
        context.obtainStyledAttributes(
            attrs, R.styleable.DiscreteSeekBar, defStyleAttr, R.style.Widget_DiscreteSeekBar);

    int max = 100;
    int min = 0;
    int value = 0;
    mMirrorForRtl = a.getBoolean(R.styleable.DiscreteSeekBar_dsb_mirrorForRtl, mMirrorForRtl);
    mAllowTrackClick =
        a.getBoolean(R.styleable.DiscreteSeekBar_dsb_allowTrackClickToDrag, mAllowTrackClick);
    mIndicatorPopupEnabled =
        a.getBoolean(R.styleable.DiscreteSeekBar_dsb_indicatorPopupEnabled, mIndicatorPopupEnabled);
    int indexMax = R.styleable.DiscreteSeekBar_dsb_max;
    int indexMin = R.styleable.DiscreteSeekBar_dsb_min;
    int indexValue = R.styleable.DiscreteSeekBar_dsb_value;
    final TypedValue out = new TypedValue();
    // Not sure why, but we wanted to be able to use dimensions here...
    if (a.getValue(indexMax, out)) {
      if (out.type == TypedValue.TYPE_DIMENSION) {
        max = a.getDimensionPixelSize(indexMax, max);
      } else {
        max = a.getInteger(indexMax, max);
      }
    }
    if (a.getValue(indexMin, out)) {
      if (out.type == TypedValue.TYPE_DIMENSION) {
        min = a.getDimensionPixelSize(indexMin, min);
      } else {
        min = a.getInteger(indexMin, min);
      }
    }
    if (a.getValue(indexValue, out)) {
      if (out.type == TypedValue.TYPE_DIMENSION) {
        value = a.getDimensionPixelSize(indexValue, value);
      } else {
        value = a.getInteger(indexValue, value);
      }
    }

    mMin = min;
    mMax = Math.max(min + 1, max);
    mValue = Math.max(min, Math.min(max, value));
    updateKeyboardRange();

    mIndicatorFormatter = a.getString(R.styleable.DiscreteSeekBar_dsb_indicatorFormatter);

    ColorStateList trackColor = a.getColorStateList(R.styleable.DiscreteSeekBar_dsb_trackColor);
    ColorStateList progressColor =
        a.getColorStateList(R.styleable.DiscreteSeekBar_dsb_progressColor);
    ColorStateList rippleColor = a.getColorStateList(R.styleable.DiscreteSeekBar_dsb_rippleColor);
    boolean editMode = isInEditMode();
    if (editMode || rippleColor == null) {
      rippleColor = new ColorStateList(new int[][] {new int[] {}}, new int[] {Color.DKGRAY});
    }
    if (editMode || trackColor == null) {
      trackColor = new ColorStateList(new int[][] {new int[] {}}, new int[] {Color.GRAY});
    }
    if (editMode || progressColor == null) {
      progressColor =
          new ColorStateList(new int[][] {new int[] {}}, new int[] {DEFAULT_THUMB_COLOR});
    }
    mRipple = SeekBarCompat.getRipple(rippleColor);
    if (isLollipopOrGreater) {
      SeekBarCompat.setBackground(this, mRipple);
    } else {
      mRipple.setCallback(this);
    }

    TrackRectDrawable shapeDrawable = new TrackRectDrawable(trackColor);
    mTrack = shapeDrawable;
    mTrack.setCallback(this);

    shapeDrawable = new TrackRectDrawable(progressColor);
    mScrubber = shapeDrawable;
    mScrubber.setCallback(this);

    mThumb = new ThumbDrawable(progressColor, thumbSize);
    mThumb.setCallback(this);
    mThumb.setBounds(0, 0, mThumb.getIntrinsicWidth(), mThumb.getIntrinsicHeight());

    if (!editMode) {
      mIndicator = new PopupIndicator(context, attrs, defStyleAttr, convertValueToMessage(mMax));
      mIndicator.setListener(mFloaterListener);
    }
    a.recycle();

    setNumericTransformer(new DefaultNumericTransformer());
  }