/* * Copyright (C) 2013 Ian G. Clifton * Code featured in Android User Interface Design: Turning Ideas and * Sketches into Beautifully Designed Apps (ISBN-10: 0321886739; * ISBN-13: 978-0321886736). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.iangclifton.auid.multibutton; import android.annotation.SuppressLint; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.TypedArray; import android.os.Parcel; import android.os.Parcelable; import android.util.AttributeSet; import android.view.InflateException; import android.view.View; import android.view.View.OnClickListener; import android.widget.CompoundButton; import android.widget.LinearLayout; import android.widget.ToggleButton; /** * A LinearLayout that creates multiple ToggleButtons, only one of which can * be selected at a time. * * @author Ian G. Clifton */ public class MultiButton extends LinearLayout implements OnClickListener { private static final int DEFAULT = -1; private OnMultiButtonCheckedListener mListener; private CompoundButton mSelectedButton; /** * Interface that listens to the MultiButton being checked */ public interface OnMultiButtonCheckedListener { /** * Notifies the interface that a new button has been checked * * @param checked CompoundButton that is now checked * @param unchecked CompoundButton that was checked previously */ public void onMultiButtonChecked(CompoundButton checked, CompoundButton unchecked); } public MultiButton(Context context, int textArrayResourceId) { super(context); init(context, null, textArrayResourceId); } public MultiButton(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs, -1); } @SuppressLint("NewApi") public MultiButton(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context, attrs, -1); } /** * Returns the currently checked button * * @return CompoundButton that is currently checked */ public CompoundButton getCheckedButton() { return mSelectedButton; } @Override public void onClick(View v) { final CompoundButton button = (CompoundButton) v; button.setChecked(true); if (v != mSelectedButton) { mSelectedButton.setChecked(false); if (mListener != null) { mListener.onMultiButtonChecked(button, mSelectedButton); } mSelectedButton = button; } } /** * Sets the listener to be notified of a new button being checked * * @param listener OnMultiButtonCheckedListener to notify, can be null */ public void setOnMultiButtonCheckedListener(OnMultiButtonCheckedListener listener) { mListener = listener; } @Override protected void onRestoreInstanceState(Parcelable state) { if (!(state instanceof SavedState)) { // Not our SavedState, so call super and be done with it super.onRestoreInstanceState(state); return; } final SavedState ourState = (SavedState) state; super.onRestoreInstanceState(ourState.getSuperState()); mSelectedButton.setChecked(false); mSelectedButton = (CompoundButton) getChildAt(ourState.selectedPosition); mSelectedButton.setChecked(true); } @Override protected Parcelable onSaveInstanceState() { final Parcelable superState = super.onSaveInstanceState(); final SavedState ourState = new SavedState(superState); final int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { if (getChildAt(i) == mSelectedButton) { ourState.selectedPosition = i; break; } } return ourState; } /** * Initializes the Buttons inside this View * * @param context Context to create Buttons with * @param attrs AttributeSet or null */ private void init(Context context, AttributeSet attrs, int textArrayResourceId) { setSaveEnabled(true); final CharSequence[] textArray; int buttonBackground = DEFAULT; int buttonBackgroundEnd = DEFAULT; ColorStateList textColors = null; if (attrs == null) { textArray = context.getResources().getTextArray(textArrayResourceId); } else { // Load custom attributes final TypedArray customAttrs = context.obtainStyledAttributes(attrs, R.styleable.MultiButton); buttonBackground = customAttrs.getResourceId(R.styleable.MultiButton_buttonBackground, DEFAULT); buttonBackgroundEnd = customAttrs.getResourceId(R.styleable.MultiButton_buttonBackgroundEnd, DEFAULT); if (buttonBackgroundEnd == DEFAULT) { buttonBackgroundEnd = buttonBackground; } textColors = customAttrs.getColorStateList(R.styleable.MultiButton_android_textColor); textArray = customAttrs.getTextArray(R.styleable.MultiButton_textArray); customAttrs.recycle(); } if (textArray == null) { throw new InflateException("MultiButton requires a textArray"); } // Loop through CharSequences to create ToggleButtons for (int i = 0; i < textArray.length; i++) { final CharSequence text = textArray[i]; final ToggleButton button = new ToggleButton(context); addView(button); button.setOnClickListener(this); button.setText(text); button.setTextOff(text); button.setTextOn(text); // Add weight LayoutParams lp = (LayoutParams) button.getLayoutParams(); lp.weight = 1f; // Assign text color if (textColors != null) { button.setTextColor(textColors); } // Assign custom backgrounds if (i == textArray.length - 1) { if (buttonBackgroundEnd != DEFAULT) { button.setBackgroundResource(buttonBackgroundEnd); } } else { if (buttonBackground != DEFAULT) { button.setBackgroundResource(buttonBackground); } } // Obtain reference to first button if (mSelectedButton == null) { mSelectedButton = button; mSelectedButton.setChecked(true); } } } private static class SavedState extends BaseSavedState { private int selectedPosition; @SuppressWarnings("unused") public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() { public SavedState createFromParcel(Parcel in) { return new SavedState(in); } public SavedState[] newArray(int size) { return new SavedState[size]; } }; public SavedState(Parcelable superState) { super(superState); } private SavedState(Parcel in) { super(in); selectedPosition = in.readInt(); } @Override public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeInt(selectedPosition); } } }