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