Android Open Source - HoloCircleSeekBar Holo Circle Seek Bar






From Project

Back to project page HoloCircleSeekBar.

License

The source code is released under:

Apache License

If you think the Android project HoloCircleSeekBar listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 * Copyright 2012 Lars Werkman/*from   www  . j a  v  a2 s  .c om*/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.jesusm.holocircleseekbar;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Paint.Style;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.SweepGradient;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

/**
 * Displays a holo-themed circular seek bar.
 *
 */
public class HoloCircleSeekBar extends View {
  /*
   * Constants used to save/restore the instance state.
   */
  private static final String STATE_PARENT = "parent";
  private static final String STATE_ANGLE = "angle";

  private OnCircleSeekBarChangeListener mOnCircleSeekBarChangeListener;

  /**
   * {@code Paint} instance used to draw the color wheel.
   */
  private Paint mColorWheelPaint;

  /**
   * {@code Paint} instance used to draw the pointer's "halo".
   */
  private Paint mPointerHaloPaint;

  /**
   * {@code Paint} instance used to draw the pointer (the selected color).
   */
  private Paint mPointerColor;

  /**
   * The stroke width used to paint the color wheel (in pixels).
   */
  private int mColorWheelStrokeWidth;

  /**
   * The radius of the pointer (in pixels).
   */
  private int mPointerRadius;

  /**
   * The rectangle enclosing the color wheel.
   */
  private RectF mColorWheelRectangle = new RectF();

  /**
   * {@code true} if the user clicked on the pointer to start the move mode.
   * {@code false} once the user stops touching the screen.
   * 
   * @see #onTouchEvent(MotionEvent)
   */
  private boolean mUserIsMovingPointer = false;

  /**
   * The ARGB value of the currently selected color.
   */
  private int mColor;

  /**
   * Number of pixels the origin of this view is moved in X- and Y-direction.
   * 
   * <p>
   * We use the center of this (quadratic) View as origin of our internal
   * coordinate system. Android uses the upper left corner as origin for the
   * View-specific coordinate system. So this is the value we use to translate
   * from one coordinate system to the other.
   * </p>
   * 
   * <p>
   * Note: (Re)calculated in {@link #onMeasure(int, int)}.
   * </p>
   * 
   * @see #onDraw(Canvas)
   */
  private float mTranslationOffset;

  /**
   * Radius of the color wheel in pixels.
   * 
   * <p>
   * Note: (Re)calculated in {@link #onMeasure(int, int)}.
   * </p>
   */
  private float mColorWheelRadius;

  /**
   * The pointer's position expressed as angle (in rad).
   */
  private float mAngle;
  private Paint textPaint;
  private String text;
  private int conversion = 0;
  private int max = 100;
  private SweepGradient s;
  private Paint mArcColor;
  private String wheel_color_attr, wheel_unactive_color_attr,
      pointer_color_attr, pointer_halo_color_attr, text_color_attr;
  private int wheel_color, unactive_wheel_color, pointer_color,
      pointer_halo_color, text_size, text_color, init_position;
  private boolean block_end = false;
  private float lastX;
  private int last_radians = 0;
  private boolean block_start = false;

  private int arc_finish_radians = 360;
  private int start_arc = 270;

  private float[] pointerPosition;
  private Paint mColorCenterHalo;
  private RectF mColorCenterHaloRectangle = new RectF();
  private Paint mCircleTextColor;
  private int end_wheel;

  private boolean show_text = true;

  public HoloCircleSeekBar(Context context) {
    super(context);
    init(null, 0);
  }

  public HoloCircleSeekBar(Context context, AttributeSet attrs) {
    super(context, attrs);
    init(attrs, 0);
  }

  public HoloCircleSeekBar(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    init(attrs, defStyle);
  }

  private void init(AttributeSet attrs, int defStyle) {
    final TypedArray a = getContext().obtainStyledAttributes(attrs,
        R.styleable.HoloCircleSeekBar, defStyle, 0);

    initAttributes(a);

    a.recycle();
    // mAngle = (float) (-Math.PI / 2);

    mColorWheelPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mColorWheelPaint.setShader(s);
    mColorWheelPaint.setColor(unactive_wheel_color);
    mColorWheelPaint.setStyle(Paint.Style.STROKE);
    mColorWheelPaint.setStrokeWidth(mColorWheelStrokeWidth);

    mColorCenterHalo = new Paint(Paint.ANTI_ALIAS_FLAG);
    mColorCenterHalo.setColor(Color.CYAN);
    mColorCenterHalo.setAlpha(0xCC);
    // mColorCenterHalo.setStyle(Paint.Style.STROKE);
    // mColorCenterHalo.setStrokeWidth(mColorCenterHaloRectangle.width() /
    // 2);

    mPointerHaloPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPointerHaloPaint.setColor(pointer_halo_color);
    mPointerHaloPaint.setStrokeWidth(mPointerRadius + 10);
    // mPointerHaloPaint.setAlpha(150);

    textPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.LINEAR_TEXT_FLAG);
    textPaint.setColor(text_color);
    textPaint.setStyle(Style.FILL_AND_STROKE);
    textPaint.setTextAlign(Align.LEFT);
    // canvas.drawPaint(textPaint);
    textPaint.setTextSize(text_size);

