ClipDrawable.java revision cda212d79d449468384cc7744878b8c99984059c
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 org.xmlpull.v1.XmlPullParser; 20import org.xmlpull.v1.XmlPullParserException; 21 22import android.content.res.Resources; 23import android.content.res.TypedArray; 24import android.content.res.Resources.Theme; 25import android.graphics.*; 26import android.view.Gravity; 27import android.util.AttributeSet; 28 29import java.io.IOException; 30 31/** 32 * A Drawable that clips another Drawable based on this Drawable's current 33 * level value. You can control how much the child Drawable gets clipped in width 34 * and height based on the level, as well as a gravity to control where it is 35 * placed in its overall container. Most often used to implement things like 36 * progress bars, by increasing the drawable's level with {@link 37 * android.graphics.drawable.Drawable#setLevel(int) setLevel()}. 38 * <p class="note"><strong>Note:</strong> The drawable is clipped completely and not visible when 39 * the level is 0 and fully revealed when the level is 10,000.</p> 40 * 41 * <p>It can be defined in an XML file with the <code><clip></code> element. For more 42 * information, see the guide to <a 43 * href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.</p> 44 * 45 * @attr ref android.R.styleable#ClipDrawable_clipOrientation 46 * @attr ref android.R.styleable#ClipDrawable_gravity 47 * @attr ref android.R.styleable#ClipDrawable_drawable 48 */ 49public class ClipDrawable extends Drawable implements Drawable.Callback { 50 private ClipState mClipState; 51 private final Rect mTmpRect = new Rect(); 52 53 public static final int HORIZONTAL = 1; 54 public static final int VERTICAL = 2; 55 56 ClipDrawable() { 57 this(null, null); 58 } 59 60 /** 61 * @param orientation Bitwise-or of {@link #HORIZONTAL} and/or {@link #VERTICAL} 62 */ 63 public ClipDrawable(Drawable drawable, int gravity, int orientation) { 64 this(null, null); 65 66 mClipState.mDrawable = drawable; 67 mClipState.mGravity = gravity; 68 mClipState.mOrientation = orientation; 69 70 if (drawable != null) { 71 drawable.setCallback(this); 72 } 73 } 74 75 @Override 76 public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme) 77 throws XmlPullParserException, IOException { 78 super.inflate(r, parser, attrs, theme); 79 80 int type; 81 82 TypedArray a = r.obtainAttributes(attrs, com.android.internal.R.styleable.ClipDrawable); 83 84 int orientation = a.getInt( 85 com.android.internal.R.styleable.ClipDrawable_clipOrientation, 86 HORIZONTAL); 87 int g = a.getInt(com.android.internal.R.styleable.ClipDrawable_gravity, Gravity.LEFT); 88 Drawable dr = a.getDrawable(com.android.internal.R.styleable.ClipDrawable_drawable); 89 90 a.recycle(); 91 92 final int outerDepth = parser.getDepth(); 93 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 94 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 95 if (type != XmlPullParser.START_TAG) { 96 continue; 97 } 98 dr = Drawable.createFromXmlInner(r, parser, attrs, theme); 99 } 100 101 if (dr == null) { 102 throw new IllegalArgumentException("No drawable specified for <clip>"); 103 } 104 105 mClipState.mDrawable = dr; 106 mClipState.mOrientation = orientation; 107 mClipState.mGravity = g; 108 109 dr.setCallback(this); 110 } 111 112 // overrides from Drawable.Callback 113 114 public void invalidateDrawable(Drawable who) { 115 final Callback callback = getCallback(); 116 if (callback != null) { 117 callback.invalidateDrawable(this); 118 } 119 } 120 121 public void scheduleDrawable(Drawable who, Runnable what, long when) { 122 final Callback callback = getCallback(); 123 if (callback != null) { 124 callback.scheduleDrawable(this, what, when); 125 } 126 } 127 128 public void unscheduleDrawable(Drawable who, Runnable what) { 129 final Callback callback = getCallback(); 130 if (callback != null) { 131 callback.unscheduleDrawable(this, what); 132 } 133 } 134 135 // overrides from Drawable 136 137 @Override 138 public int getChangingConfigurations() { 139 return super.getChangingConfigurations() 140 | mClipState.mChangingConfigurations 141 | mClipState.mDrawable.getChangingConfigurations(); 142 } 143 144 @Override 145 public boolean getPadding(Rect padding) { 146 // XXX need to adjust padding! 147 return mClipState.mDrawable.getPadding(padding); 148 } 149 150 @Override 151 public boolean setVisible(boolean visible, boolean restart) { 152 mClipState.mDrawable.setVisible(visible, restart); 153 return super.setVisible(visible, restart); 154 } 155 156 @Override 157 public void setAlpha(int alpha) { 158 mClipState.mDrawable.setAlpha(alpha); 159 } 160 161 @Override 162 public int getAlpha() { 163 return mClipState.mDrawable.getAlpha(); 164 } 165 166 @Override 167 public void setColorFilter(ColorFilter cf) { 168 mClipState.mDrawable.setColorFilter(cf); 169 } 170 171 @Override 172 public int getOpacity() { 173 return mClipState.mDrawable.getOpacity(); 174 } 175 176 @Override 177 public boolean isStateful() { 178 return mClipState.mDrawable.isStateful(); 179 } 180 181 @Override 182 protected boolean onStateChange(int[] state) { 183 return mClipState.mDrawable.setState(state); 184 } 185 186 @Override 187 protected boolean onLevelChange(int level) { 188 mClipState.mDrawable.setLevel(level); 189 invalidateSelf(); 190 return true; 191 } 192 193 @Override 194 protected void onBoundsChange(Rect bounds) { 195 mClipState.mDrawable.setBounds(bounds); 196 } 197 198 @Override 199 public void draw(Canvas canvas) { 200 201 if (mClipState.mDrawable.getLevel() == 0) { 202 return; 203 } 204 205 final Rect r = mTmpRect; 206 final Rect bounds = getBounds(); 207 int level = getLevel(); 208 int w = bounds.width(); 209 final int iw = 0; //mClipState.mDrawable.getIntrinsicWidth(); 210 if ((mClipState.mOrientation & HORIZONTAL) != 0) { 211 w -= (w - iw) * (10000 - level) / 10000; 212 } 213 int h = bounds.height(); 214 final int ih = 0; //mClipState.mDrawable.getIntrinsicHeight(); 215 if ((mClipState.mOrientation & VERTICAL) != 0) { 216 h -= (h - ih) * (10000 - level) / 10000; 217 } 218 final int layoutDirection = getLayoutDirection(); 219 Gravity.apply(mClipState.mGravity, w, h, bounds, r, layoutDirection); 220 221 if (w > 0 && h > 0) { 222 canvas.save(); 223 canvas.clipRect(r); 224 mClipState.mDrawable.draw(canvas); 225 canvas.restore(); 226 } 227 } 228 229 @Override 230 public int getIntrinsicWidth() { 231 return mClipState.mDrawable.getIntrinsicWidth(); 232 } 233 234 @Override 235 public int getIntrinsicHeight() { 236 return mClipState.mDrawable.getIntrinsicHeight(); 237 } 238 239 @Override 240 public ConstantState getConstantState() { 241 if (mClipState.canConstantState()) { 242 mClipState.mChangingConfigurations = getChangingConfigurations(); 243 return mClipState; 244 } 245 return null; 246 } 247 248 /** @hide */ 249 @Override 250 public void setLayoutDirection(int layoutDirection) { 251 mClipState.mDrawable.setLayoutDirection(layoutDirection); 252 super.setLayoutDirection(layoutDirection); 253 } 254 255 final static class ClipState extends ConstantState { 256 Drawable mDrawable; 257 int mChangingConfigurations; 258 int mOrientation; 259 int mGravity; 260 261 private boolean mCheckedConstantState; 262 private boolean mCanConstantState; 263 264 ClipState(ClipState orig, ClipDrawable owner, Resources res) { 265 if (orig != null) { 266 if (res != null) { 267 mDrawable = orig.mDrawable.getConstantState().newDrawable(res); 268 } else { 269 mDrawable = orig.mDrawable.getConstantState().newDrawable(); 270 } 271 mDrawable.setCallback(owner); 272 mDrawable.setLayoutDirection(orig.mDrawable.getLayoutDirection()); 273 mOrientation = orig.mOrientation; 274 mGravity = orig.mGravity; 275 mCheckedConstantState = mCanConstantState = true; 276 } 277 } 278 279 @Override 280 public Drawable newDrawable() { 281 return new ClipDrawable(this, null); 282 } 283 284 @Override 285 public Drawable newDrawable(Resources res) { 286 return new ClipDrawable(this, res); 287 } 288 289 @Override 290 public int getChangingConfigurations() { 291 return mChangingConfigurations; 292 } 293 294 boolean canConstantState() { 295 if (!mCheckedConstantState) { 296 mCanConstantState = mDrawable.getConstantState() != null; 297 mCheckedConstantState = true; 298 } 299 300 return mCanConstantState; 301 } 302 } 303 304 private ClipDrawable(ClipState state, Resources res) { 305 mClipState = new ClipState(state, this, res); 306 } 307} 308 309