MultiStateButton.java revision ac6a81dcde848f58b67b20feec47fb79c10cd2dc
1ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik/* 2ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik * Copyright (C) 2010 The Android Open Source Project 3ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik * 4ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik * Licensed under the Apache License, Version 2.0 (the "License"); 5ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik * you may not use this file except in compliance with the License. 6ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik * You may obtain a copy of the License at 7ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik * 8ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik * http://www.apache.org/licenses/LICENSE-2.0 9ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik * 10ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik * Unless required by applicable law or agreed to in writing, software 11ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik * distributed under the License is distributed on an "AS IS" BASIS, 12ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik * See the License for the specific language governing permissions and 14ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik * limitations under the License. 15ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik */ 16ac6a81dcde848f58b67b20feec47fb79c10cd2dcErikpackage com.android.calendar; 17ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik 18ac6a81dcde848f58b67b20feec47fb79c10cd2dcErikimport android.content.Context; 19ac6a81dcde848f58b67b20feec47fb79c10cd2dcErikimport android.content.res.TypedArray; 20ac6a81dcde848f58b67b20feec47fb79c10cd2dcErikimport android.graphics.Canvas; 21ac6a81dcde848f58b67b20feec47fb79c10cd2dcErikimport android.graphics.drawable.Drawable; 22ac6a81dcde848f58b67b20feec47fb79c10cd2dcErikimport android.util.AttributeSet; 23ac6a81dcde848f58b67b20feec47fb79c10cd2dcErikimport android.util.Log; 24ac6a81dcde848f58b67b20feec47fb79c10cd2dcErikimport android.view.Gravity; 25ac6a81dcde848f58b67b20feec47fb79c10cd2dcErikimport android.widget.Button; 26ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik 27ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik/** 28ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik * <p> 29ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik * A button with more than two states. When the button is pressed 30ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik * or clicked, the state transitions automatically. 31ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik * </p> 32ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik * 33ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik * <p><strong>XML attributes</strong></p> 34ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik * <p> 35ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik * See {@link R.styleable#MultiStateButton 36ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik * MultiStateButton Attributes}, {@link android.R.styleable#Button Button 37ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik * Attributes}, {@link android.R.styleable#TextView TextView Attributes}, {@link 38ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik * android.R.styleable#View View Attributes} 39ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik * </p> 40ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik */ 41ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik 42ac6a81dcde848f58b67b20feec47fb79c10cd2dcErikpublic class MultiStateButton extends Button { 43ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik //The current state for this button, ranging from 0 to maxState-1 44ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik private int mState; 45ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik //The maximum number of states allowed for this button. 46ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik private int mMaxStates; 47ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik //The currently displaying resource ID. This gets set to a default on creation and remains 48ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik //on the last set if the resources get set to null. 49ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik private int mButtonResource; 50ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik //A list of all drawable resources used by this button in the order it uses them. 51ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik private int[] mButtonResources; 52ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik private Drawable mButtonDrawable; 53ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik private String TAG = "MSB"; 54ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik 55ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik public MultiStateButton(Context context) { 56ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik this(context, null); 57ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik } 58ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik 59ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik public MultiStateButton(Context context, AttributeSet attrs) { 60ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik this(context, attrs, 0); 61ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik } 62ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik 63ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik public MultiStateButton(Context context, AttributeSet attrs, int defStyle) { 64ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik //Currently using the standard buttonStyle, will update when new resources are added. 65ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik super(context, attrs, defStyle); 66ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik mMaxStates = 1; 67ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik mState = 0; 68ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik //TODO add a more generic default button 69ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik mButtonResources = new int[] { R.drawable.widget_show }; 70ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik setButtonDrawable(mButtonResources[mState]); 71ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik } 72ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik 73ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik @Override 74ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik public boolean performClick() { 75ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik /* When clicked, toggle the state */ 76ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik transitionState(); 77ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik return super.performClick(); 78ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik } 79ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik 80ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik public void transitionState() { 81ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik mState = (mState + 1) % mMaxStates; 82ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik setButtonDrawable(mButtonResources[mState]); 83ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik } 84ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik 85ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik /** 86ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik * Allows for a new set of drawable resource ids to be set. 87ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik * 88ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik * This sets the maximum states allowed to the length of the resources array. It will also 89ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik * set the current state to the maximum allowed if it's greater than the new max. 90ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik */ 91ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik public void setButtonResources(int[] resources) throws IllegalArgumentException { 92ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik if(resources == null) { 93ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik throw new IllegalArgumentException("Button resources cannot be null"); 94ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik } 95ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik mMaxStates = resources.length; 96ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik if(mState >= mMaxStates) { 97ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik mState = mMaxStates - 1; 98ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik } 99ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik mButtonResources = resources; 100ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik } 101ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik 102ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik /** 103ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik * Attempts to set the state. Returns true if successful, false otherwise. 104ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik */ 105ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik public boolean setState(int state){ 106ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik if(state >= mMaxStates || state < 0) { 107ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik //When moved out of Calendar the tag should be changed. 108ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik Log.w("Cal", "MultiStateButton state set to value greater than maxState or < 0"); 109ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik return false; 110ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik } 111ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik mState = state; 112ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik setButtonDrawable(mButtonResources[mState]); 113ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik return true; 114ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik } 115ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik 116ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik public int getState() { 117ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik return mState; 118ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik } 119ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik 120ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik /** 121ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik * Set the background to a given Drawable, identified by its resource id. 122ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik * 123ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik * @param resid the resource id of the drawable to use as the background 124ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik */ 125ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik public void setButtonDrawable(int resid) { 126ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik if (resid != 0 && resid == mButtonResource) { 127ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik return; 128ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik } 129ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik 130ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik mButtonResource = resid; 131ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik 132ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik Drawable d = null; 133ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik if (mButtonResource != 0) { 134ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik d = getResources().getDrawable(mButtonResource); 135ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik } 136ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik setButtonDrawable(d); 137ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik } 138ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik 139ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik /** 140ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik * Set the background to a given Drawable 141ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik * 142ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik * @param d The Drawable to use as the background 143ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik */ 144ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik public void setButtonDrawable(Drawable d) { 145ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik if (d != null) { 146ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik if (mButtonDrawable != null) { 147ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik mButtonDrawable.setCallback(null); 148ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik unscheduleDrawable(mButtonDrawable); 149ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik } 150ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik d.setCallback(this); 151ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik d.setState(getDrawableState()); 152ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik d.setVisible(getVisibility() == VISIBLE, false); 153ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik mButtonDrawable = d; 154ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik mButtonDrawable.setState(null); 155ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik setMinHeight(mButtonDrawable.getIntrinsicHeight()); 156ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik setWidth(mButtonDrawable.getIntrinsicWidth()); 157ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik } 158ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik refreshDrawableState(); 159ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik } 160ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik 161ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik @Override 162ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik protected void onDraw(Canvas canvas) { 163ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik super.onDraw(canvas); 164ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik if (mButtonDrawable != null) { 165ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik final int verticalGravity = getGravity() & Gravity.VERTICAL_GRAVITY_MASK; 166ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik final int horizontalGravity = getGravity() & Gravity.HORIZONTAL_GRAVITY_MASK; 167ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik final int height = mButtonDrawable.getIntrinsicHeight(); 168ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik final int width = mButtonDrawable.getIntrinsicWidth(); 169ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik 170ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik int y = 0; 171ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik int x = 0; 172ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik 173ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik switch (verticalGravity) { 174ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik case Gravity.BOTTOM: 175ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik y = getHeight() - height; 176ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik break; 177ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik case Gravity.CENTER_VERTICAL: 178ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik y = (getHeight() - height) / 2; 179ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik break; 180ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik } 181ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik switch (horizontalGravity) { 182ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik case Gravity.RIGHT: 183ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik x = getWidth() - width; 184ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik break; 185ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik case Gravity.CENTER_HORIZONTAL: 186ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik x = (getWidth() - width) / 2; 187ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik break; 188ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik } 189ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik 190ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik mButtonDrawable.setBounds(x, y, x + width, y + height); 191ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik mButtonDrawable.draw(canvas); 192ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik } 193ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik } 194ac6a81dcde848f58b67b20feec47fb79c10cd2dcErik} 195