21

I am going to develop the app and i want to create the menu like Catch notes App

Screen Shoot

enter image description here

anybody who know how can i implement this or if any library available for it. please let me know.

Thank you

Community
  • 1
  • 1
  • 1
    +1 I have same problem dude.. see [my Question](http://stackoverflow.com/questions/12577200/radial-menu-android-with-button-click) – MAC Sep 25 '12 at 08:45
  • 3
    I don't have to implement something like that, but I am interested in the answer. I would look in the direction of the animation API if I had to guess. – Teovald Sep 25 '12 at 08:51

1 Answers1

31

I recently created this circular menu to add up in my recent project. It looks like enter image description here

What you need is to create a new View and draw this view, check for the user inputs (where he is touching), design a feedback mechanism, for example, in my view, if user touches any of the 5 arcs, background color changes to skyblue. Here is my code for View Class.

[EDIT] : This time I am posting all my code for the view. Hope it might help you.

public class SmallCircleView extends View{

    private Paint smallCircleCore;
    private Paint lineCore;
    private Paint bigArc;
    private Paint bigArcDraw;
    private Paint pencil;
    private Paint middleCircleBody;
    private Paint arcTouchedBack;
    private Paint circlePaint;

    private int initial, midInitial;
    private int finalangle;
    private int middleCircleRadius;
    private int i =0;

    private int centerCircleradius;

    int[] colors = new int[]{0xFF33B5E5,
            0xFFAA66CC,
            0xFF99CC00,
            0xFFFFBB33,
            0xFFFF4444,
            0xFF0099CC,
            0xFFAA66CC,
            0xFF669900,
            0xFFFF8800,
            0xFFCC0000};

    private RectF oval = new RectF();
    private RectF middleOval = new RectF();
    private RectF finalOVal = new RectF();

    private Context context;
    private int px, py;

    private boolean isClicked = true;
    private boolean[] arcTouched= new boolean[]{false, false, false, false, false};

    EmbossMaskFilter emboss;
    EmbossMaskFilter forBig;

    private ArrayList<Bitmap> bitmap = new ArrayList<Bitmap>();

    public SmallCircleView(Context context) {
        super(context);
        this.context = context;
        initSmallCircle();

        // TODO Auto-generated constructor stub
    }

    public SmallCircleView(Context context, AttributeSet attri)
    {
        super(context, attri);
        this.context = context;

        initSmallCircle();
    }

    public SmallCircleView(Context context, AttributeSet attri, int defaultStyle)
    {
        super(context, attri, defaultStyle);
        this.context = context;

        initSmallCircle();
    }

    private void initSmallCircle()
    {
        setFocusable(true);

        smallCircleCore = new Paint(Paint.ANTI_ALIAS_FLAG);
        lineCore = new Paint(Paint.ANTI_ALIAS_FLAG);
        bigArc = new Paint(Paint.ANTI_ALIAS_FLAG);
        pencil = new Paint(Paint.ANTI_ALIAS_FLAG);

        lineCore.setColor(0xFFFFFFFF);
        lineCore.setStyle(Paint.Style.FILL);
        lineCore.setStrokeWidth(4);
        //bigCircleCore.setStrokeWidth(5);

        smallCircleCore.setStyle(Paint.Style.FILL);
        smallCircleCore.setColor(0xFFFFFFFF);

        bigArc.setColor(0xFF424242);
        bigArc.setStyle(Paint.Style.FILL);

        bigArcDraw = new Paint(smallCircleCore);
        bigArcDraw.setStrokeWidth(4);
        bigArcDraw.setColor(0xFF000000);

        pencil.setStrokeWidth(0.5f);
        pencil.setColor(0x80444444);
        pencil.setStyle(Paint.Style.STROKE);

        middleCircleBody = new Paint(bigArc);
        middleCircleBody.setColor(0xFFE6E6E6);

        arcTouchedBack = new Paint(Paint.ANTI_ALIAS_FLAG);
        arcTouchedBack.setColor(0xFF81DAF5);
        arcTouchedBack.setStyle(Paint.Style.FILL);      

        circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        circlePaint.setStyle(Paint.Style.STROKE);
        circlePaint.setStrokeWidth(10);



        float[] direection = new float[]{1,1,1};
        float light = 0.4f;
        float specualr = 6;
        float blur = 3.5f;      

        emboss = new EmbossMaskFilter(direection, light, specualr, blur);
        forBig = new EmbossMaskFilter(direection, 1f, 4, 2.5f);

        bitmap.clear();
        bitmap.add(BitmapFactory.decodeResource(getResources(), R.drawable.tasks));
        bitmap.add(BitmapFactory.decodeResource(getResources(), R.drawable.file_manager));
        bitmap.add(BitmapFactory.decodeResource(getResources(), R.drawable.home));
        bitmap.add(BitmapFactory.decodeResource(getResources(), R.drawable.contacts));      
        bitmap.add(BitmapFactory.decodeResource(getResources(), R.drawable.reminder));


        middleCircleBody.setMaskFilter(forBig);
        bigArc.setMaskFilter(forBig);

        String log="";
        Log.d(log, "Value of px & py " + getMeasuredWidth() + " " + getMeasuredHeight());
    }

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        int measuredWidth = measure(widthMeasureSpec);
        int measuredHeight = measure(heightMeasureSpec);

        int d = Math.min(measuredWidth, measuredHeight);

        setMeasuredDimension(d,d);
    }

    private int measure(int measureSpec)
    {
        int result = 0;

        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        if(specMode == MeasureSpec.UNSPECIFIED)
        {
            result = 200;
        }
        else
        {
            result = specSize;
        }

        return result;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        // TODO Auto-generated method stub

        px = getMeasuredWidth()/2;
        py = getMeasuredHeight();


        initial = 144;
        finalangle = 252;

        centerCircleradius  = 30;
        middleCircleRadius = 140;




            int init, fina;
            init = 160;
            fina = 360;
            finalOVal.set(px-middleCircleRadius-4, py-middleCircleRadius-4, px+middleCircleRadius+4, py+middleCircleRadius+4);
            middleOval.set(px-middleCircleRadius, py-middleCircleRadius, px+middleCircleRadius, py+middleCircleRadius);
            while(init<fina)
            {
                circlePaint.setColor(colors[i]);
                canvas.drawArc(finalOVal,init,10,false, circlePaint);
                i++;
                if(i>=colors.length)
                {
                    i=0;
                }
                init = init + 10;

            }
            //Setting Mask Filter


            canvas.drawArc(middleOval, 180, 180, false, pencil);

            midInitial = 180;


            i=0;

            //Creating the first Arc
            if(arcTouched[0])
            {

                canvas.drawArc(middleOval, midInitial, 36, true, arcTouchedBack);
                canvas.drawArc(middleOval, midInitial, 36, true, pencil);
            }
            else
            {
                canvas.drawArc(middleOval, midInitial, 36, true, middleCircleBody);
                canvas.drawArc(middleOval, midInitial, 36, true, pencil);
            }
            canvas.drawBitmap(bitmap.get(0), null, (putBitmapTo(midInitial, 36, 140, px, py)), null);
            midInitial+=36;

            if(arcTouched[1])
            {

                canvas.drawArc(middleOval, midInitial, 36, true, arcTouchedBack);
                canvas.drawArc(middleOval, midInitial, 36, true, pencil);
            }
            else
            {
                canvas.drawArc(middleOval, midInitial, 36, true, middleCircleBody);
                canvas.drawArc(middleOval, midInitial, 36, true, pencil);
            }
            canvas.drawBitmap(bitmap.get(1), null, (putBitmapTo(midInitial, 36, 140, px, py)), null);
            midInitial+=36;

            if(arcTouched[2])
            {

                canvas.drawArc(middleOval, midInitial, 36, true, arcTouchedBack);
                canvas.drawArc(middleOval, midInitial, 36, true, pencil);
            }
            else
            {
                canvas.drawArc(middleOval, midInitial, 36, true, middleCircleBody);
                canvas.drawArc(middleOval, midInitial, 36, true, pencil);
            }
            canvas.drawBitmap(bitmap.get(2), null, (putBitmapTo(midInitial, 36, 140, px, py)), null);
            midInitial+=36;
            //Creatring the second Arc

            if(arcTouched[3])
            {

                canvas.drawArc(middleOval, midInitial, 36, true, arcTouchedBack);
                canvas.drawArc(middleOval, midInitial, 36, true, pencil);
            }
            else
            {
                canvas.drawArc(middleOval, midInitial, 36, true, middleCircleBody);
                canvas.drawArc(middleOval, midInitial, 36, true, pencil);
            }
            canvas.drawBitmap(bitmap.get(3), null, (putBitmapTo(midInitial, 36, 140, px, py)), null);
            midInitial+=36;

            if(arcTouched[4])
            {

                canvas.drawArc(middleOval, midInitial, 36, true, arcTouchedBack);
                canvas.drawArc(middleOval, midInitial, 36, true, pencil);
            }
            else
            {
                canvas.drawArc(middleOval, midInitial, 36, true, middleCircleBody);
                canvas.drawArc(middleOval, midInitial, 36, true, pencil);
            }
            canvas.drawBitmap(bitmap.get(4), null, (putBitmapTo(midInitial, 36, 140, px, py)), null);           
            canvas.drawCircle(px, py-10, 40, pencil);
            canvas.drawCircle(px, py-10, 39, smallCircleCore);

            canvas.drawCircle(px, py-10, 35, bigArc);
            canvas.drawCircle(px, py-10, 20, smallCircleCore);

            canvas.drawCircle(px, py-10, 15, bigArc);
            canvas.drawLine(px-8, py-10, px+8, py-10, lineCore);

        canvas.save();
    }



    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // TODO Auto-generated method stub

        float centerX = px;
        float centerY = (py-centerCircleradius+(centerCircleradius/4));

        switch(event.getAction())
        {
            case MotionEvent.ACTION_DOWN:
            {
                if(isInSideCenterCircle(centerX, centerY, centerCircleradius, event.getX(), event.getY()))
                {
                    return super.onTouchEvent(event);
                }
                if(isInArc(180, 36, middleCircleRadius, px, py, event.getX(), event.getY()))
                {

                    makeAllFalse();
                    arcTouched[0] = true;
                    invalidate();
                    return true;
                }

                if(isInArc(216, 36, middleCircleRadius, px, py, event.getX(), event.getY()))
                {

                    makeAllFalse();
                    arcTouched[1] = true;
                    invalidate();
                    return true;
                }

                if(isInArc(252, 36, middleCircleRadius, px, py, event.getX(), event.getY()))
                {

                    makeAllFalse();
                    arcTouched[2] = true;
                    invalidate();
                    return true;
                }

                if(isInArc(288,36, middleCircleRadius, px, py, event.getX(), event.getY()))
                {

                    makeAllFalse();
                    arcTouched[3] = true;
                    invalidate();
                    return true;
                }

                if(isInArc(324, 36, middleCircleRadius, px, py, event.getX(), event.getY()))
                {

                    makeAllFalse();
                    arcTouched[4] = true;
                    invalidate();
                    return true;
                }
            }
            case MotionEvent.ACTION_UP:
            {


                if(isInSideCenterCircle(centerX, centerY, centerCircleradius, event.getX(), event.getY()))
                {
                    return super.onTouchEvent(event);
                }

                if(isInArc(180, 36, middleCircleRadius, px, py, event.getX(), event.getY()))
                {
                    Toast.makeText(getContext(), "In the first Arc !!!", Toast.LENGTH_SHORT).show();
                    makeAllFalse();

                    invalidate();
                    return false;
                }

                if(isInArc(216, 36, middleCircleRadius, px, py, event.getX(), event.getY()))
                {
                    Toast.makeText(getContext(), "In Second Arc", Toast.LENGTH_SHORT).show();
                    makeAllFalse();

                    invalidate();
                    return false;
                }

                if(isInArc(252, 36, middleCircleRadius, px, py, event.getX(), event.getY()))
                {
                    Toast.makeText(getContext(), "In Third Arc", Toast.LENGTH_SHORT).show();
                    makeAllFalse();

                    invalidate();
                    return false;
                }

                if(isInArc(288,36, middleCircleRadius, px, py, event.getX(), event.getY()))
                {
                    Toast.makeText(getContext(), "In fourth Arc", Toast.LENGTH_SHORT).show();
                    makeAllFalse();

                    invalidate();
                    return false;
                }

                if(isInArc(324, 36, middleCircleRadius, px, py, event.getX(), event.getY()))
                {
                    Toast.makeText(getContext(), "In Fifth Arc", Toast.LENGTH_SHORT).show();
                    makeAllFalse();

                    invalidate();
                    return false;
                }

                else
                {
                    makeAllFalse();
                    invalidate();
                    return super.onTouchEvent(event);
                }
            }
            case MotionEvent.ACTION_MOVE:
            {
                if(isInSideCenterCircle(centerX, centerY, centerCircleradius, event.getX(), event.getY()))
                {
                    makeAllFalse();
                    invalidate();
                    return super.onTouchEvent(event);
                }

                if(isInArc(180, 36, middleCircleRadius, px, py, event.getX(), event.getY()))
                {

                    makeAllFalse();
                    arcTouched[0] = true;
                    invalidate();
                    return false;
                }

                if(isInArc(216, 36, middleCircleRadius, px, py, event.getX(), event.getY()))
                {

                    makeAllFalse();
                    arcTouched[1] = true;
                    invalidate();
                    return false;
                }

                if(isInArc(252, 36, middleCircleRadius, px, py, event.getX(), event.getY()))
                {

                    makeAllFalse();
                    arcTouched[2] = true;
                    invalidate();
                    return false;
                }

                if(isInArc(288,36, middleCircleRadius, px, py, event.getX(), event.getY()))
                {

                    makeAllFalse();
                    arcTouched[3] = true;
                    invalidate();
                    return false;
                }

                if(isInArc(324, 36, middleCircleRadius, px, py, event.getX(), event.getY()))
                {

                    makeAllFalse();
                    arcTouched[4] = true;
                    invalidate();
                    return false;
                }

                else
                {
                    makeAllFalse();
                    invalidate();
                    return super.onTouchEvent(event);
                }
            }
        }
        return super.onTouchEvent(event);
    }

    private boolean isInSideCenterCircle(float centerX, float centerY, float radius, float co_ordinateX, float co_ordinateY)
    {       
        return ((Math.pow((centerX-co_ordinateX),2))+(Math.pow((centerY-co_ordinateY),2))-Math.pow(radius, 2))<=0;      
    }

    private void changeIsClicked()
    {
        isClicked =(isClicked?(false):(true));
    }

    private RectF putBitmapTo(int startAngle, int SweepAngle, float radius, float centerX, float centerY)
    {
        float locx = (float) (centerX +((radius/17*11)*Math.cos(Math.toRadians(
                                                         (startAngle+SweepAngle+startAngle)/2
                                                         ))
                                                         ));
        float locy = (float) (centerY + ((radius/17*11)*Math.sin(Math.toRadians(
                                                         (startAngle+SweepAngle+startAngle)/2
                                                         ))
                                                         ));

        return new RectF(locx-20, locy-20, locx+20, locy+20);   

    }

    private boolean isInArc(int startAngle, int SweepAngle, float radius, float centerX, float centerY, float toCheckX, float toCheckY)
    {

        double degree;

        if(
           (Math.pow(centerX - toCheckX, 2) + 
           Math.pow(centerY - toCheckY, 2) -
           Math.pow(radius, 2))<=0)
        {           
            double radian = Math.atan((centerY-toCheckY)/(centerX-toCheckX));
            if(radian<0)                
            {
                degree = Math.toDegrees(radian);
                degree = 360+ degree;                   
            }
            else
            {               
                degree = Math.toDegrees(radian);
                degree = 180 + degree;          
            }           

            if(degree>=startAngle)
            {               
                if(degree<=(startAngle+SweepAngle))
                {                   
                    return true;
                }               
            }           
        }       
        else
        {
            return false;
        }       
        return false;
    }

    private void makeAllFalse()
    {
        for(int i=0;i<arcTouched.length;i++)
        {
            arcTouched[i] = false;
        }
    }

}

