ClipDrawable.java revision 08c7116ab9cd04ad6dd3c04aa1017237e7f409ac
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 com.android.internal.R; 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.graphics.*; 28import android.view.Gravity; 29import android.util.AttributeSet; 30 31import java.io.IOException; 32 33/** 34 * A Drawable that clips another Drawable based on this Drawable's current 35 * level value. You can control how much the child Drawable gets clipped in width 36 * and height based on the level, as well as a gravity to control where it is 37 * placed in its overall container. Most often used to implement things like 38 * progress bars, by increasing the drawable's level with {@link 39 * android.graphics.drawable.Drawable#setLevel(int) setLevel()}. 40 * <p class="note"><strong>Note:</strong> The drawable is clipped completely and not visible when 41 * the level is 0 and fully revealed when the level is 10,000.</p> 42 * 43 * <p>It can be defined in an XML file with the <code><clip></code> element. For more 44 * information, see the guide to <a 45 * href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.</p> 46 * 47 * @attr ref android.R.styleable#ClipDrawable_clipOrientation 48 * @attr ref android.R.styleable#ClipDrawable_gravity 49 * @attr ref android.R.styleable#ClipDrawable_drawable 50 */ 51public class ClipDrawable extends DrawableWrapper { 52 public static final int HORIZONTAL = 1; 53 public static final int VERTICAL = 2; 54 55 private static final int MAX_LEVEL = 10000; 56 57 private final Rect mTmpRect = new Rect(); 58 59 private ClipState mState; 60 61 ClipDrawable() { 62 this(new ClipState(null), null); 63 } 64 65 /** 66 * Creates a new clip drawable with the specified gravity and orientation. 67 * 68 * @param drawable the drawable to clip 69 * @param gravity gravity constant (see {@link Gravity} used to position 70 * the clipped drawable within the parent container 71 * @param orientation bitwise-or of {@link #HORIZONTAL} and/or 72 * {@link #VERTICAL} 73 */ 74 public ClipDrawable(Drawable drawable, int gravity, int orientation) { 75 this(new ClipState(null), null); 76 77 mState.mGravity = gravity; 78 mState.mOrientation = orientation; 79 80 setDrawable(drawable); 81 } 82 83 @Override 84 public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme) 85 throws XmlPullParserException, IOException { 86 super.inflate(r, parser, attrs, theme); 87 88 final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.ClipDrawable); 89 updateStateFromTypedArray(a); 90 inflateChildDrawable(r, parser, attrs, theme); 91 verifyRequiredAttributes(a); 92 a.recycle(); 93 } 94 95 private void verifyRequiredAttributes(TypedArray a) throws XmlPullParserException { 96 // If we're not waiting on a theme, verify required attributes. 97 if (getDrawable() == null && (mState.mThemeAttrs == null 98 || mState.mThemeAttrs[R.styleable.ClipDrawable_drawable] == 0)) { 99 throw new XmlPullParserException(a.getPositionDescription() 100 + ": <clip> tag requires a 'drawable' attribute or " 101 + "child tag defining a drawable"); 102 } 103 } 104 105 @Override 106 void updateStateFromTypedArray(TypedArray a) { 107 super.updateStateFromTypedArray(a); 108 109 final ClipState state = mState; 110 state.mOrientation = a.getInt( 111 R.styleable.ClipDrawable_clipOrientation, state.mOrientation); 112 state.mGravity = a.getInt( 113 R.styleable.ClipDrawable_gravity, state.mGravity); 114 115 final Drawable dr = a.getDrawable(R.styleable.ClipDrawable_drawable); 116 if (dr != null) { 117 setDrawable(dr); 118 } 119 } 120 121 @Override 122 public void applyTheme(Theme t) { 123 final ClipState state = mState; 124 if (state == null) { 125 return; 126 } 127 128 if (state.mThemeAttrs != null) { 129 final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.ClipDrawable); 130 try { 131 updateStateFromTypedArray(a); 132 verifyRequiredAttributes(a); 133 } catch (XmlPullParserException e) { 134 throw new RuntimeException(e); 135 } finally { 136 a.recycle(); 137 } 138 } 139 140 // The drawable may have changed as a result of applying the theme, so 141 // apply the theme to the wrapped drawable last. 142 super.applyTheme(t); 143 } 144 145 @Override 146 protected boolean onLevelChange(int level) { 147 super.onLevelChange(level); 148 invalidateSelf(); 149 return true; 150 } 151 152 @Override 153 public void draw(Canvas canvas) { 154 final Drawable dr = getDrawable(); 155 if (dr.getLevel() == 0) { 156 return; 157 } 158 159 final Rect r = mTmpRect; 160 final Rect bounds = getBounds(); 161 final int level = getLevel(); 162 163 int w = bounds.width(); 164 final int iw = 0; //mState.mDrawable.getIntrinsicWidth(); 165 if ((mState.mOrientation & HORIZONTAL) != 0) { 166 w -= (w - iw) * (MAX_LEVEL - level) / MAX_LEVEL; 167 } 168 169 int h = bounds.height(); 170 final int ih = 0; //mState.mDrawable.getIntrinsicHeight(); 171 if ((mState.mOrientation & VERTICAL) != 0) { 172 h -= (h - ih) * (MAX_LEVEL - level) / MAX_LEVEL; 173 } 174 175 final int layoutDirection = getLayoutDirection(); 176 Gravity.apply(mState.mGravity, w, h, bounds, r, layoutDirection); 177 178 if (w > 0 && h > 0) { 179 canvas.save(); 180 canvas.clipRect(r); 181 dr.draw(canvas); 182 canvas.restore(); 183 } 184 } 185 186 static final class ClipState extends DrawableWrapper.DrawableWrapperState { 187 int mOrientation = HORIZONTAL; 188 int mGravity = Gravity.LEFT; 189 190 ClipState(ClipState orig) { 191 super(orig); 192 193 if (orig != null) { 194 mOrientation = orig.mOrientation; 195 mGravity = orig.mGravity; 196 } 197 } 198 199 @Override 200 public Drawable newDrawable(Resources res) { 201 return new ClipDrawable(this, res); 202 } 203 } 204 205 private ClipDrawable(ClipState state, Resources res) { 206 super(state, res); 207 208 mState = state; 209 } 210} 211 212