LevelListDrawable.java revision 727cae197b123ef764a1f8fbe08a995b000d14c3
1/* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.graphics.drawable; 18 19import java.io.IOException; 20 21import org.xmlpull.v1.XmlPullParser; 22import org.xmlpull.v1.XmlPullParserException; 23 24import android.content.res.Resources; 25import android.content.res.TypedArray; 26import android.content.res.Resources.Theme; 27import android.util.AttributeSet; 28 29/** 30 * A resource that manages a number of alternate Drawables, each assigned a maximum numerical value. 31 * Setting the level value of the object with {@link #setLevel(int)} will load the image with the next 32 * greater or equal value assigned to its max attribute. 33 * A good example use of 34 * a LevelListDrawable would be a battery level indicator icon, with different images to indicate the current 35 * battery level. 36 * <p> 37 * It can be defined in an XML file with the <code><level-list></code> element. 38 * Each Drawable level is defined in a nested <code><item></code>. For example: 39 * </p> 40 * <pre> 41 * <level-list xmlns:android="http://schemas.android.com/apk/res/android"> 42 * <item android:maxLevel="0" android:drawable="@drawable/ic_wifi_signal_1" /> 43 * <item android:maxLevel="1" android:drawable="@drawable/ic_wifi_signal_2" /> 44 * <item android:maxLevel="2" android:drawable="@drawable/ic_wifi_signal_3" /> 45 * <item android:maxLevel="3" android:drawable="@drawable/ic_wifi_signal_4" /> 46 * </level-list> 47 *</pre> 48 * <p>With this XML saved into the res/drawable/ folder of the project, it can be referenced as 49 * the drawable for an {@link android.widget.ImageView}. The default image is the first in the list. 50 * It can then be changed to one of the other levels with 51 * {@link android.widget.ImageView#setImageLevel(int)}. For more 52 * information, see the guide to <a 53 * href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.</p> 54 * 55 * @attr ref android.R.styleable#LevelListDrawableItem_minLevel 56 * @attr ref android.R.styleable#LevelListDrawableItem_maxLevel 57 * @attr ref android.R.styleable#LevelListDrawableItem_drawable 58 */ 59public class LevelListDrawable extends DrawableContainer { 60 private final LevelListState mLevelListState; 61 private boolean mMutated; 62 63 public LevelListDrawable() { 64 this(null, null); 65 } 66 67 public void addLevel(int low, int high, Drawable drawable) { 68 if (drawable != null) { 69 mLevelListState.addLevel(low, high, drawable); 70 // in case the new state matches our current state... 71 onLevelChange(getLevel()); 72 } 73 } 74 75 // overrides from Drawable 76 77 @Override 78 protected boolean onLevelChange(int level) { 79 int idx = mLevelListState.indexOfLevel(level); 80 if (selectDrawable(idx)) { 81 return true; 82 } 83 return super.onLevelChange(level); 84 } 85 86 @Override 87 public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme) 88 throws XmlPullParserException, IOException { 89 super.inflate(r, parser, attrs, theme); 90 91 int type; 92 93 int low = 0; 94 95 final int innerDepth = parser.getDepth() + 1; 96 int depth; 97 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 98 && ((depth = parser.getDepth()) >= innerDepth 99 || type != XmlPullParser.END_TAG)) { 100 if (type != XmlPullParser.START_TAG) { 101 continue; 102 } 103 104 if (depth > innerDepth || !parser.getName().equals("item")) { 105 continue; 106 } 107 108 TypedArray a = obtainAttributes(r, theme, attrs, 109 com.android.internal.R.styleable.LevelListDrawableItem); 110 111 low = a.getInt( 112 com.android.internal.R.styleable.LevelListDrawableItem_minLevel, 0); 113 int high = a.getInt( 114 com.android.internal.R.styleable.LevelListDrawableItem_maxLevel, 0); 115 int drawableRes = a.getResourceId( 116 com.android.internal.R.styleable.LevelListDrawableItem_drawable, 0); 117 118 a.recycle(); 119 120 if (high < 0) { 121 throw new XmlPullParserException(parser.getPositionDescription() 122 + ": <item> tag requires a 'maxLevel' attribute"); 123 } 124 125 Drawable dr; 126 if (drawableRes != 0) { 127 dr = r.getDrawable(drawableRes, theme); 128 } else { 129 while ((type = parser.next()) == XmlPullParser.TEXT) { 130 } 131 if (type != XmlPullParser.START_TAG) { 132 throw new XmlPullParserException( 133 parser.getPositionDescription() 134 + ": <item> tag requires a 'drawable' attribute or " 135 + "child tag defining a drawable"); 136 } 137 dr = Drawable.createFromXmlInner(r, parser, attrs, theme); 138 } 139 140 mLevelListState.addLevel(low, high, dr); 141 } 142 143 onLevelChange(getLevel()); 144 } 145 146 @Override 147 public Drawable mutate() { 148 if (!mMutated && super.mutate() == this) { 149 mLevelListState.mLows = mLevelListState.mLows.clone(); 150 mLevelListState.mHighs = mLevelListState.mHighs.clone(); 151 mMutated = true; 152 } 153 return this; 154 } 155 156 /** 157 * @hide 158 */ 159 public void clearMutated() { 160 super.clearMutated(); 161 mMutated = false; 162 } 163 164 private final static class LevelListState extends DrawableContainerState { 165 private int[] mLows; 166 private int[] mHighs; 167 168 LevelListState(LevelListState orig, LevelListDrawable owner, Resources res) { 169 super(orig, owner, res); 170 171 if (orig != null) { 172 mLows = orig.mLows; 173 mHighs = orig.mHighs; 174 } else { 175 mLows = new int[getCapacity()]; 176 mHighs = new int[getCapacity()]; 177 } 178 } 179 180 public void addLevel(int low, int high, Drawable drawable) { 181 int pos = addChild(drawable); 182 mLows[pos] = low; 183 mHighs[pos] = high; 184 } 185 186 public int indexOfLevel(int level) { 187 final int[] lows = mLows; 188 final int[] highs = mHighs; 189 final int N = getChildCount(); 190 for (int i = 0; i < N; i++) { 191 if (level >= lows[i] && level <= highs[i]) { 192 return i; 193 } 194 } 195 return -1; 196 } 197 198 @Override 199 public Drawable newDrawable() { 200 return new LevelListDrawable(this, null); 201 } 202 203 @Override 204 public Drawable newDrawable(Resources res) { 205 return new LevelListDrawable(this, res); 206 } 207 208 @Override 209 public void growArray(int oldSize, int newSize) { 210 super.growArray(oldSize, newSize); 211 int[] newInts = new int[newSize]; 212 System.arraycopy(mLows, 0, newInts, 0, oldSize); 213 mLows = newInts; 214 newInts = new int[newSize]; 215 System.arraycopy(mHighs, 0, newInts, 0, oldSize); 216 mHighs = newInts; 217 } 218 } 219 220 private LevelListDrawable(LevelListState state, Resources res) { 221 LevelListState as = new LevelListState(state, this, res); 222 mLevelListState = as; 223 setConstantState(as); 224 onLevelChange(getLevel()); 225 } 226} 227 228