Thursday, December 13, 2012

[android-developers] Re: Android circular seek bar

@sink what i have done so far is this view which create circular seek bar type now m prob is how to put image (thumb) so that which can be drag in circle to increase count and to have circle background  help needed 

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.text.format.DateFormat;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

/**
 * A slider around a circle, to select a time between now and 12 hours from now.
 */
final class ClockSlider extends View {
    /* Display modes */
    public static final int CLOCK_SLIDER = 1;
    public static final int VOLUME_SLIDER = 2;
    public static final int VIBRATE_PICKER = 3;

    public static final boolean ENABLE_VIBRATE = false;
    private static final int INSETS = 6;
    private static final int MINUTES_PER_HALF_DAY = 720;

    private RingerMutedDialog ringerMutedDialog;

    private int width;
    private int height;
    private int centerX;
    private int centerY;
    private int diameter;
    private RectF outerCircle;
    private RectF innerCircle;
    private RectF buttonCircle;
    private final Path path = new Path();

    private RectF smallVolume;
    private RectF smallVolumeTouchRegion;
    private RectF largeVolume;
    private int displayMode = CLOCK_SLIDER;
    /** Volume to restore to; between 0.0 and 1.0 */
    private float volume = 0.8f;

    /** Vibrate mode */
    private boolean vibrateNow = true;
    private boolean vibrateLater = true;
    private RectF smallVibrate;
    private RectF smallVibrateTouchRegion;

    private Paint lightGrey = new Paint();
    private Paint pink = new Paint();
    private Paint white = new Paint();
    private Paint duration = new Paint();
    private Paint durationUnits = new Paint();
    private Paint unshushTime = new Paint();
    private Paint percentPaint = new Paint();
    private Paint buttonCirclePaint = new Paint();

    private Calendar start = new GregorianCalendar();
    private int startAngle = 0;
    private Calendar end = new GregorianCalendar();

    /** minutes to shush. */
    private int minutes = 0;
    private boolean upPushed;
    private boolean downPushed;

