12

I'm still new to android so I'm not totally familiar with all the view components. I'm struggling with aligning Buttons dynamically around a circle.

What I am trying to achieve is to add n buttons (n can change at creation time) to a view that looks like the attached image:

I'd like to avoid using absoluteLayout (but I'm open to suggestions if that's the only way to solve it). I already came up with a calculation for the x/y positions for the buttons (ignoring button size for now):

int iNumberOfButtons = 10;
double dIncrease = Math.PI * 2 / iNumberOfButtons,
    dAngle = 0,
        x = 0,
        y = 0;

  for( int i = 0; i < iNumberOfButtons; i++ )
  {
    x = 100 * Math.cos( dAngle ) + 200;
    y = 100 * Math.sin( dAngle ) + 200;
    dAngle += dIncrease;
    // get button and set position?
  }

I thought about using this code from inside a custom view but from what I've seen the view needs to be subclassed from ViewGroup to have the addView method and then again only absoluteLayout seems to allow setting x, y positions... I'm at a loss how to implement this feature.

I might add some animations to that view later on, so using SurfaceView might be nice if it's possible but it's not a requirement.

ArtKorchagin
  • 4,801
  • 13
  • 42
  • 58
Nathaniel K.
  • 151
  • 1
  • 6

3 Answers3

3

I think I found the solution I tried to achieve.

I create my own view subclassing RelativeLayout. In onCreate() I set

setWillNotDraw(false);

so that onDraw() gets called. I then continue in onDraw():

int iHeight = getHeight();
int iWidth = getWidth();
int iNumberOfButtons = 10;
double dIncrease = Math.PI * 2 / iNumberOfButtons,
       dAngle = 0,
       x = 0,
       y = 0;

  for( int i = 0; i < iNumberOfButtons; i++ )
  {
    x = 200 * Math.cos( dAngle ) + iWidth/2;
    y = 200 * Math.sin( dAngle ) + iHeight/2;
    dAngle += dIncrease;
    Button xButton = new Button(m_xContext);
    xButton.setAdjustViewBounds(true);
    xButton.setBackgroundResource(R.drawable.some_image);
    LayoutParams xParams = (RelativeLayout.LayoutParams)xButton.getLayoutParams();
    if( xParams == null )
    {
      xParams = new RelativeLayout.LayoutParams( xButton.getBackground().getIntrinsicWidth(), xButton.getBackground().getIntrinsicHeight() );
    }
    xParams.leftMargin = (int)x - ( xButton.getBackground().getIntrinsicWidth() / 2 ) ;
    xParams.topMargin =  (int)y - ( xButton.getBackground().getIntrinsicHeight() / 2 );
    addView( xButton, xParams );
  }

This gives me the desired result, however the initializing of the LayoutParams feels (and most likely is) wrong. Is there a better way to do this?

Nathaniel K.
  • 151
  • 1
  • 6
  • I'm running into another issue with this approach. onDraw() gets repeatedly called which is not what I want when setting up buttons there. I guess I could probably surround the initialization with an if and a boolean but is there an accepted way to implement the behavior I want? (e.g. Init my custom view with the buttons, laid out in a circle from the center, without resorting to onDraw?) – Nathaniel K. Feb 21 '12 at 22:15
2

The following Apache 2.0 Licensed project may be of service: https://github.com/dmitry-zaitsev/CircleLayout

cshadowstar
  • 303
  • 2
  • 17
0

Instead of absolute layout you can use RelativeLayout and to child view, set the top and the left margins. Something like this:

RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) view.getLayoutParams();
params.leftMargin = x;
params.topMargin = y;
peter.bartos
  • 11,855
  • 3
  • 51
  • 62
  • That's a good idea, your suggestion achieves what I was trying to do. I subclassed RelativeLayout for this and create the buttons in onCreate. Their LayoutParams (and the ones from the view itself) are null at this point though. What is the preferred way to initialize them? Another requirement which I forgot to mention is that the circle should be positioned in the center of the view. I thought it would be easy to just add the values I fetch from getHeight() and getWidth() to offset the positions but both methods return 0 (as do getMeasuredWidth() and Height). Is there any way to fix that? – Nathaniel K. Feb 21 '12 at 16:51