Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a selection border that tracks the checked radio button?

I have a set of RadioButtons with a custom style. I want to display a border around the button that is currently checked. This should be easy enough using XML, but now I want the border to be animated. If a new radio button is checked, the border should "fly" to its new location with a fancy animation:

+------+
|* btn1| o btn2
+------+

    +------+
 o b|n1  * |tn2
    +------+

        +------+
 o btn1 |* btn2|
        +------+

Because of this, I decided to make the border into a separate View object, so I can animate it properly. The trouble is in tracking the location of the corresponding radio button on the screen.

I'm trying to get it to work without animations first. My current attempt looks something like this (only showing the relevant attributes):

    <RelativeLayout
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true">
        <RadioGroup
            android:id="@+id/radio_group">
            <RadioButton/>
            <RadioButton/>
            <RadioButton/>
        </RadioGroup>
        <View
            android:id="@+id/selection_border"
            android:layout_alignParentLeft="true"
            android:layout_alignParentTop="true"/>
    </RelativeLayout>

In the OnCheckedChangeListener of the RadioGroup, I move the selection border around by setting its margin (I could set the position, but that's a little harder with a RelativeLayout):

View radioButton = findViewById(checkedId);
View selectionBorder = findViewById(R.id.selection_border);
ViewGroup radioGroup = (ViewGroup)findViewById(R.id.radio_group);

RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(selectionBorder.getLayoutParams());
params.leftMargin = radioGroup.getLeft() + radioButton.getLeft();
params.topMargin = radioGroup.getTop() + radioButton.getTop();
selection.setLayoutParams(params);

selection.requestLayout();

Trouble happens, however, on initialization. Because no layouting has been done yet, the position of the border is set incorrectly. It doesn't seem to be possible to force a relayout immediately, and it also doesn't seem to be possible to get an event after layouting has been done.

All this hassle leads me to believe that there must be a cleaner way to accomplish what I want. Any bright ideas?

like image 750
Thomas Avatar asked Feb 19 '11 12:02

Thomas


2 Answers

best way I think is the treat check box as an image. Two types checked/not checked. Then you are basically moving an image across the screen. Handle everthing with TouchEvents and images if user mouse downs with the region radious of image you know they have selected. This is easy because now you are just dealing with images, a canvas and stardard x,y coordinates.

like image 79
Androider Avatar answered Nov 04 '22 08:11

Androider


To address your trouble with initialization (I've had much trouble in the past with this myself): If I need to know when a view's layout is complete, I give it a runnable in Activity.onCreate() then call that runnable from View.onSizeChanged(). I've come to use this pattern quite a bit and I've found it very reliable. You just need to remember to only call the runnable from onSizeChanged() if the new width and height are greater than 0 and if they are not equal to the old width and height.

So in your activity:

public void onCreate() {
    ...
    yourView.setLayoutRunnable(new Runnable() {
        @Override
        public void run() {
            //do post-layout stuff...
        }
    });
    ...
}

and in your view:

public Runnable layoutRunner;

public void setLayoutRunnable(Runnable runner) {
    layoutRunner = runner;
}

@Override
public void onSizeChanged(int w, int h, int oldw, int oldh) {
    if (w > 0 && h > 0 && (w != oldw || h != oldh)) {
        if (layoutRunner != null) layoutRunner.run();
    }
}

If you have multiple views that all need to have their layouts finished before you do your stuff, you have to keep a count as you get the notifications so you know when they're done.

And regarding your topic in general, I implemented something similar to your radio button concept in a game I wrote, but I decided to override View from scratch to do it. I had all options for a set in a single view, so really I replaced the RadioGroup view rather than the RadioButton view. Each option had a value and an extent. I always used text, but images would work just as easily. Then I just tracked the taps and drags relative to the extents of the options and moved the "selector" on a timed handler (a la animations in javascript).

like image 42
atheaos Avatar answered Nov 04 '22 09:11

atheaos