I know this isn't the best way and not professional too. I created this menu as per need. Its not scale able in a way until you change the angle calculations.

Gaurav
  • 3,614
  • 3
  • 30
  • 51
  • Yes. Sure. I would love to. Just being curious, which part of the code I need to put down here ? Is it touch handling part (I nearly banged my head against wall for how to decide where user touched ?), or is it the initialization part ? – Gaurav May 03 '13 at 18:35
  • Actually I need the codes which can be draw the menu ... and click listener too.. – Pankaj Kumar May 06 '13 at 04:48
  • Thanks for the code. Helped a lot. Could you tell us how to make it a full circle 360? – DanKodi Sep 17 '13 at 06:16
  • @DanKodi: As you can see in function, onDraw(), there are variables named initial and finalangle, change it to 0 and 360, you will get a full circle. Please keep in mind, you would also have to change the y co-ordinate of center too in order to view the full circle. – Gaurav Sep 19 '13 at 07:32
  • 1
    hay, @hasH working fine but some issue i want large menu circle can you help me where i need to change ... thank you – Sagar Devani Dec 24 '14 at 07:19
  • Change setMeasuredDimension(d, d) to the size you want. But you may run into overlapping plus touch feedback issues. – Gaurav Dec 24 '14 at 09:00
  • its really nice but need some customization like can we do the center "minus" view as like navigation drawer icon (three bars) and want to put this in right side center vertical, if i click on bars the arcs will pop out again click it will hide. – Devendra Singh May 06 '15 at 10:38