LevelListDrawable.java revision c2974809373697147cbe5754835cc871fb93aef1
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.util.AttributeSet; 27 28/** 29 * A resource that manages a number of alternate Drawables, each assigned a maximum numerical value. 30 * Setting the level value of the object with {@link #setLevel(int)} will load the image with the next 31 * greater or equal value assigned to its max attribute. 32 * A good example use of 33 * a LevelListDrawable would be a battery level indicator icon, with different images to indicate the current 34 * battery level. 35 * <p> 36 * It can be defined in an XML file with the <code><level-list></code> element. 37 * Each Drawable level is defined in a nested <code><item></code>. For example: 38 * </p> 39 * <pre> 40 * <level-list xmlns:android="http://schemas.android.com/apk/res/android"> 41 * <item android:maxLevel="0" android:drawable="@drawable/ic_wifi_signal_1" /> 42 * <item android:maxLevel="1" android:drawable="@drawable/ic_wifi_signal_2" /> 43 * <item android:maxLevel="2" android:drawable="@drawable/ic_wifi_signal_3" /> 44 * <item android:maxLevel="3" android:drawable="@drawable/ic_wifi_signal_4" /> 45 * </level-list> 46 *</pre> 47 * <p>With this XML saved into the res/drawable/ folder of the project, it can be referenced as 48 * the drawable for an {@link android.widget.ImageView}. The default image is the first in the list. 49 * It can then be changed to one of the other levels with 50 * {@link android.widget.ImageView#setImageLevel(int)}.</p> 51 * @attr ref android.R.styleable#LevelListDrawableItem_minLevel 52 * @attr ref android.R.styleable#LevelListDrawableItem_maxLevel 53 * @attr ref android.R.styleable#LevelListDrawableItem_drawable 54 */ 55public class LevelListDrawable extends DrawableContainer { 56 private final LevelListState mLevelListState; 57 private boolean mMutated; 58 59 public LevelListDrawable() { 60 this(null, null); 61 } 62 63 public void addLevel(int low, int high, Drawable drawable) { 64 if (drawable != null) { 65 mLevelListState.addLevel(low, high, drawable); 66 // in case the new state matches our current state... 67 onLevelChange(getLevel()); 68 } 69 } 70 71 // overrides from Drawable 72 73 @Override 74 protected boolean onLevelChange(int level) { 75 int idx = mLevelListState.indexOfLevel(level); 76 if (selectDrawable(idx)) { 77 return true; 78 } 79 return super.onLevelChange(level); 80 } 81 82 @Override 83 public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs) 84 throws XmlPullParserException, IOException { 85 86 super.inflate(r, parser, attrs); 87 88 int type; 89 90 int low = 0; 91 92 final int innerDepth = parser.getDepth() + 1; 93 int depth; 94 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 95 && ((depth = parser.getDepth()) >= innerDepth 96 || type != XmlPullParser.END_TAG)) { 97 if (type != XmlPullParser.START_TAG) { 98 continue; 99 } 100 101 if (depth > innerDepth || !parser.getName().equals("item")) { 102 continue; 103 } 104 105 TypedArray a = r.obtainAttributes(attrs, 106 com.android.internal.R.styleable.LevelListDrawableItem); 107 108 low = a.getInt( 109 com.android.internal.R.styleable.LevelListDrawableItem_minLevel, 0); 110 int high = a.getInt( 111 com.android.internal.R.styleable.LevelListDrawableItem_maxLevel, 0); 112 int drawableRes = a.getResourceId( 113 com.android.internal.R.styleable.LevelListDrawableItem_drawable, 0); 114 115 a.recycle(); 116 117 if (high < 0) { 118 throw new XmlPullParserException(parser.getPositionDescription() 119 + ": <item> tag requires a 'maxLevel' attribute"); 120 } 121 122 Drawable dr; 123 if (drawableRes != 0) { 124 dr = r.getDrawable(drawableRes); 125 } else { 126 while ((type = parser.next()) == XmlPullParser.TEXT) { 127 } 128 if (type != XmlPullParser.START_TAG) { 129 throw new XmlPullParserException( 130 parser.getPositionDescription() 131 + ": <item> tag requires a 'drawable' attribute or " 132 + "child tag defining a drawable"); 133 } 134 dr = Drawable.createFromXmlInner(r, parser, attrs); 135 } 136 137 mLevelListState.addLevel(low, high, dr); 138 } 139 140 onLevelChange(getLevel()); 141 } 142 143 @Override 144 public Drawable mutate() { 145 if (!mMutated && super.mutate() == this) { 146 mLevelListState.mLows = mLevelListState.mLows.clone(); 147 mLevelListState.mHighs = mLevelListState.mHighs.clone(); 148 mMutated = true; 149 } 150 return this; 151 } 152 153 private final static class LevelListState extends DrawableContainerState { 154 private int[] mLows; 155 private int[] mHighs; 156 157 LevelListState(LevelListState orig, LevelListDrawable owner, Resources res) { 158 super(orig, owner, res); 159 160 if (orig != null) { 161 mLows = orig.mLows; 162 mHighs = orig.mHighs; 163 } else { 164 mLows = new int[getChildren().length]; 165 mHighs = new int[getChildren().length]; 166 } 167 } 168 169 public void addLevel(int low, int high, Drawable drawable) { 170 int pos = addChild(drawable); 171 mLows[pos] = low; 172 mHighs[pos] = high; 173 } 174 175 public int indexOfLevel(int level) { 176 final int[] lows = mLows; 177 final int[] highs = mHighs; 178 final int N = getChildCount(); 179 for (int i = 0; i < N; i++) { 180 if (level >= lows[i] && level <= highs[i]) { 181 return i; 182 } 183 } 184 return -1; 185 } 186 187 @Override 188 public Drawable newDrawable() { 189 return new LevelListDrawable(this, null); 190 } 191 192 @Override 193 public Drawable newDrawable(Resources res) { 194 return new LevelListDrawable(this, res); 195 } 196 197 @Override 198 public void growArray(int oldSize, int newSize) { 199 super.growArray(oldSize, newSize); 200 int[] newInts = new int[newSize]; 201 System.arraycopy(mLows, 0, newInts, 0, oldSize); 202 mLows = newInts; 203 newInts = new int[newSize]; 204 System.arraycopy(mHighs, 0, newInts, 0, oldSize); 205 mHighs = newInts; 206 } 207 } 208 209 private LevelListDrawable(LevelListState state, Resources res) { 210 LevelListState as = new LevelListState(state, this, res); 211 mLevelListState = as; 212 setConstantState(as); 213 onLevelChange(getLevel()); 214 } 215} 216 217