custom made arc shaped seekbar

2019-04-11 06:38发布

问题:

What I am trying to achieve is to make an arc shaped seekbar. I know there are plenty of libraries I could use to achieve this, but I am just trying my hands on custom made views. I have encountered couple of problems:

  1. I have a class which extends SeekBar, and I have implemented onDraw and onMeasure methods as well, but I am not able to view that in layout editor in eclipse, here is the code for the custom view class:

    package com.custom.android.views;
    
    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.graphics.Path;
    import android.graphics.Path.Direction;
    import android.graphics.PathMeasure;
    
    
    import android.util.AttributeSet;
    import android.view.MotionEvent;
    import android.view.View;
    
    import android.widget.SeekBar;
    import android.widget.Toast;
    
    
    
    public class CustomSeekBar extends SeekBar {
    
        public CustomSeekBar(Context context) {
            super(context);
            // TODO Auto-generated constructor stub
    
        }
    
        public CustomSeekBar(Context context, AttributeSet attrs) {
            this(context, attrs,0);
    
        }
    
        public CustomSeekBar(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
    
        }
    
        @Override
        public void draw(Canvas canvas) {
            // TODO Auto-generated method stub
            super.draw(canvas);
        }
    
        @Override
        protected synchronized void onMeasure(int widthMeasureSpec,
                int heightMeasureSpec) {
            // TODO Auto-generated method stub
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    
    
    }
    

Here is my layout xml :

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <com.custom.android.views.CustomSeekBar
         android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/seekBar"/>

</RelativeLayout>
  1. If I use canvas class to draw an arc or any shape, would that be a good starting point?

What exactly is wrong with the eclipse adt and how could I use the onDraw method to give shape to that seekbar?

回答1:

Drawing a ProgressBar with any shape, is pretty easy. With the SeekBar you have some complexity, since you have to achieve 3 diferent things:

  1. Draw the line
  2. Draw the draggable thumb, if you want.
  3. Handle the user interaction

You have to think of it as an arc that is draw inside a rectangle. So point 3 could be easy: just let the user move the finger in a horizontal line, or exactly over the arc, but considering only the x coordinate of the touch event. What does this mean, in short? ok, good news: you dont have to do anything, since thats the normal behavior of the base SeekBar.

For the second point, you can choose an image for the handler, and write it in the corresponding position with a little maths. Or you can forget the handler for know, and just draw the seek bar as a line representing the full track, and another line over it representing the progress. When you have this working, if you want you can add the handler.

And for the first point, this is the main one, but its not hard to achieve. You can use this code:

UPDATE: I made some improvements in the code

public class ArcSeekBar extends SeekBar {


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

    }

    public ArcSeekBar(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);

    }

    private Paint mBasePaint;
    private Paint mProgressPaint;
    private RectF mOval = new RectF(5, 5, 550, 550);
    private int defaultmax = 180;
    private int startAngle=180;
    private int strokeWidth=10;

    private int trackColor=0xFF000000;
    private int progressColor=0xFFFF0000;



    public void setOval(RectF mOval) {
            this.mOval = mOval;
    }



    public void setStartAngle(int startAngle) {
            this.startAngle = startAngle;
    }

    public void setStrokeWidth(int strokeWidth) {
            this.strokeWidth = strokeWidth;
    }

    public void setTrackColor(int trackColor) {
            this.trackColor = trackColor;
    }

    public void setProgressColor(int progressColor) {
            this.progressColor = progressColor;
    }

    public ArcSeekBar(Context context) {
            super(context);
            mBasePaint = new Paint();
            mBasePaint.setAntiAlias(true);
            mBasePaint.setColor(trackColor);
            mBasePaint.setStrokeWidth(strokeWidth);
            mBasePaint.setStyle(Paint.Style.STROKE);

            mProgressPaint = new Paint();
            mProgressPaint.setAntiAlias(true);
            mProgressPaint.setColor(progressColor);
            mProgressPaint.setStrokeWidth(strokeWidth);
            mProgressPaint.setStyle(Paint.Style.STROKE);

            setMax(defaultmax);// degrees


    }

    @Override
    protected void onDraw(Canvas canvas) {
            canvas.drawArc(mOval, startAngle, getMax(), false, mBasePaint);
            canvas.drawArc(mOval, startAngle, getProgress(), false, mProgressPaint);
            invalidate();
            //Log.i("ARC", getProgress()+"/"+getMax());

    }

 }

Of course, you can and you should make everything configurable, be means of the contructor, or with some setters for the start and end angles, dimensions of the containing rectangle, stroke widths, colors, etc.

Also, note that the arc is drawn from 0 to getProgress, being this number an angle relative to the x axis, growing clocwise, so, if it go from 0 to 90 degrees, it will be something like:

Of course you can change this: canvas.drawArc get any number as an angle, and it is NOT treated as module 360, but you can do the maths and have it starting and ending in any point you want.

In my example the beggining is in the 9 of a clock, and it takes 180 degrees, to the 3 in the clock.

UPDATE

I uploaded a running example to github