update
This commit is contained in:
@@ -0,0 +1,201 @@
|
||||
package com.shayu.onetoone.widget.flexboxradiogroup;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.ViewDebug;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
import android.widget.Checkable;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
public class CustomizableRadioButton extends FrameLayout implements Checkable {
|
||||
private boolean mChecked = false;
|
||||
private CompoundButton.OnCheckedChangeListener mOnCheckedChangeListener;
|
||||
private CompoundButton compoundButton;
|
||||
private boolean mBroadcasting;
|
||||
|
||||
public CustomizableRadioButton(Context context) {
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
public CustomizableRadioButton(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
setClickable(true);
|
||||
setFocusable(true);
|
||||
|
||||
compoundButton = new CompoundButton(getContext()) {
|
||||
@Override
|
||||
public int getId() {
|
||||
return CustomizableRadioButton.this.getId();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* If the radio button is already checked, this method will not toggle the radio button.
|
||||
*/
|
||||
@Override
|
||||
public void toggle() {
|
||||
// we override to prevent toggle when the radio is already
|
||||
// checked (as opposed to check boxes widgets)
|
||||
if (!isChecked()) {
|
||||
setChecked(!mChecked);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performClick() {
|
||||
/*
|
||||
* XXX: These are tiny, need some surrounding 'expanded touch area',
|
||||
* which will need to be implemented in Button if we only override
|
||||
* performClick()
|
||||
*/
|
||||
|
||||
/* When clicked, toggle the state */
|
||||
toggle();
|
||||
return super.performClick();
|
||||
}
|
||||
|
||||
@ViewDebug.ExportedProperty
|
||||
public boolean isChecked() {
|
||||
return mChecked;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Changes the checked state of this button.</p>
|
||||
*
|
||||
* @param checked true to check the button, false to uncheck it
|
||||
*/
|
||||
public void setChecked(boolean checked) {
|
||||
if (mChecked != checked) {
|
||||
mChecked = checked;
|
||||
refreshDrawableState();
|
||||
compoundButton.setChecked(checked);
|
||||
|
||||
// Avoid infinite recursions if setChecked() is called from a listener
|
||||
if (mBroadcasting) {
|
||||
return;
|
||||
}
|
||||
|
||||
mBroadcasting = true;
|
||||
if (mOnCheckedChangeListener != null) {
|
||||
mOnCheckedChangeListener.onCheckedChanged(compoundButton, mChecked);
|
||||
}
|
||||
|
||||
|
||||
mBroadcasting = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback to be invoked when the checked state of this button
|
||||
* changes.
|
||||
*
|
||||
* @param listener the callback to call on checked state change
|
||||
*/
|
||||
public void setOnCheckedChangeListener(CompoundButton.OnCheckedChangeListener listener) {
|
||||
mOnCheckedChangeListener = listener;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
|
||||
super.onInitializeAccessibilityEvent(event);
|
||||
event.setClassName(CustomizableRadioButton.class.getName());
|
||||
event.setChecked(mChecked);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
|
||||
super.onInitializeAccessibilityNodeInfo(info);
|
||||
info.setClassName(CustomizableRadioButton.class.getName());
|
||||
info.setCheckable(true);
|
||||
info.setChecked(mChecked);
|
||||
}
|
||||
|
||||
private static final int[] CHECKED_STATE_SET = {android.R.attr.state_checked};
|
||||
|
||||
@Override
|
||||
protected int[] onCreateDrawableState(int extraSpace) {
|
||||
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
|
||||
if (isChecked()) {
|
||||
mergeDrawableStates(drawableState, CHECKED_STATE_SET);
|
||||
}
|
||||
return drawableState;
|
||||
}
|
||||
|
||||
|
||||
static class SavedState extends BaseSavedState {
|
||||
boolean checked;
|
||||
|
||||
/**
|
||||
* Constructor called from {@link CompoundButton#onSaveInstanceState()}
|
||||
*/
|
||||
SavedState(Parcelable superState) {
|
||||
super(superState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor called from {@link #CREATOR}
|
||||
*/
|
||||
private SavedState(Parcel in) {
|
||||
super(in);
|
||||
checked = (Boolean) in.readValue(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
super.writeToParcel(out, flags);
|
||||
out.writeValue(checked);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CompoundButton.SavedState{"
|
||||
+ Integer.toHexString(System.identityHashCode(this))
|
||||
+ " checked=" + checked + "}";
|
||||
}
|
||||
|
||||
public static final Creator<SavedState> CREATOR
|
||||
= new Creator<SavedState>() {
|
||||
public SavedState createFromParcel(Parcel in) {
|
||||
return new SavedState(in);
|
||||
}
|
||||
|
||||
public SavedState[] newArray(int size) {
|
||||
return new SavedState[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parcelable onSaveInstanceState() {
|
||||
// Force our ancestor class to save its state
|
||||
|
||||
Parcelable superState = super.onSaveInstanceState();
|
||||
|
||||
SavedState ss = new SavedState(superState);
|
||||
|
||||
ss.checked = isChecked();
|
||||
return ss;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRestoreInstanceState(Parcelable state) {
|
||||
SavedState ss = (SavedState) state;
|
||||
|
||||
super.onRestoreInstanceState(ss.getSuperState());
|
||||
setChecked(ss.checked);
|
||||
requestLayout();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,363 @@
|
||||
package com.shayu.onetoone.widget.flexboxradiogroup;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
import android.widget.CompoundButton;
|
||||
|
||||
import android.widget.RadioButton;
|
||||
import android.widget.RadioGroup;
|
||||
|
||||
import com.google.android.flexbox.FlexDirection;
|
||||
import com.google.android.flexbox.FlexboxLayout;
|
||||
import com.shayu.onetoone.R;
|
||||
|
||||
public class FlexBoxRadioGroup extends FlexboxLayout {
|
||||
|
||||
|
||||
// holds the checked id; the selection is empty by default
|
||||
private int mCheckedId = -1;
|
||||
// tracks children radio buttons checked state
|
||||
private CompoundButton.OnCheckedChangeListener mChildOnCheckedChangeListener;
|
||||
// when true, mOnCheckedChangeListener discards events
|
||||
private boolean mProtectFromCheckedChange = false;
|
||||
private OnCheckedChangeListener mOnCheckedChangeListener;
|
||||
private PassThroughHierarchyChangeListener mPassThroughListener;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public FlexBoxRadioGroup(Context context) {
|
||||
super(context);
|
||||
setFlexDirection(FlexDirection.ROW);
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public FlexBoxRadioGroup(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
// retrieve selected radio button as requested by the user in the
|
||||
// XML layout file
|
||||
TypedArray attributes = context.obtainStyledAttributes(
|
||||
attrs, R.styleable.FlexBoxRadioGroup);
|
||||
|
||||
int value = attributes.getResourceId(R.styleable.FlexBoxRadioGroup_uiCheckedButton, View.NO_ID);
|
||||
if (value != View.NO_ID) {
|
||||
mCheckedId = value;
|
||||
}
|
||||
|
||||
attributes.recycle();
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
mChildOnCheckedChangeListener = new CheckedStateTracker();
|
||||
mPassThroughListener = new PassThroughHierarchyChangeListener();
|
||||
super.setOnHierarchyChangeListener(mPassThroughListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) {
|
||||
// the user listener is delegated to our pass-through listener
|
||||
mPassThroughListener.mOnHierarchyChangeListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
|
||||
// checks the appropriate radio button as requested in the XML file
|
||||
if (mCheckedId != -1) {
|
||||
mProtectFromCheckedChange = true;
|
||||
setCheckedStateForView(mCheckedId, true);
|
||||
mProtectFromCheckedChange = false;
|
||||
setCheckedId(mCheckedId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addView(View child, int index, ViewGroup.LayoutParams params) {
|
||||
if (child instanceof RadioButton) {
|
||||
final RadioButton button = (RadioButton) child;
|
||||
if (button.isChecked()) {
|
||||
mProtectFromCheckedChange = true;
|
||||
if (mCheckedId != -1) {
|
||||
setCheckedStateForView(mCheckedId, false);
|
||||
}
|
||||
mProtectFromCheckedChange = false;
|
||||
setCheckedId(button.getId());
|
||||
}
|
||||
}
|
||||
|
||||
super.addView(child, index, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets the selection to the radio button whose identifier is passed in
|
||||
* parameter. Using -1 as the selection identifier clears the selection;
|
||||
* such an operation is equivalent to invoking {@link #clearCheck()}.</p>
|
||||
*
|
||||
* @param id the unique id of the radio button to select in this group
|
||||
* @see #getCheckedRadioButtonId()
|
||||
* @see #clearCheck()
|
||||
*/
|
||||
public void check(int id) {
|
||||
// don't even bother
|
||||
if (id != -1 && (id == mCheckedId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mCheckedId != -1) {
|
||||
setCheckedStateForView(mCheckedId, false);
|
||||
}
|
||||
|
||||
if (id != -1) {
|
||||
setCheckedStateForView(id, true);
|
||||
}
|
||||
|
||||
setCheckedId(id);
|
||||
}
|
||||
|
||||
private void setCheckedId(int id) {
|
||||
mCheckedId = id;
|
||||
if (mOnCheckedChangeListener != null) {
|
||||
mOnCheckedChangeListener.onCheckedChanged(this, mCheckedId);
|
||||
}
|
||||
}
|
||||
|
||||
private void setCheckedStateForView(int viewId, boolean checked) {
|
||||
View checkedView = findViewById(viewId);
|
||||
if (checkedView != null && checkedView instanceof RadioButton) {
|
||||
((RadioButton) checkedView).setChecked(checked);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns the identifier of the selected radio button in this group.
|
||||
* Upon empty selection, the returned value is -1.</p>
|
||||
*
|
||||
* @return the unique id of the selected radio button in this group
|
||||
* @attr ref android.R.styleable#RadioGroup_checkedButton
|
||||
* @see #check(int)
|
||||
* @see #clearCheck()
|
||||
*/
|
||||
public int getCheckedRadioButtonId() {
|
||||
return mCheckedId;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Clears the selection. When the selection is cleared, no radio button
|
||||
* in this group is selected and {@link #getCheckedRadioButtonId()} returns
|
||||
* null.</p>
|
||||
*
|
||||
* @see #check(int)
|
||||
* @see #getCheckedRadioButtonId()
|
||||
*/
|
||||
public void clearCheck() {
|
||||
check(-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Register a callback to be invoked when the checked radio button
|
||||
* changes in this group.</p>
|
||||
*
|
||||
* @param listener the callback to call on checked state change
|
||||
*/
|
||||
public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
|
||||
mOnCheckedChangeListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
// @Override
|
||||
// public FlexBoxRadioGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
|
||||
// return new FlexBoxRadioGroup.LayoutParams(getContext(), attrs);
|
||||
// }
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
|
||||
return p instanceof RadioGroup.LayoutParams;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FlexboxLayout.LayoutParams generateDefaultLayoutParams() {
|
||||
return new LayoutParams(RadioGroup.LayoutParams.WRAP_CONTENT, RadioGroup.LayoutParams.WRAP_CONTENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
|
||||
super.onInitializeAccessibilityEvent(event);
|
||||
event.setClassName(RadioGroup.class.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
|
||||
super.onInitializeAccessibilityNodeInfo(info);
|
||||
info.setClassName(RadioGroup.class.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>This set of layout parameters defaults the width and the height of
|
||||
* the children to {@link #WRAP_CONTENT} when they are not specified in the
|
||||
* XML file. Otherwise, this class uses the value read from the XML file.</p>
|
||||
*/
|
||||
public static class LayoutParams extends FlexboxLayout.LayoutParams {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public LayoutParams(Context c, AttributeSet attrs) {
|
||||
super(c, attrs);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public LayoutParams(int w, int h) {
|
||||
super(w, h);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * {@inheritDoc}
|
||||
// */
|
||||
// public LayoutParams(int w, int h, float initWeight) {
|
||||
// super(w, h, initWeight);
|
||||
// }
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public LayoutParams(ViewGroup.LayoutParams p) {
|
||||
super(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public LayoutParams(MarginLayoutParams source) {
|
||||
super(source);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Fixes the child's width to
|
||||
* {@link ViewGroup.LayoutParams#WRAP_CONTENT} and the child's
|
||||
* height to {@link ViewGroup.LayoutParams#WRAP_CONTENT}
|
||||
* when not specified in the XML file.</p>
|
||||
*
|
||||
* @param a the styled attributes set
|
||||
* @param widthAttr the width attribute to fetch
|
||||
* @param heightAttr the height attribute to fetch
|
||||
*/
|
||||
@Override
|
||||
protected void setBaseAttributes(TypedArray a,
|
||||
int widthAttr, int heightAttr) {
|
||||
|
||||
if (a.hasValue(widthAttr)) {
|
||||
width = a.getLayoutDimension(widthAttr, "layout_width");
|
||||
} else {
|
||||
width = WRAP_CONTENT;
|
||||
}
|
||||
|
||||
if (a.hasValue(heightAttr)) {
|
||||
height = a.getLayoutDimension(heightAttr, "layout_height");
|
||||
} else {
|
||||
height = WRAP_CONTENT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Interface definition for a callback to be invoked when the checked
|
||||
* radio button changed in this group.</p>
|
||||
*/
|
||||
public interface OnCheckedChangeListener {
|
||||
/**
|
||||
* <p>Called when the checked radio button has changed. When the
|
||||
* selection is cleared, checkedId is -1.</p>
|
||||
*
|
||||
* @param group the group in which the checked radio button has changed
|
||||
* @param checkedId the unique identifier of the newly checked radio button
|
||||
*/
|
||||
void onCheckedChanged(FlexBoxRadioGroup group, int checkedId);
|
||||
}
|
||||
|
||||
private class CheckedStateTracker implements CompoundButton.OnCheckedChangeListener {
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
// prevents from infinite recursion
|
||||
if (mProtectFromCheckedChange) {
|
||||
return;
|
||||
}
|
||||
|
||||
mProtectFromCheckedChange = true;
|
||||
if (mCheckedId != -1) {
|
||||
setCheckedStateForView(mCheckedId, false);
|
||||
}
|
||||
mProtectFromCheckedChange = false;
|
||||
|
||||
int id = buttonView.getId();
|
||||
setCheckedId(id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>A pass-through listener acts upon the events and dispatches them
|
||||
* to another listener. This allows the table layout to set its own internal
|
||||
* hierarchy change listener without preventing the user to setup his.</p>
|
||||
*/
|
||||
private class PassThroughHierarchyChangeListener implements
|
||||
OnHierarchyChangeListener {
|
||||
private OnHierarchyChangeListener mOnHierarchyChangeListener;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void onChildViewAdded(View parent, View child) {
|
||||
if (parent == FlexBoxRadioGroup.this && child instanceof RadioButton) {
|
||||
int id = child.getId();
|
||||
// generates an id if it's missing
|
||||
if (id == View.NO_ID) {
|
||||
id = child.hashCode();
|
||||
child.setId(id);
|
||||
}
|
||||
((RadioButton) child).setOnCheckedChangeListener(
|
||||
mChildOnCheckedChangeListener);
|
||||
}
|
||||
|
||||
if (mOnHierarchyChangeListener != null) {
|
||||
mOnHierarchyChangeListener.onChildViewAdded(parent, child);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void onChildViewRemoved(View parent, View child) {
|
||||
if (parent == FlexBoxRadioGroup.this && child instanceof RadioButton) {
|
||||
((RadioButton) child).setOnCheckedChangeListener(null);
|
||||
}
|
||||
|
||||
if (mOnHierarchyChangeListener != null) {
|
||||
mOnHierarchyChangeListener.onChildViewRemoved(parent, child);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,396 @@
|
||||
package com.shayu.onetoone.widget.flexboxradiogroup;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
import android.widget.Checkable;
|
||||
import android.widget.CompoundButton;
|
||||
|
||||
import android.widget.RadioButton;
|
||||
import android.widget.RadioGroup;
|
||||
|
||||
import com.google.android.flexbox.FlexDirection;
|
||||
import com.google.android.flexbox.FlexboxLayout;
|
||||
import com.shayu.onetoone.R;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
|
||||
public class FlexBoxSingleCheckableGroup extends FlexboxLayout {
|
||||
|
||||
private static final String TAG = "FlexBoxSingleCheckableG";
|
||||
// holds the checked id; the selection is empty by default
|
||||
private int mCheckedId = -1;
|
||||
// tracks children radio buttons checked state
|
||||
private CompoundButton.OnCheckedChangeListener mChildOnCheckedChangeListener;
|
||||
// when true, mOnCheckedChangeListener discards events
|
||||
private boolean mProtectFromCheckedChange = false;
|
||||
private OnCheckedChangeListener mOnCheckedChangeListener;
|
||||
private PassThroughHierarchyChangeListener mPassThroughListener;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public FlexBoxSingleCheckableGroup(Context context) {
|
||||
super(context);
|
||||
setFlexDirection(FlexDirection.ROW);
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public FlexBoxSingleCheckableGroup(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
// retrieve selected radio button as requested by the user in the
|
||||
// XML layout file
|
||||
TypedArray attributes = context.obtainStyledAttributes(
|
||||
attrs, R.styleable.FlexBoxRadioGroup);
|
||||
|
||||
int value = attributes.getResourceId(R.styleable.FlexBoxRadioGroup_uiCheckedButton, View.NO_ID);
|
||||
if (value != View.NO_ID) {
|
||||
mCheckedId = value;
|
||||
}
|
||||
|
||||
attributes.recycle();
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
mChildOnCheckedChangeListener = new CheckedStateTracker();
|
||||
mPassThroughListener = new PassThroughHierarchyChangeListener();
|
||||
super.setOnHierarchyChangeListener(mPassThroughListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) {
|
||||
// the user listener is delegated to our pass-through listener
|
||||
mPassThroughListener.mOnHierarchyChangeListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
|
||||
// checks the appropriate radio button as requested in the XML file
|
||||
if (mCheckedId != -1) {
|
||||
mProtectFromCheckedChange = true;
|
||||
setCheckedStateForView(mCheckedId, true);
|
||||
mProtectFromCheckedChange = false;
|
||||
setCheckedId(mCheckedId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void addView(View child, int index, ViewGroup.LayoutParams params, boolean isChecked) {
|
||||
if (child instanceof Checkable) {
|
||||
|
||||
if (isChecked) {
|
||||
mProtectFromCheckedChange = true;
|
||||
if (mCheckedId != -1) {
|
||||
setCheckedStateForView(mCheckedId, false);
|
||||
}
|
||||
mProtectFromCheckedChange = false;
|
||||
setCheckedId(child.getId());
|
||||
}
|
||||
}
|
||||
|
||||
super.addView(child, index, params);
|
||||
// if (child instanceof RadioButton) {
|
||||
// final RadioButton button = (RadioButton) child;
|
||||
// if (button.isChecked()) {
|
||||
// mProtectFromCheckedChange = true;
|
||||
// if (mCheckedId != -1) {
|
||||
// setCheckedStateForView(mCheckedId, false);
|
||||
// }
|
||||
// mProtectFromCheckedChange = false;
|
||||
// setCheckedId(button.getId());
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// super.addView(child, index, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets the selection to the radio button whose identifier is passed in
|
||||
* parameter. Using -1 as the selection identifier clears the selection;
|
||||
* such an operation is equivalent to invoking {@link #clearCheck()}.</p>
|
||||
*
|
||||
* @param id the unique id of the radio button to select in this group
|
||||
* @see #getCheckedRadioButtonId()
|
||||
* @see #clearCheck()
|
||||
*/
|
||||
public void check(int id) {
|
||||
// don't even bother
|
||||
if (id != -1 && (id == mCheckedId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mCheckedId != -1) {
|
||||
setCheckedStateForView(mCheckedId, false);
|
||||
}
|
||||
|
||||
if (id != -1) {
|
||||
setCheckedStateForView(id, true);
|
||||
}
|
||||
|
||||
setCheckedId(id);
|
||||
}
|
||||
|
||||
private void setCheckedId(int id) {
|
||||
mCheckedId = id;
|
||||
if (mOnCheckedChangeListener != null) {
|
||||
mOnCheckedChangeListener.onCheckedChanged(this, mCheckedId);
|
||||
}
|
||||
}
|
||||
|
||||
private void setCheckedStateForView(int viewId, boolean checked) {
|
||||
View checkedView = findViewById(viewId);
|
||||
if (checkedView != null && checkedView instanceof Checkable) {
|
||||
try {
|
||||
Method m = checkedView.getClass()
|
||||
.getMethod("setChecked", boolean.class);
|
||||
m.invoke(checkedView, checked);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns the identifier of the selected radio button in this group.
|
||||
* Upon empty selection, the returned value is -1.</p>
|
||||
*
|
||||
* @return the unique id of the selected radio button in this group
|
||||
* @attr ref android.R.styleable#RadioGroup_checkedButton
|
||||
* @see #check(int)
|
||||
* @see #clearCheck()
|
||||
*/
|
||||
public int getCheckedRadioButtonId() {
|
||||
return mCheckedId;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Clears the selection. When the selection is cleared, no radio button
|
||||
* in this group is selected and {@link #getCheckedRadioButtonId()} returns
|
||||
* null.</p>
|
||||
*
|
||||
* @see #check(int)
|
||||
* @see #getCheckedRadioButtonId()
|
||||
*/
|
||||
public void clearCheck() {
|
||||
check(-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Register a callback to be invoked when the checked radio button
|
||||
* changes in this group.</p>
|
||||
*
|
||||
* @param listener the callback to call on checked state change
|
||||
*/
|
||||
public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
|
||||
mOnCheckedChangeListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
// @Override
|
||||
// public FlexBoxRadioGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
|
||||
// return new FlexBoxRadioGroup.LayoutParams(getContext(), attrs);
|
||||
// }
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
|
||||
return p instanceof RadioGroup.LayoutParams;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FlexboxLayout.LayoutParams generateDefaultLayoutParams() {
|
||||
return new LayoutParams(RadioGroup.LayoutParams.WRAP_CONTENT, RadioGroup.LayoutParams.WRAP_CONTENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
|
||||
super.onInitializeAccessibilityEvent(event);
|
||||
event.setClassName(RadioGroup.class.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
|
||||
super.onInitializeAccessibilityNodeInfo(info);
|
||||
info.setClassName(RadioGroup.class.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>This set of layout parameters defaults the width and the height of
|
||||
* the children to {@link #WRAP_CONTENT} when they are not specified in the
|
||||
* XML file. Otherwise, this class uses the value read from the XML file.</p>
|
||||
*/
|
||||
public static class LayoutParams extends FlexboxLayout.LayoutParams {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public LayoutParams(Context c, AttributeSet attrs) {
|
||||
super(c, attrs);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public LayoutParams(int w, int h) {
|
||||
super(w, h);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * {@inheritDoc}
|
||||
// */
|
||||
// public LayoutParams(int w, int h, float initWeight) {
|
||||
// super(w, h, initWeight);
|
||||
// }
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public LayoutParams(ViewGroup.LayoutParams p) {
|
||||
super(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public LayoutParams(MarginLayoutParams source) {
|
||||
super(source);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Fixes the child's width to
|
||||
* {@link ViewGroup.LayoutParams#WRAP_CONTENT} and the child's
|
||||
* height to {@link ViewGroup.LayoutParams#WRAP_CONTENT}
|
||||
* when not specified in the XML file.</p>
|
||||
*
|
||||
* @param a the styled attributes set
|
||||
* @param widthAttr the width attribute to fetch
|
||||
* @param heightAttr the height attribute to fetch
|
||||
*/
|
||||
@Override
|
||||
protected void setBaseAttributes(TypedArray a,
|
||||
int widthAttr, int heightAttr) {
|
||||
|
||||
if (a.hasValue(widthAttr)) {
|
||||
width = a.getLayoutDimension(widthAttr, "layout_width");
|
||||
} else {
|
||||
width = WRAP_CONTENT;
|
||||
}
|
||||
|
||||
if (a.hasValue(heightAttr)) {
|
||||
height = a.getLayoutDimension(heightAttr, "layout_height");
|
||||
} else {
|
||||
height = WRAP_CONTENT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Interface definition for a callback to be invoked when the checked
|
||||
* radio button changed in this group.</p>
|
||||
*/
|
||||
public interface OnCheckedChangeListener {
|
||||
/**
|
||||
* <p>Called when the checked radio button has changed. When the
|
||||
* selection is cleared, checkedId is -1.</p>
|
||||
*
|
||||
* @param group the group in which the checked radio button has changed
|
||||
* @param checkedId the unique identifier of the newly checked radio button
|
||||
*/
|
||||
void onCheckedChanged(FlexBoxSingleCheckableGroup group, int checkedId);
|
||||
}
|
||||
|
||||
private class CheckedStateTracker implements CompoundButton.OnCheckedChangeListener {
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
// prevents from infinite recursion
|
||||
if (mProtectFromCheckedChange) {
|
||||
return;
|
||||
}
|
||||
|
||||
mProtectFromCheckedChange = true;
|
||||
if (mCheckedId != -1) {
|
||||
setCheckedStateForView(mCheckedId, false);
|
||||
}
|
||||
mProtectFromCheckedChange = false;
|
||||
|
||||
int id = buttonView.getId();
|
||||
setCheckedId(id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>A pass-through listener acts upon the events and dispatches them
|
||||
* to another listener. This allows the table layout to set its own internal
|
||||
* hierarchy change listener without preventing the user to setup his.</p>
|
||||
*/
|
||||
private class PassThroughHierarchyChangeListener implements
|
||||
OnHierarchyChangeListener {
|
||||
private OnHierarchyChangeListener mOnHierarchyChangeListener;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void onChildViewAdded(View parent, View child) {
|
||||
if (parent == FlexBoxSingleCheckableGroup.this && child instanceof Checkable) {
|
||||
int id = child.getId();
|
||||
// generates an id if it's missing
|
||||
if (id == View.NO_ID) {
|
||||
id = child.hashCode();
|
||||
child.setId(id);
|
||||
}
|
||||
try {
|
||||
Method m = child.getClass()
|
||||
.getMethod("setOnCheckedChangeListener", CompoundButton.OnCheckedChangeListener.class);
|
||||
m.invoke(child, mChildOnCheckedChangeListener);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "");
|
||||
}
|
||||
}
|
||||
|
||||
if (mOnHierarchyChangeListener != null) {
|
||||
mOnHierarchyChangeListener.onChildViewAdded(parent, child);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void onChildViewRemoved(View parent, View child) {
|
||||
if (parent == FlexBoxSingleCheckableGroup.this && child instanceof Checkable) {
|
||||
((RadioButton) child).setOnCheckedChangeListener(null);
|
||||
try {
|
||||
Method m = child.getClass()
|
||||
.getMethod("setOnCheckedChangeListener", CompoundButton.OnCheckedChangeListener.class);
|
||||
m.invoke(child, (Object[]) null);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "");
|
||||
}
|
||||
}
|
||||
|
||||
if (mOnHierarchyChangeListener != null) {
|
||||
mOnHierarchyChangeListener.onChildViewRemoved(parent, child);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user