    public ClockSlider(Context context, AttributeSet attrs) {
        super(context, attrs);

        lightGrey.setColor(Color.rgb(115, 115, 115));
        lightGrey.setAntiAlias(true);
        pink.setColor(Color.rgb(255, 0, 165));
        pink.setAntiAlias(true);
        white.setColor(Color.WHITE);
        white.setAntiAlias(true);
        duration.setTypeface(Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD));
        duration.setSubpixelText(true);
        duration.setAntiAlias(true);
        duration.setColor(Color.WHITE);
        duration.setTextAlign(Paint.Align.CENTER);
        durationUnits = new Paint(duration);
        durationUnits.setTypeface(Typeface.SANS_SERIF);
        unshushTime = new Paint(duration);
        unshushTime.setColor(lightGrey.getColor());
        percentPaint = new Paint(duration);
        percentPaint.setTextAlign(Paint.Align.LEFT);
        percentPaint.setColor(lightGrey.getColor());
        buttonCirclePaint.setColor(Color.argb(102, 115, 115, 115));
        buttonCirclePaint.setAntiAlias(true);
    }

    public void setColor(int color) {
        pink.setColor(color);
    }

    public void setRingerMutedDialog(RingerMutedDialog ringerMutedDialog) {
        this.ringerMutedDialog = ringerMutedDialog;
    }

    @Override protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        if (getWidth() != width || getHeight() != height) {
            width = getWidth();
            height = getHeight();
            centerX = width / 2;
            centerY = height / 2;

            diameter = Math.min(width, height) - (2 * INSETS);
            int thickness = diameter / 15;

            int left = (width - diameter) / 2;
            int top = (height - diameter) / 2;
            int bottom = top + diameter;
            int right = left + diameter;
            outerCircle = new RectF(left, top, right, bottom);

            int innerDiameter = diameter - thickness * 2;
            innerCircle = new RectF(left + thickness, top + thickness,
                    left + thickness + innerDiameter, top + thickness + innerDiameter);

            int offset = thickness * 2;
            int buttonDiameter = diameter - offset * 2;
            buttonCircle = new RectF(left + offset, top + offset,
                    left + offset + buttonDiameter, top + offset + buttonDiameter);

            // the large volume triangle
            int volumeLeft = Math.max(INSETS * 2, centerX - diameter);
            int volumeRight = Math.min(width - INSETS * 2, centerX + diameter);
            int volumeHeight = (volumeRight - volumeLeft) / 2;
            largeVolume = new RectF(volumeLeft, bottom - volumeHeight, volumeRight, bottom);

            // the small volume triangle fits into the large triangle's bottom left corner
            float smallVolumeWidth = diameter * 0.25f;
            float smallVolumeHeight = smallVolumeWidth / largeVolume.width() * largeVolume.height();
            smallVolume = new RectF(volumeLeft,
                    bottom - smallVolumeHeight,
                    volumeLeft + smallVolumeWidth,
                    bottom);
            smallVibrate = new RectF(volumeRight - smallVolumeWidth,
                    bottom - smallVolumeHeight,
                    volumeRight,
                    bottom);

            // the small volume touch region is slightly bigger than the triangle
            smallVolumeTouchRegion = new RectF(
                    smallVolume.left   - smallVolume.width() * 0.25f,
                    smallVolume.top    - smallVolume.height() * 0.50f,
                    smallVolume.right  + smallVolume.width() * 0.10f,
                    smallVolume.bottom + smallVolume.height() * 0.25f);
            smallVibrateTouchRegion = new RectF(
                    smallVibrate.left   - smallVibrate.width() * 0.10f,
                    smallVibrate.top    - smallVibrate.height() * 0.50f,
                    smallVibrate.right  + smallVibrate.width() * 0.25f,
                    smallVibrate.bottom + smallVibrate.height() * 0.25f);

            duration.setTextSize(diameter * 0.32f);
            durationUnits.setTextSize(diameter * 0.10f);
            unshushTime.setTextSize(diameter * 0.13f);
            percentPaint.setTextSize(diameter * 0.08f);
        }

        if (displayMode == CLOCK_SLIDER) {
            drawClock(canvas);
            drawClockTextAndButtons(canvas);
            drawVolumeSlider(canvas, smallVolume);
            if (ENABLE_VIBRATE) {
                drawDevice(canvas, smallVibrate.centerX(), smallVibrate.centerY(),
                        smallVibrate.height(), true, vibrateNow, false, true);
                drawDevice(canvas, smallVibrate.centerX(), smallVibrate.centerY(),
                        smallVibrate.height(), false, vibrateLater, false, true);
            }
        } else if (displayMode == VOLUME_SLIDER) {
            drawVolumeSlider(canvas, largeVolume);
        } else if (displayMode == VIBRATE_PICKER) {
            drawVibratePicker(canvas);
        } else {
            throw new AssertionError();
        }
    }

    public Date getStart() {
        return start.getTime();
    }

    public void setStart(Date now) {
        start.setTime(now);
        int minuteOfHalfDay = start.get(Calendar.HOUR_OF_DAY) * 60 + start.get(Calendar.MINUTE);
        if (minuteOfHalfDay > MINUTES_PER_HALF_DAY) {
            minuteOfHalfDay -= MINUTES_PER_HALF_DAY;
        }
        int angle = minuteOfHalfDay / 2; // 720 minutes per half-day -> 360 degrees per circle
        angle += 270; // clocks start at 12:00, but our angles start at 3:00
        startAngle = angle % 360;
        postInvalidate();
    }

    public int getMinutes() {
        return minutes;
    }

    public void setMinutes(int minutes) {
        if (minutes == this.minutes) {
            return; // avoid unnecessary repaints
        }
        this.minutes = minutes;
        end.setTimeInMillis(start.getTimeInMillis() + (this.minutes * 60 * 1000L));
        postInvalidate();
    }

    public float getVolume() {
        return volume;
    }

    public void setVibrateNow(boolean vibrateNow) {
        setVolumeAndVibrate(displayMode, volume, vibrateNow, vibrateLater);
    }

    public void setVibrateLater(boolean vibrateLater) {
        setVolumeAndVibrate(displayMode, volume, vibrateNow, vibrateLater);
    }

    public void setVolume(float volume) {
        setVolumeAndVibrate(displayMode, volume, vibrateNow, vibrateLater);
    }

    private void setVolumeAndVibrate(int displayMode, float volume,
            boolean vibrateNow, boolean vibrateLater) {
        if (displayMode == this.displayMode
                && volume == this.volume
                && vibrateNow == this.vibrateNow
                && vibrateLater == this.vibrateLater) {
            return; // avoid unnecessary repaints
        }
        if (displayMode != this.displayMode
                || vibrateNow != this.vibrateNow
                || vibrateLater != this.vibrateLater) {
            this.ringerMutedDialog.modeChanged(displayMode, vibrateNow, vibrateLater);
        }
        this.displayMode = displayMode;
        this.volume = volume;
        this.vibrateNow = vibrateNow;
        this.vibrateLater = vibrateLater;
        postInvalidate();
    }

    public Date getEnd() {
        return end.getTime();
    }

    /**
     * Draw a circle and an arc of the selected duration from start thru end.
     */
    private void drawClock(Canvas canvas) {
        int sweepDegrees = (minutes / 2) - 1;

        // the colored "filled" part of the circle
        drawArc(canvas, startAngle, sweepDegrees, pink);

        // the white selected part of the circle
        drawArc(canvas, startAngle + sweepDegrees, 2, white);

        // the grey empty part of the circle
        drawArc(canvas, startAngle + sweepDegrees + 2, 360 - sweepDegrees - 2, lightGrey);
    }

    private void drawArc(Canvas canvas, int startAngle, int sweepDegrees, Paint paint) {
        if (sweepDegrees <= 0) {
            return;
        }

        path.reset();
        path.arcTo(outerCircle, startAngle, sweepDegrees);
        path.arcTo(innerCircle, startAngle + sweepDegrees, -sweepDegrees);
        path.close();
        canvas.drawPath(path, paint);
    }

    /**
     * This draws a triangle showing the current volume level. The triangle may
     * be small for an icon button or large for an active slider. At min volume
     * the triangle is mostly grey; at max volume it is all pink.
     */
    private void drawVolumeSlider(Canvas canvas, RectF bound) {
        int percent = (int) (volume * 100);
        float textX = largeVolume.left;
        float textY = largeVolume.bottom - smallVolume.height();
        canvas.drawText(percent + "%", textX, textY, percentPaint);

        drawTriangleSlice(canvas, bound, 0.0f, volume, pink);
        drawTriangleSlice(canvas, bound, volume, 1.0f, lightGrey);

        float linePercent = 3.0f / bound.width(); // percent of bound that's 3 dips wide
        float top = Math.min(volume + (linePercent / 2), 1.0f);
        float bottom = top - linePercent;
        drawTriangleSlice(canvas, bound, bottom, top, white);
    }

    /**
     * Draws a vertical slice of a right triangle whose slope goes from left to
     * right in bound. Left and right are lower and upper fractions of the
     * drawn area of bound.
     */
    private void drawTriangleSlice(Canvas canvas, RectF bound, float from, float to, Paint paint) {

        /*
         * Fill the middle slice of the triangle bounded by 'bound':
         *
         *        /|
         *       / |
         *      /| |
         *     / | |
         *    /| | |
         *   /_|_|_|
         */

        float left = bound.left + (from * bound.width());
        float right = bound.left + (to * bound.width());
        float levelTwo = bound.bottom - (from * bound.height());
        float levelThree = bound.bottom - (to * bound.height());

        path.reset();
        path.moveTo(left, bound.bottom);
        path.lineTo(left, levelTwo);
        path.lineTo(right, levelThree);
        path.lineTo(right, bound.bottom);
        path.close();
        canvas.drawPath(path, paint);
    }

    /**
     * Write labels in the middle of the circle like so:
     *
     *    2 1/2
     *    hours
     *  10:15 PM
     */
    private void drawClockTextAndButtons(Canvas canvas) {
        // up/down button backgrounds
        if (upPushed) {
            canvas.drawArc(buttonCircle, 270, 180, true, buttonCirclePaint);
        }
        if (downPushed) {
            canvas.drawArc(buttonCircle, 90, 180, true, buttonCirclePaint);
        }

        String durationText;
        int durationUnitsId;
        long timeInMillis = end.getTimeInMillis();
        String onAtText = DateFormat.getTimeFormat(getContext()).format(timeInMillis);
        if (minutes < 60) {
            durationText = Integer.toString(minutes);
            durationUnitsId = R.string.minutes;
        } else if (minutes == 60) {
            durationText = "1";
            durationUnitsId = R.string.hour;
        } else if (minutes % 60 == 0) {
            durationText = Integer.toString(minutes / 60);
            durationUnitsId = R.string.hours;
        } else if (minutes % 60 == 15) {
            durationText = minutes / 60 + "\u00BC"; // 1/4
            durationUnitsId = R.string.hours;
        } else if (minutes % 60 == 30) {
            durationText = minutes / 60 + "\u00BD"; // 1/2
            durationUnitsId = R.string.hours;
        } else if (minutes % 60 == 45) {
            durationText = minutes / 60 + "\u00BE"; // 3/4
            durationUnitsId = R.string.hours;
        } else {
            throw new AssertionError();
        }
        String durationUnitsText = getResources().getString(durationUnitsId);
        canvas.drawText(durationText,      centerX, centerY - (diameter * 0.08f), duration);
        canvas.drawText(durationUnitsText, centerX, centerY + (diameter * 0.06f), durationUnits);
        canvas.drawText(onAtText,          centerX, centerY + (diameter * 0.25f), unshushTime);

        // up/down buttons
        Paint downPaint = downPushed ? white : lightGrey;
        canvas.drawRect(centerX - diameter * 0.32f, centerY - diameter * 0.01f,
                        centerX - diameter * 0.22f, centerY + diameter * 0.01f, downPaint);
        Paint upPaint = upPushed ? white : lightGrey;
        canvas.drawRect(centerX + diameter * 0.22f, centerY - diameter * 0.01f,
                        centerX + diameter * 0.32f, centerY + diameter * 0.01f, upPaint);
        canvas.drawRect(centerX + diameter * 0.26f, centerY - diameter * 0.05f,
                        centerX + diameter * 0.28f, centerY + diameter * 0.05f, upPaint);
    }

    /**
     * Accept a touches near the circle's edge, translate it to an angle, and
     * update the sweep angle.
     */
    @Override public boolean onTouchEvent(MotionEvent event) {
        if (outerCircle == null) {
            return true; // ignore all events until the canvas is drawn
        }

        int touchX = (int) event.getX();
        int touchY = (int) event.getY();

        // Handle volume slider and vibrate picker.
        int newDisplayMode = displayMode;
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            if (smallVolumeTouchRegion.contains(touchX, touchY)) {
                newDisplayMode = VOLUME_SLIDER;
            } else if (ENABLE_VIBRATE && smallVibrateTouchRegion.contains(touchX, touchY)) {
                newDisplayMode = VIBRATE_PICKER;
            }
        }
        if (newDisplayMode == VOLUME_SLIDER) {
            float newVolume = toVolume((touchX - largeVolume.left) / largeVolume.width());
            if (event.getAction() == MotionEvent.ACTION_UP) {
                newDisplayMode = CLOCK_SLIDER;
            }
            setVolumeAndVibrate(newDisplayMode, newVolume, vibrateNow, vibrateLater);
            return true;
        }
        if (newDisplayMode == VIBRATE_PICKER) {
            boolean vibrateNow = touchX < width / 2;
            boolean vibrateLater = touchY < height / 2;
            if (event.getAction() == MotionEvent.ACTION_UP) {
                newDisplayMode = CLOCK_SLIDER;
            }
            setVolumeAndVibrate(newDisplayMode, volume, vibrateNow, vibrateLater);
            return true;
        }

        upPushed = false;
        downPushed = false;
        int distanceFromCenterX = centerX - touchX;
        int distanceFromCenterY = centerY - touchY;
        int distanceFromCenterSquared = distanceFromCenterX * distanceFromCenterX
                + distanceFromCenterY * distanceFromCenterY;
        float maxSlider = (diameter * 1.3f) / 2;
        float maxUpDown = (diameter * 0.8f) / 2;

        // handle increment/decrement
        if (distanceFromCenterSquared < (maxUpDown * maxUpDown)) {
            boolean up = touchX > centerX;

            if (event.getAction() == MotionEvent.ACTION_DOWN
                    || event.getAction() == MotionEvent.ACTION_MOVE) {
                if (up) {
                    upPushed = true;
                } else {
                    downPushed = true;
                }
                postInvalidate();
                return true;
            }

            int angle = up ? (15 + minutes) : (705 + minutes);
            if (angle > 720) {
                angle -= 720;
            }
            setMinutes(angle);
            return true;

        // if it's on the slider, handle that
        } else if (distanceFromCenterSquared < (maxSlider * maxSlider)) {
            int angle = pointToAngle(touchX, touchY);
            /*
             * Convert the angle into a sweep angle. The sweep angle is a positive
             * angle between the start angle and the touched angle.
             */
            angle = 360 + angle - startAngle;
            int angleX2 = angle * 2;
            angleX2 = roundToNearest15(angleX2);
            if (angleX2 > 720) {
                angleX2 = angleX2 - 720; // avoid mod because we prefer 720 over 0
            }
            setMinutes(angleX2);
            return true;

        } else {
            return false;
        }
    }

    /**
     * Returns a volume fraction that's in permitted bounds. We don't let the
     * volume go too low (what would be the point!) or above 100%.
     */
    private float toVolume(float rawFraction) {
        if (rawFraction < 0.1) {
            return 0.1f;
        }
        if (rawFraction > 1.0) {
            return 1.0f;
        }
        return rawFraction;
    }

    @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);

        // Don't use the full screen width on tablets!
        DisplayMetrics metrics = new DisplayMetrics();
        WindowManager windowManager = (WindowManager) getContext()
                .getSystemService(Context.WINDOW_SERVICE);
        windowManager.getDefaultDisplay().getMetrics(metrics);
        float maxWidthInches = 2.3f;

        width = Math.min(width, (int) (maxWidthInches * metrics.densityDpi));
        height = Math.min(height, (int) (width * 0.7f));

        setMeasuredDimension(width, height);
    }

    /**
     * Returns the number of degrees (0-359) for the given point, such that
     * 3pm is 0 and 9pm is 180.
     */
    private int pointToAngle(int x, int y) {

        /* Get the angle from a triangle by dividing opposite by adjacent
         * and taking the atan. This code is careful not to divide by 0.
         *
         *
         *      adj | opp
         *          |
         * opp +180 | +270 adj
         * _________|_________
         *          |
         * adj  +90 | +0   opp
         *          |
         *      opp | adj
         *
         */

        if (x >= centerX && y < centerY) {
            double opp = x - centerX;
            double adj = centerY - y;
            return 270 + (int) Math.toDegrees(Math.atan(opp / adj));
        } else if (x > centerX && y >= centerY) {
            double opp = y - centerY;
            double adj = x - centerX;
            return (int) Math.toDegrees(Math.atan(opp / adj));
        } else if (x <= centerX && y > centerY) {
            double opp = centerX - x;
            double adj = y - centerY;
            return 90 + (int) Math.toDegrees(Math.atan(opp / adj));
        } else if (x < centerX && y <= centerY) {
            double opp = centerY - y;
            double adj = centerX - x;
            return 180 + (int) Math.toDegrees(Math.atan(opp / adj));
        }

        throw new IllegalArgumentException();
    }

    /**
     * Rounds the angle to the nearest 7.5 degrees, which equals 15 minutes on
     * a clock. Not strictly necessary, but it discourages fat-fingered users
     * from being frustrated when trying to select a fine-grained period.
     */
    private int roundToNearest15(int angleX2) {
        return ((angleX2 + 8) / 15) * 15;
    }

    private void drawVibratePicker(Canvas canvas) {
        boolean onOn = vibrateNow & vibrateLater;
        boolean offOn = !vibrateNow & vibrateLater;
        boolean onOff = vibrateNow & !vibrateLater;
        boolean offOff = !vibrateNow & !vibrateLater;

        int selectedLeft = vibrateNow ? 0 : width / 2;
        int selectedTop = vibrateLater ? 0 : height / 2;
        canvas.drawRect(selectedLeft, selectedTop,
                selectedLeft + width / 2, selectedTop + height / 2, buttonCirclePaint);

        float deviceHeight = height * 0.2f;
        // top left ON ON
        drawDevice(canvas, width * 0.25f, height * 0.25f, deviceHeight, true, true, onOn, true);
        drawDevice(canvas, width * 0.25f, height * 0.25f, deviceHeight, false, true, onOn, true);
        // top right OFF ON
        drawDevice(canvas, width * 0.75f, height * 0.25f, deviceHeight, true, false, offOn, true);
        drawDevice(canvas, width * 0.75f, height * 0.25f, deviceHeight, false, true, offOn, true);
        // bottom left ON OFF
        drawDevice(canvas, width * 0.25f, height * 0.75f, deviceHeight, true, true, onOff, true);
        drawDevice(canvas, width * 0.25f, height * 0.75f, deviceHeight, false, false, onOff, true);
        // bottom right, OFF OFF
        drawDevice(canvas, width * 0.75f, height * 0.75f, deviceHeight, true, false, offOff, true);
        drawDevice(canvas, width * 0.75f, height * 0.75f, deviceHeight, false, false, offOff, true);
    }

    private void drawDevice(Canvas canvas, float centerX, float centerY, float height,
            boolean now, boolean vibrate, boolean selected, boolean sideBySide) {
        canvas.save();

        int width = (int) (height * 0.7f);
        int bezel = (int) (height * 0.08f);
        int left = (int) (now ? (centerX - width - bezel) : (centerX + bezel));
        int right = left + width;
        int top = (int) (centerY - height / 2);
        int bottom = (int) (centerY + height / 2);

        // If we're vibrating, rotate the device.
        if (vibrate) {
            // If we're right beside another device, move away from center as to not overlap.
            if (sideBySide) {
                canvas.translate(now ? -height * 0.18f : height * 0.18f, 0f);
            }
            canvas.rotate(27f, left + width / 2, centerY);
        }

        Paint paint = selected ? white : lightGrey;
        canvas.drawRect(left, top, left + bezel, bottom, paint); // left edge
        canvas.drawRect(right - bezel, top, right, bottom, paint); // right edge
        canvas.drawRect(left, top, right, top + bezel, paint); // top edge
        canvas.drawRect(left, bottom - bezel, right, bottom, paint); // bottom edge

        if (vibrate) {
            canvas.drawRect(left - 4 * bezel, top + 3 * bezel,
                    left - 3 * bezel, top + 7 * bezel, paint);
            canvas.drawRect(left - 2 * bezel, top + 1 * bezel,
                    left - 1 * bezel, top + 9 * bezel, paint);
            canvas.drawRect(right + 3 * bezel, bottom - 7 * bezel,
                    right + 4 * bezel, bottom - 3 * bezel, paint);
            canvas.drawRect(right + 1 * bezel, bottom - 9 * bezel,
                    right + 2 * bezel, bottom - 1 * bezel, paint);
        }

        canvas.restore();
    }
}

--
You received this message because you are subscribed to the Google
Groups "Android Developers" group.
To post to this group, send email to android-developers@googlegroups.com
To unsubscribe from this group, send email to
android-developers+unsubscribe@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/android-developers?hl=en

No comments:

Post a Comment