/* Copyright (c) 2002-2011 by XMLVM.org
*
* Project Info: http://www.xmlvm.org
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*/
package android.widget;
import java.util.ArrayList;
import java.util.List;
import org.xmlvm.acl.common.adapter.RadioGroupAdapter;
import org.xmlvm.acl.common.objects.CommonFont;
import org.xmlvm.acl.common.objects.CommonView;
import android.content.Context;
import android.graphics.RectF;
import android.internal.Assert;
import android.internal.CommonDeviceAPIFinder;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
/**
*
* Maps Android's RadioGroup to iPhone's UISegmentedControl. The capabilities of
* this mapping are limited compared to Android's RadioGroup. Especially special
* layouting features and adding widgets other than RadioButtons to a RadioGroup
* is not supported.
*
*/
public class RadioGroup extends LinearLayout {
/** Horizontal padding which should always be around a text. */
private static final int INTERNAL_PADDING_X = 10;
/** Vertical padding which should always be around a text. */
private static final int INTERNAL_PADDING_y = 5;
/** The RadioButtons held by this RadioGroup. */
private List<RadioButton> radioButtons = new ArrayList<RadioButton>();
/**
*
* Constructor to create an empty RadioGroup. Basically this creates a
* UISegmentedControl without any special parameters or segments.
*
* @param context
* The context used to create this RadioGroup.
*
*/
public RadioGroup(Context context) {
super(context);
initRadioGroup(context, null);
}
/**
*
* Constructor to create an empty RadioGroup. Basically this creates a
* UISegmentedControl without any segments. Parameters of the RadioGroup
* will be retrieved from the provided AttributeSet. Note that not all
* attributes supported by Android's RadioGroup are supported. Providing
* these attributes will not cause errors - they are simply ignored.
*
* @param context
* The context used to create this RadioGroup.
*
* @param attrs
* The AttributeSet to get parameters from.
*
*/
public RadioGroup(Context context, AttributeSet attrs) {
super(context, attrs);
initRadioGroup(context, attrs);
}
/**
*
* Perform the initialization of the RadioGroup. If attributes are provided
* they will be parsed to retrieve parameters from.
*
* @param context
* The context used to create this RadioGroup.
*
* @param attrs
* The AttributeSet to get parameters from.
*
*/
private void initRadioGroup(Context context, AttributeSet attrs) {
if (attrs != null) {
parseRadioGroupAttributes(attrs);
}
}
/**
*
* Adds a view to this RadioGroup. Since only RadioButtons are supported as
* children this implementation ensures that the view to be added is
* actually a RadioButton before adding it.
*
* @param child
* The child to be added.
*
* @param idx
* The position where to add the child.
*
*/
@Override
public void addView(View child, int idx) {
if (!(child instanceof RadioButton)) {
Assert.FAIL("RadioGroup does not support " + child.getClass().getName()
+ " as children");
}
child.xmlvmSetParent(this);
String title = ((RadioButton) child).getText();
((RadioGroupAdapter) getCommonView()).insertSegmentWithTitle(title, idx, false);
radioButtons.add(idx, (RadioButton) child);
}
/**
*
* Adds a view to this RadioGroup. Since only RadioButtons are supported as
* children this implementation ensures that the view to be added is
* actually a RadioButton before adding it.
*
* @param child
* The child to be added.
*
* @param layoutParams
* The layout parameters to be used when layouting the provided
* child.
*
*/
@Override
public void addView(View child, ViewGroup.LayoutParams layoutParams) {
if (!(child instanceof RadioButton)) {
Assert.FAIL("RadioGroup does not support " + child.getClass().getName()
+ " as children");
}
child.xmlvmSetParent(this);
String title = ((RadioButton) child).getText();
int position = radioButtons.size();
((RadioGroupAdapter) getCommonView()).insertSegmentWithTitle(title, position, false);
radioButtons.add((RadioButton) child);
}
/**
*
* Adds a view to this RadioGroup. Since only RadioButtons are supported as
* children this implementation ensures that the view to be added is
* actually a RadioButton before adding it.
*
* @param child
* The child to be added.
*
*/
@Override
public void addView(View child) {
if (!(child instanceof RadioButton)) {
Assert.FAIL("RadioGroup does not support " + child.getClass().getName()
+ " as children");
}
child.xmlvmSetParent(this);
String title = ((RadioButton) child).getText();
int position = radioButtons.size();
((RadioGroupAdapter) getCommonView()).insertSegmentWithTitle(title, position, false);
radioButtons.add((RadioButton) child);
}
/**
*
* Parses RadioGroup attributes from the provided AttributeSet.
*
* @param attrs
* The attributes to parse.
*
*/
private void parseRadioGroupAttributes(AttributeSet attrs) {
setIgnoreRequestLayout(true);
// Parse attributes
setIgnoreRequestLayout(false);
}
/**
*
* Measures the size of the UISegmentedControl taking the provided
* MeasureSpec into account. Height is determined by the controls font and
* padding while the width is computed based on the number of segments,
* padding and the length of the longest title to be displayed.
*
* @param widthMeasureSpec
* The measure spec to consider when determining the
* RadioGroups's width.
*
* @param heightMeasureSpec
* The measure spec to consider when determining the
* RadioGroups's height.
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int minWidth = getSuggestedMinimumWidth();
int minHeight = getSuggestedMinimumHeight();
// Determine maximum string text length
float maxWidth = 0.0f;
float maxHeight = 0.0f;
for (RadioButton rb : radioButtons) {
RectF s = getTextSize(rb.getText());
if (s.width() > maxWidth) {
maxWidth = s.width();
}
if (s.height() > maxHeight) {
maxHeight = s.height();
}
}
int computedWidth = radioButtons.size() * (2 * INTERNAL_PADDING_X + (int) maxWidth);
int computedHeight = 2 * INTERNAL_PADDING_y + (int) maxHeight;
int width = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY ? MeasureSpec
.getSize(widthMeasureSpec) : computedWidth;
int height = MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY ? MeasureSpec
.getSize(heightMeasureSpec) : computedHeight;
setMeasuredDimension(Math.max(width, minWidth), Math.max(height, minHeight));
}
/**
*
* Determines the size a text will need using the system font.
*
* @param text
* The text to be tested.
*
* @return The size of the given text.
*
*/
private RectF getTextSize(String text) {
CommonFont font = CommonDeviceAPIFinder
.instance()
.getFontFactory()
.systemFontOfSize(CommonDeviceAPIFinder.instance().getFontFactory().labelFontSize());
RectF mSize = CommonDeviceAPIFinder.instance().getFontFactory().sizeWithFont("M", font);
RectF textSize = CommonDeviceAPIFinder.instance().getFontFactory().sizeWithFont(text, font);
if (text.length() == 0) {
textSize.bottom = textSize.top + mSize.height();
}
return textSize;
}
/**
*
* Searches the contained RadioButtons for on matching a given id.
*
* @param id
* The id to look for.
*
* @return The RadioButton with the given ID, null if none is found.
*
*/
@Override
protected View findViewTraversal(int id) {
if (id == getId()) {
return this;
}
for (View v : radioButtons) {
v = v.findViewById(id);
if (v != null) {
return v;
}
}
return null;
}
/**
*
* Creates the new iPhone widget to which RadioGroup is being mapped. This
* is a UISegmentedControl.
*
*/
@Override
protected CommonView xmlvmNewCommonDeviceView(AttributeSet attrs) {
return CommonDeviceAPIFinder.instance().getWidgetFactory().createRadioGroup(this);
}
/**
*
* Distributes an OnClickEvent to the proper RadioButton's OnClickListener.
* First the selected RadioButton is determined. If that RadioButton has an
* OnClickListener set it will be called.
*
*/
public void distributeOnClick() {
int index = ((RadioGroupAdapter) getCommonView()).getSelectedSegmentIndex();
if (index >= 0) {
radioButtons.get(index).callOnClickListener();
}
}
/**
*
* Selects the provided RadioButton by setting the UISegmentedControl's
* selected segment index.
*
* @param button
* The RadioButton to be selected.
*
*/
void setChecked(RadioButton button) {
int index = radioButtons.indexOf(button);
((RadioGroupAdapter) getCommonView()).setSelectedSegmentIndex(index);
}
/**
*
* Sets the title for the given RadioButton by setting the
* UISegmentedControl's title at the index associated with the button.
*
* @param button
* The RadioButton which triggered setting the text.
*
* @param text
* The text to be set.
*
*/
void setText(RadioButton button, String text) {
int index = radioButtons.indexOf(button);
((RadioGroupAdapter) getCommonView()).setTitle(text, index);
}
/**
*
* Determines the currently selected RadioButton.
*
* @return The determined RadioButton or null, if none is selected.
*
*/
RadioButton getSelectedRadioButton() {
int index = ((RadioGroupAdapter) getCommonView()).getSelectedSegmentIndex();
return index >= 0 ? radioButtons.get(index) : null;
}
}