    mPointerColor = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPointerColor.setStrokeWidth(mPointerRadius);

    // mPointerColor.setColor(calculateColor(mAngle));
    mPointerColor.setColor(pointer_color);

    mArcColor = new Paint(Paint.ANTI_ALIAS_FLAG);
    mArcColor.setColor(wheel_color);
    mArcColor.setStyle(Paint.Style.STROKE);
    mArcColor.setStrokeWidth(mColorWheelStrokeWidth);

    mCircleTextColor = new Paint(Paint.ANTI_ALIAS_FLAG);
    mCircleTextColor.setColor(Color.WHITE);
    mCircleTextColor.setStyle(Paint.Style.FILL);

    arc_finish_radians = (int) calculateAngleFromText(init_position) - 90;

    if (arc_finish_radians > end_wheel)
      arc_finish_radians = end_wheel;
    mAngle = calculateAngleFromRadians(arc_finish_radians > end_wheel ? end_wheel
        : arc_finish_radians);
    text = String.valueOf(calculateTextFromAngle(arc_finish_radians));

    invalidate();
  }

  private void initAttributes(TypedArray a) {
    mColorWheelStrokeWidth = a.getInteger(
        R.styleable.HoloCircleSeekBar_wheel_size, 16);
    mPointerRadius = a.getInteger(
        R.styleable.HoloCircleSeekBar_pointer_size, 48);
    max = a.getInteger(R.styleable.HoloCircleSeekBar_max, 100);

    wheel_color_attr = a
        .getString(R.styleable.HoloCircleSeekBar_wheel_active_color);
    wheel_unactive_color_attr = a
        .getString(R.styleable.HoloCircleSeekBar_wheel_unactive_color);
    pointer_color_attr = a
        .getString(R.styleable.HoloCircleSeekBar_pointer_color);
    pointer_halo_color_attr = a
        .getString(R.styleable.HoloCircleSeekBar_pointer_halo_color);

    text_color_attr = a.getString(R.styleable.HoloCircleSeekBar_text_color);

    text_size = a.getInteger(R.styleable.HoloCircleSeekBar_text_size, 95);

    init_position = a.getInteger(
        R.styleable.HoloCircleSeekBar_init_position, 0);

    start_arc = a.getInteger(R.styleable.HoloCircleSeekBar_start_angle, 0);
    end_wheel = a.getInteger(R.styleable.HoloCircleSeekBar_end_angle, 360);

    show_text = a.getBoolean(R.styleable.HoloCircleSeekBar_show_text, true);

    last_radians = end_wheel;

    if (init_position < start_arc)
      init_position = calculateTextFromStartAngle(start_arc);

    // mAngle = (float) calculateAngleFromText(init_position);

    if (wheel_color_attr != null) {
      try {
        wheel_color = Color.parseColor(wheel_color_attr);
      } catch (IllegalArgumentException e) {
        wheel_color = Color.DKGRAY;
      }

    } else {
      wheel_color = Color.DKGRAY;
    }
    if (wheel_unactive_color_attr != null) {
      try {
        unactive_wheel_color = Color
            .parseColor(wheel_unactive_color_attr);
      } catch (IllegalArgumentException e) {
        unactive_wheel_color = Color.CYAN;
      }

    } else {
      unactive_wheel_color = Color.CYAN;
    }

    if (pointer_color_attr != null) {
      try {
        pointer_color = Color.parseColor(pointer_color_attr);
      } catch (IllegalArgumentException e) {
        pointer_color = Color.CYAN;
      }

    } else {
      pointer_color = Color.CYAN;
    }

    if (pointer_halo_color_attr != null) {
      try {
        pointer_halo_color = Color.parseColor(pointer_halo_color_attr);
      } catch (IllegalArgumentException e) {
        pointer_halo_color = Color.CYAN;
      }

    } else {
      pointer_halo_color = Color.DKGRAY;
    }

    if (text_color_attr != null) {
      try {
        text_color = Color.parseColor(text_color_attr);
      } catch (IllegalArgumentException e) {
        text_color = Color.CYAN;
      }
    } else {
      text_color = Color.CYAN;
    }

  }

  @Override
  protected void onDraw(Canvas canvas) {
    // All of our positions are using our internal coordinate system.
    // Instead of translating
    // them we let Canvas do the work for us.

    canvas.translate(mTranslationOffset, mTranslationOffset);

    // Draw the color wheel.
    // canvas.drawOval(mColorWheelRectangle, mColorWheelPaint);
    canvas.drawArc(mColorWheelRectangle, start_arc + 270, end_wheel
        - (start_arc), false, mColorWheelPaint);

    canvas.drawArc(mColorWheelRectangle, start_arc + 270,
        (arc_finish_radians) > (end_wheel) ? end_wheel - (start_arc)
            : arc_finish_radians - start_arc, false, mArcColor);

    // canvas.drawArc(mColorCenterHaloRectangle, 270, arc_finish_radians,
    // false, mColorCenterHalo);

    // Draw the pointer's "halo"
    canvas.drawCircle(pointerPosition[0], pointerPosition[1],
        mPointerRadius, mPointerHaloPaint);

    // Draw the pointer (the currently selected color) slightly smaller on
    // top.

    canvas.drawCircle(pointerPosition[0], pointerPosition[1],
        (float) (mPointerRadius / 1.2), mPointerColor);
    Rect bounds = new Rect();
    textPaint.getTextBounds(text, 0, text.length(), bounds);
    // canvas.drawCircle(mColorWheelRectangle.centerX(),
    // mColorWheelRectangle.centerY(), (bounds.width() / 2) + 5,
    // mCircleTextColor);
    if (show_text)
      canvas.drawText(
          text,
          (mColorWheelRectangle.centerX())
              - (textPaint.measureText(text) / 2),
          mColorWheelRectangle.centerY() + bounds.height() / 2,
          textPaint);

    // last_radians = calculateRadiansFromAngle(mAngle);

  }

  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int height = getDefaultSize(getSuggestedMinimumHeight(),
        heightMeasureSpec);
    int width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
    int min = Math.min(width, height);
    setMeasuredDimension(min, min);

    mTranslationOffset = min * 0.5f;
    mColorWheelRadius = mTranslationOffset - mPointerRadius;

    mColorWheelRectangle.set(-mColorWheelRadius, -mColorWheelRadius,
        mColorWheelRadius, mColorWheelRadius);

    mColorCenterHaloRectangle.set(-mColorWheelRadius / 2,
        -mColorWheelRadius / 2, mColorWheelRadius / 2,
        mColorWheelRadius / 2);

    pointerPosition = calculatePointerPosition(mAngle);

  }

  private int calculateTextFromAngle(float angle) {
    float m = angle - start_arc;

    float f = (float) ((end_wheel - start_arc) / m);

    return (int) (max / f);
  }

  private int calculateTextFromStartAngle(float angle) {
    float m = angle;

    float f = (float) ((end_wheel - start_arc) / m);

    return (int) (max / f);
  }

  private double calculateAngleFromText(int position) {
    if (position == 0 || position >= max)
      return (float) 90;

    double f = (double) max / (double) position;

    double f_r = 360 / f;

    double ang = f_r + 90;

    return ang;

  }

  private int calculateRadiansFromAngle(float angle) {
    float unit = (float) (angle / (2 * Math.PI));
    if (unit < 0) {
      unit += 1;
    }
    int radians = (int) ((unit * 360) - ((360 / 4) * 3));
    if (radians < 0)
      radians += 360;
    return radians;
  }

  private float calculateAngleFromRadians(int radians) {
    return (float) (((radians + 270) * (2 * Math.PI)) / 360);
  }

  /**
   * Get the selected value
   * 
   * @return the value between 0 and max
   */
  public int getValue() {
    return conversion;
  }

  @Override
  public boolean onTouchEvent(MotionEvent event) {
    // Convert coordinates to our internal coordinate system
    float x = event.getX() - mTranslationOffset;
    float y = event.getY() - mTranslationOffset;

    switch (event.getAction()) {
    case MotionEvent.ACTION_DOWN:
      // Check whether the user pressed on (or near) the pointer
      mAngle = (float) java.lang.Math.atan2(y, x);

      block_end = false;
      block_start = false;
      mUserIsMovingPointer = true;

      arc_finish_radians = calculateRadiansFromAngle(mAngle);

      if (arc_finish_radians > end_wheel) {
        arc_finish_radians = end_wheel;
        block_end = true;
      }

      if (!block_end && !block_start) {
        text = String
            .valueOf(calculateTextFromAngle(arc_finish_radians));
        pointerPosition = calculatePointerPosition(mAngle);
        invalidate();
      }
      break;
    case MotionEvent.ACTION_MOVE:
      if (mUserIsMovingPointer) {
        mAngle = (float) java.lang.Math.atan2(y, x);

        int radians = calculateRadiansFromAngle(mAngle);

        if (last_radians > radians && radians < (360 / 6) && x > lastX
            && last_radians > (360 / 6)) {

          if (!block_end && !block_start)
            block_end = true;
          // if (block_start)
          // block_start = false;
        } else if (last_radians >= start_arc
            && last_radians <= (360 / 4) && radians <= (360 - 1)
            && radians >= ((360 / 4) * 3) && x < lastX) {
          if (!block_start && !block_end)
            block_start = true;
          // if (block_end)
          // block_end = false;

        } else if (radians >= end_wheel && !block_start
            && last_radians < radians) {
          block_end = true;
        } else if (radians < end_wheel && block_end
            && last_radians > end_wheel) {
          block_end = false;
        } else if (radians < start_arc && last_radians > radians
            && !block_end) {
          block_start = true;
        } else if (block_start && last_radians < radians
            && radians > start_arc && radians < end_wheel) {
          block_start = false;
        }

        if (block_end) {

          arc_finish_radians = end_wheel - 1;
          text = String.valueOf(max);
          mAngle = calculateAngleFromRadians(arc_finish_radians);
          pointerPosition = calculatePointerPosition(mAngle);
        } else if (block_start) {

          arc_finish_radians = start_arc;
          mAngle = calculateAngleFromRadians(arc_finish_radians);
          text = String.valueOf(0);
          pointerPosition = calculatePointerPosition(mAngle);
        } else {
          // text = String.valueOf(calculateTextFromAngle(mAngle));
          arc_finish_radians = calculateRadiansFromAngle(mAngle);
          text = String
              .valueOf(calculateTextFromAngle(arc_finish_radians));
          pointerPosition = calculatePointerPosition(mAngle);
        }
        invalidate();
        if (mOnCircleSeekBarChangeListener != null)
          mOnCircleSeekBarChangeListener.onProgressChanged(this,
              Integer.parseInt(text), true);

        last_radians = radians;

      }
      break;
    case MotionEvent.ACTION_UP:
      mUserIsMovingPointer = false;
      break;
    }
    // Fix scrolling
    if (event.getAction() == MotionEvent.ACTION_MOVE && getParent() != null) {
      getParent().requestDisallowInterceptTouchEvent(true);
    }
    lastX = x;

    return true;
  }

  /**
   * Calculate the pointer's coordinates on the color wheel using the supplied
   * angle.
   * 
   * @param angle
   *            The position of the pointer expressed as angle (in rad).
   * 
   * @return The coordinates of the pointer's center in our internal
   *         coordinate system.
   */
  private float[] calculatePointerPosition(float angle) {
    // if (calculateRadiansFromAngle(angle) > end_wheel)
    // angle = calculateAngleFromRadians(end_wheel);
    float x = (float) (mColorWheelRadius * Math.cos(angle));
    float y = (float) (mColorWheelRadius * Math.sin(angle));

    return new float[] { x, y };
  }

  @Override
  protected Parcelable onSaveInstanceState() {
    Parcelable superState = super.onSaveInstanceState();

    Bundle state = new Bundle();
    state.putParcelable(STATE_PARENT, superState);
    state.putFloat(STATE_ANGLE, mAngle);

    return state;
  }

  @Override
  protected void onRestoreInstanceState(Parcelable state) {
    Bundle savedState = (Bundle) state;

    Parcelable superState = savedState.getParcelable(STATE_PARENT);
    super.onRestoreInstanceState(superState);

    mAngle = savedState.getFloat(STATE_ANGLE);
    arc_finish_radians = calculateRadiansFromAngle(mAngle);
    text = String.valueOf(calculateTextFromAngle(arc_finish_radians));
    pointerPosition = calculatePointerPosition(mAngle);

    // mPointerColor.setColor(pointer_color);
  }

  public void setOnSeekBarChangeListener(OnCircleSeekBarChangeListener l) {
    mOnCircleSeekBarChangeListener = l;
  }

  public interface OnCircleSeekBarChangeListener {

    public abstract void onProgressChanged(HoloCircleSeekBar seekBar,
                                               int progress, boolean fromUser);

  }

}




Java Source Code List

com.jesusm.holocircleseekbar.HoloCircleSeekBar.java