Home | History | Annotate | Download | only in drawable
      1 /*
      2  * Copyright (C) 2008 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.util.AttributeSet;
     26 import android.util.Log;
     27 
     28 import java.io.IOException;
     29 
     30 /**
     31  * A Drawable that insets another Drawable by a specified distance.
     32  * This is used when a View needs a background that is smaller than
     33  * the View's actual bounds.
     34  *
     35  * <p>It can be defined in an XML file with the <code>&lt;inset></code> element. For more
     36  * information, see the guide to <a
     37  * href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.</p>
     38  *
     39  * @attr ref android.R.styleable#InsetDrawable_visible
     40  * @attr ref android.R.styleable#InsetDrawable_drawable
     41  * @attr ref android.R.styleable#InsetDrawable_insetLeft
     42  * @attr ref android.R.styleable#InsetDrawable_insetRight
     43  * @attr ref android.R.styleable#InsetDrawable_insetTop
     44  * @attr ref android.R.styleable#InsetDrawable_insetBottom
     45  */
     46 public class InsetDrawable extends Drawable implements Drawable.Callback
     47 {
     48     // Most of this is copied from ScaleDrawable.
     49     private InsetState mInsetState;
     50     private final Rect mTmpRect = new Rect();
     51     private boolean mMutated;
     52 
     53     /*package*/ InsetDrawable() {
     54         this(null, null);
     55     }
     56 
     57     public InsetDrawable(Drawable drawable, int inset) {
     58         this(drawable, inset, inset, inset, inset);
     59     }
     60 
     61     public InsetDrawable(Drawable drawable, int insetLeft, int insetTop,
     62                          int insetRight, int insetBottom) {
     63         this(null, null);
     64 
     65         mInsetState.mDrawable = drawable;
     66         mInsetState.mInsetLeft = insetLeft;
     67         mInsetState.mInsetTop = insetTop;
     68         mInsetState.mInsetRight = insetRight;
     69         mInsetState.mInsetBottom = insetBottom;
     70 
     71         if (drawable != null) {
     72             drawable.setCallback(this);
     73         }
     74     }
     75 
     76     @Override public void inflate(Resources r, XmlPullParser parser,
     77                                   AttributeSet attrs)
     78     throws XmlPullParserException, IOException {
     79         int type;
     80 
     81         TypedArray a = r.obtainAttributes(attrs,
     82                 com.android.internal.R.styleable.InsetDrawable);
     83 
     84         super.inflateWithAttributes(r, parser, a,
     85                 com.android.internal.R.styleable.InsetDrawable_visible);
     86 
     87         int drawableRes = a.getResourceId(com.android.internal.R.styleable.
     88                                     InsetDrawable_drawable, 0);
     89 
     90         int inLeft = a.getDimensionPixelOffset(com.android.internal.R.styleable.
     91                                     InsetDrawable_insetLeft, 0);
     92         int inTop = a.getDimensionPixelOffset(com.android.internal.R.styleable.
     93                                     InsetDrawable_insetTop, 0);
     94         int inRight = a.getDimensionPixelOffset(com.android.internal.R.styleable.
     95                                     InsetDrawable_insetRight, 0);
     96         int inBottom = a.getDimensionPixelOffset(com.android.internal.R.styleable.
     97                                     InsetDrawable_insetBottom, 0);
     98 
     99         a.recycle();
    100 
    101         Drawable dr;
    102         if (drawableRes != 0) {
    103             dr = r.getDrawable(drawableRes);
    104         } else {
    105             while ((type=parser.next()) == XmlPullParser.TEXT) {
    106             }
    107             if (type != XmlPullParser.START_TAG) {
    108                 throw new XmlPullParserException(
    109                         parser.getPositionDescription()
    110                         + ": <inset> tag requires a 'drawable' attribute or "
    111                         + "child tag defining a drawable");
    112             }
    113             dr = Drawable.createFromXmlInner(r, parser, attrs);
    114         }
    115 
    116         if (dr == null) {
    117             Log.w("drawable", "No drawable specified for <inset>");
    118         }
    119 
    120         mInsetState.mDrawable = dr;
    121         mInsetState.mInsetLeft = inLeft;
    122         mInsetState.mInsetRight = inRight;
    123         mInsetState.mInsetTop = inTop;
    124         mInsetState.mInsetBottom = inBottom;
    125 
    126         if (dr != null) {
    127             dr.setCallback(this);
    128         }
    129     }
    130 
    131     // overrides from Drawable.Callback
    132 
    133     public void invalidateDrawable(Drawable who) {
    134         final Callback callback = getCallback();
    135         if (callback != null) {
    136             callback.invalidateDrawable(this);
    137         }
    138     }
    139 
    140     public void scheduleDrawable(Drawable who, Runnable what, long when) {
    141         final Callback callback = getCallback();
    142         if (callback != null) {
    143             callback.scheduleDrawable(this, what, when);
    144         }
    145     }
    146 
    147     public void unscheduleDrawable(Drawable who, Runnable what) {
    148         final Callback callback = getCallback();
    149         if (callback != null) {
    150             callback.unscheduleDrawable(this, what);
    151         }
    152     }
    153 
    154     // overrides from Drawable
    155 
    156     @Override
    157     public void draw(Canvas canvas) {
    158         mInsetState.mDrawable.draw(canvas);
    159     }
    160 
    161     @Override
    162     public int getChangingConfigurations() {
    163         return super.getChangingConfigurations()
    164                 | mInsetState.mChangingConfigurations
    165                 | mInsetState.mDrawable.getChangingConfigurations();
    166     }
    167 
    168     @Override
    169     public boolean getPadding(Rect padding) {
    170         boolean pad = mInsetState.mDrawable.getPadding(padding);
    171 
    172         padding.left += mInsetState.mInsetLeft;
    173         padding.right += mInsetState.mInsetRight;
    174         padding.top += mInsetState.mInsetTop;
    175         padding.bottom += mInsetState.mInsetBottom;
    176 
    177         if (pad || (mInsetState.mInsetLeft | mInsetState.mInsetRight |
    178                     mInsetState.mInsetTop | mInsetState.mInsetBottom) != 0) {
    179             return true;
    180         } else {
    181             return false;
    182         }
    183     }
    184 
    185     @Override
    186     public boolean setVisible(boolean visible, boolean restart) {
    187         mInsetState.mDrawable.setVisible(visible, restart);
    188         return super.setVisible(visible, restart);
    189     }
    190 
    191     @Override
    192     public void setAlpha(int alpha) {
    193         mInsetState.mDrawable.setAlpha(alpha);
    194     }
    195 
    196     @Override
    197     public void setColorFilter(ColorFilter cf) {
    198         mInsetState.mDrawable.setColorFilter(cf);
    199     }
    200 
    201     @Override
    202     public int getOpacity() {
    203         return mInsetState.mDrawable.getOpacity();
    204     }
    205 
    206     @Override
    207     public boolean isStateful() {
    208         return mInsetState.mDrawable.isStateful();
    209     }
    210 
    211     @Override
    212     protected boolean onStateChange(int[] state) {
    213         boolean changed = mInsetState.mDrawable.setState(state);
    214         onBoundsChange(getBounds());
    215         return changed;
    216     }
    217 
    218     @Override
    219     protected void onBoundsChange(Rect bounds) {
    220         final Rect r = mTmpRect;
    221         r.set(bounds);
    222 
    223         r.left += mInsetState.mInsetLeft;
    224         r.top += mInsetState.mInsetTop;
    225         r.right -= mInsetState.mInsetRight;
    226         r.bottom -= mInsetState.mInsetBottom;
    227 
    228         mInsetState.mDrawable.setBounds(r.left, r.top, r.right, r.bottom);
    229     }
    230 
    231     @Override
    232     public int getIntrinsicWidth() {
    233         return mInsetState.mDrawable.getIntrinsicWidth();
    234     }
    235 
    236     @Override
    237     public int getIntrinsicHeight() {
    238         return mInsetState.mDrawable.getIntrinsicHeight();
    239     }
    240 
    241     @Override
    242     public ConstantState getConstantState() {
    243         if (mInsetState.canConstantState()) {
    244             mInsetState.mChangingConfigurations = getChangingConfigurations();
    245             return mInsetState;
    246         }
    247         return null;
    248     }
    249 
    250     @Override
    251     public Drawable mutate() {
    252         if (!mMutated && super.mutate() == this) {
    253             mInsetState.mDrawable.mutate();
    254             mMutated = true;
    255         }
    256         return this;
    257     }
    258 
    259     final static class InsetState extends ConstantState {
    260         Drawable mDrawable;
    261         int mChangingConfigurations;
    262 
    263         int mInsetLeft;
    264         int mInsetTop;
    265         int mInsetRight;
    266         int mInsetBottom;
    267 
    268         boolean mCheckedConstantState;
    269         boolean mCanConstantState;
    270 
    271         InsetState(InsetState orig, InsetDrawable owner, Resources res) {
    272             if (orig != null) {
    273                 if (res != null) {
    274                     mDrawable = orig.mDrawable.getConstantState().newDrawable(res);
    275                 } else {
    276                     mDrawable = orig.mDrawable.getConstantState().newDrawable();
    277                 }
    278                 mDrawable.setCallback(owner);
    279                 mInsetLeft = orig.mInsetLeft;
    280                 mInsetTop = orig.mInsetTop;
    281                 mInsetRight = orig.mInsetRight;
    282                 mInsetBottom = orig.mInsetBottom;
    283                 mCheckedConstantState = mCanConstantState = true;
    284             }
    285         }
    286 
    287         @Override
    288         public Drawable newDrawable() {
    289             return new InsetDrawable(this, null);
    290         }
    291 
    292         @Override
    293         public Drawable newDrawable(Resources res) {
    294             return new InsetDrawable(this, res);
    295         }
    296 
    297         @Override
    298         public int getChangingConfigurations() {
    299             return mChangingConfigurations;
    300         }
    301 
    302         boolean canConstantState() {
    303             if (!mCheckedConstantState) {
    304                 mCanConstantState = mDrawable.getConstantState() != null;
    305                 mCheckedConstantState = true;
    306             }
    307 
    308             return mCanConstantState;
    309         }
    310     }
    311 
    312     private InsetDrawable(InsetState state, Resources res) {
    313         mInsetState = new InsetState(state, this, res);
    314     }
    315 }
    316 
    317