ShapeDrawable.java revision b3c56086d802ae28888dd97ba1f49bd6cee0b673
1e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet/* 2e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet * Copyright (C) 2007 The Android Open Source Project 3e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet * 4e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet * Licensed under the Apache License, Version 2.0 (the "License"); 5e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet * you may not use this file except in compliance with the License. 6e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet * You may obtain a copy of the License at 7e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet * 8e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet * http://www.apache.org/licenses/LICENSE-2.0 9e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet * 10e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet * Unless required by applicable law or agreed to in writing, software 11e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet * distributed under the License is distributed on an "AS IS" BASIS, 12e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet * See the License for the specific language governing permissions and 14e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet * limitations under the License. 15e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet */ 16e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet 17e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichetpackage android.graphics.drawable; 18e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet 19aadb8928f5920c758c1a385bbc8b8b128a60657cCraig Stoutimport android.content.res.ColorStateList; 20aadb8928f5920c758c1a385bbc8b8b128a60657cCraig Stoutimport android.content.res.Resources; 21e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichetimport android.content.res.TypedArray; 225a59bde085588f95dc067bd1ed64a940f355343cbulicimport android.graphics.Canvas; 23f0e71182c62bba18e9d8098941a29c4f5031ce36Jerome Poichetimport android.graphics.ColorFilter; 24aadb8928f5920c758c1a385bbc8b8b128a60657cCraig Stoutimport android.graphics.Outline; 25e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichetimport android.graphics.Paint; 26f0e71182c62bba18e9d8098941a29c4f5031ce36Jerome Poichetimport android.graphics.PixelFormat; 274cf79b1c4d38a190317961891f9fd052836710fdCraig Stoutimport android.graphics.PorterDuff.Mode; 2870acb0c19be3831a2080e4f902324de16bfbf62eTor Norbyeimport android.graphics.PorterDuffColorFilter; 29e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichetimport android.graphics.Rect; 30e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichetimport android.graphics.Shader; 31e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichetimport android.graphics.drawable.shapes.Shape; 32e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichetimport android.content.res.Resources.Theme; 334cf79b1c4d38a190317961891f9fd052836710fdCraig Stoutimport android.util.AttributeSet; 34f0e71182c62bba18e9d8098941a29c4f5031ce36Jerome Poichet 35e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichetimport org.xmlpull.v1.XmlPullParser; 36937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichetimport org.xmlpull.v1.XmlPullParserException; 37937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet 38937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichetimport java.io.IOException; 39a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout 40937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet/** 414cf79b1c4d38a190317961891f9fd052836710fdCraig Stout * A Drawable object that draws primitive shapes. A ShapeDrawable takes a 42e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet * {@link android.graphics.drawable.shapes.Shape} object and manages its 43558f7b70035c50045908efca5f4f3d65df685cacCraig Stout * presence on the screen. If no Shape is given, then the ShapeDrawable will 444cf79b1c4d38a190317961891f9fd052836710fdCraig Stout * default to a {@link android.graphics.drawable.shapes.RectShape}. 45937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet * <p> 46937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet * This object can be defined in an XML file with the <code><shape></code> 474fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout * element. 484cf79b1c4d38a190317961891f9fd052836710fdCraig Stout * </p> 49aadb8928f5920c758c1a385bbc8b8b128a60657cCraig Stout * <div class="special reference"> <h3>Developer Guides</h3> 5044f004b2612b04510a09a4c1e33f5a109582d669Craig Stout * <p> 51c62efa44831b1c60dcbdfd968735e27ac8294439Craig Stout * For more information about how to use ShapeDrawable, read the <a 52c62efa44831b1c60dcbdfd968735e27ac8294439Craig Stout * href="{@docRoot}guide/topics/graphics/2d-graphics.html#shape-drawable"> 53aadb8928f5920c758c1a385bbc8b8b128a60657cCraig Stout * Canvas and Drawables</a> document. For more information about defining a 5477b750bc2094fbe921058d8748fe26f830fbc6c8Dmitri Plotnikov * ShapeDrawable in XML, read the <a href="{@docRoot} 5577b750bc2094fbe921058d8748fe26f830fbc6c8Dmitri Plotnikov * guide/topics/resources/drawable-resource.html#Shape">Drawable Resources</a> 56aadb8928f5920c758c1a385bbc8b8b128a60657cCraig Stout * document. 574fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout * </p> 584fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout * </div> 594fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout * 604fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout * @attr ref android.R.styleable#ShapeDrawablePadding_left 614fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout * @attr ref android.R.styleable#ShapeDrawablePadding_top 624fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout * @attr ref android.R.styleable#ShapeDrawablePadding_right 634fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout * @attr ref android.R.styleable#ShapeDrawablePadding_bottom 644fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout * @attr ref android.R.styleable#ShapeDrawable_color 654fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout * @attr ref android.R.styleable#ShapeDrawable_width 66c4788eac909f51e73e05885314d3ace65d0a2f26bulic * @attr ref android.R.styleable#ShapeDrawable_height 674fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout */ 684fdd3589c982860b831c0fad63c0082cb9079f47Craig Stoutpublic class ShapeDrawable extends Drawable { 6970acb0c19be3831a2080e4f902324de16bfbf62eTor Norbye private ShapeState mShapeState; 704fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout private PorterDuffColorFilter mTintFilter; 714fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout private boolean mMutated; 724fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout 734fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout /** 744fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout * ShapeDrawable constructor. 754fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout */ 76c4788eac909f51e73e05885314d3ace65d0a2f26bulic public ShapeDrawable() { 774fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout this((ShapeState) null); 784fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout } 794fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout 8070acb0c19be3831a2080e4f902324de16bfbf62eTor Norbye /** 814fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout * Creates a ShapeDrawable with a specified Shape. 824fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout * 834fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout * @param s the Shape that this ShapeDrawable should be 844fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout */ 854fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout public ShapeDrawable(Shape s) { 864fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout this((ShapeState) null); 874fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout 884fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout mShapeState.mShape = s; 894fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout } 904fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout 9170acb0c19be3831a2080e4f902324de16bfbf62eTor Norbye private ShapeDrawable(ShapeState state) { 924fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout mShapeState = new ShapeState(state); 934fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout 944fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout updateTintFilter(mTintFilter, state.mTint, state.mTintMode); 954fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout } 964fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout 974fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout /** 984fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout * Returns the Shape of this ShapeDrawable. 994fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout */ 10070acb0c19be3831a2080e4f902324de16bfbf62eTor Norbye public Shape getShape() { 1014fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout return mShapeState.mShape; 1024fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout } 1034fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout 1044fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout /** 1054fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout * Sets the Shape of this ShapeDrawable. 10670acb0c19be3831a2080e4f902324de16bfbf62eTor Norbye */ 1074fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout public void setShape(Shape s) { 1084fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout mShapeState.mShape = s; 1094fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout updateShape(); 1104fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout } 1114fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout 11270acb0c19be3831a2080e4f902324de16bfbf62eTor Norbye /** 1134fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout * Sets a ShaderFactory to which requests for a 1144fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout * {@link android.graphics.Shader} object will be made. 1154fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout * 1164fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout * @param fact an instance of your ShaderFactory implementation 1174fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout */ 1184fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout public void setShaderFactory(ShaderFactory fact) { 1194fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout mShapeState.mShaderFactory = fact; 1204fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout } 1214fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout 1224fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout /** 1234fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout * Returns the ShaderFactory used by this ShapeDrawable for requesting a 1244fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout * {@link android.graphics.Shader}. 1254fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout */ 1264fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout public ShaderFactory getShaderFactory() { 1274fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout return mShapeState.mShaderFactory; 128aadb8928f5920c758c1a385bbc8b8b128a60657cCraig Stout } 129aadb8928f5920c758c1a385bbc8b8b128a60657cCraig Stout 130aadb8928f5920c758c1a385bbc8b8b128a60657cCraig Stout /** 131aadb8928f5920c758c1a385bbc8b8b128a60657cCraig Stout * Returns the Paint used to draw the shape. 132aadb8928f5920c758c1a385bbc8b8b128a60657cCraig Stout */ 133aadb8928f5920c758c1a385bbc8b8b128a60657cCraig Stout public Paint getPaint() { 134aadb8928f5920c758c1a385bbc8b8b128a60657cCraig Stout return mShapeState.mPaint; 135aadb8928f5920c758c1a385bbc8b8b128a60657cCraig Stout } 136aadb8928f5920c758c1a385bbc8b8b128a60657cCraig Stout 137aadb8928f5920c758c1a385bbc8b8b128a60657cCraig Stout /** 138e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet * Sets padding for the shape. 13969e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout * 14069e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout * @param left padding for the left side (in pixels) 14169e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout * @param top padding for the top (in pixels) 14269e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout * @param right padding for the right side (in pixels) 14369e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout * @param bottom padding for the bottom (in pixels) 14469e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout */ 145c62efa44831b1c60dcbdfd968735e27ac8294439Craig Stout public void setPadding(int left, int top, int right, int bottom) { 14669e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout if ((left | top | right | bottom) == 0) { 14769e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout mShapeState.mPadding = null; 14869e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout } else { 14999ec8b0cb375f7e5577ea3ec9f09e6ff7a95de0dAurimas Liutikas if (mShapeState.mPadding == null) { 150c62efa44831b1c60dcbdfd968735e27ac8294439Craig Stout mShapeState.mPadding = new Rect(); 151c62efa44831b1c60dcbdfd968735e27ac8294439Craig Stout } 152c62efa44831b1c60dcbdfd968735e27ac8294439Craig Stout mShapeState.mPadding.set(left, top, right, bottom); 153c62efa44831b1c60dcbdfd968735e27ac8294439Craig Stout } 154e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet invalidateSelf(); 155e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet } 156e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet 157e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet /** 158e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet * Sets padding for this shape, defined by a Rect object. Define the padding 159937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet * in the Rect object as: left, top, right, bottom. 160e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet */ 161e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet public void setPadding(Rect padding) { 162937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet if (padding == null) { 163937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet mShapeState.mPadding = null; 164e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet } else { 1655a59bde085588f95dc067bd1ed64a940f355343cbulic if (mShapeState.mPadding == null) { 1665a59bde085588f95dc067bd1ed64a940f355343cbulic mShapeState.mPadding = new Rect(); 167e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet } 168e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet mShapeState.mPadding.set(padding); 1694c696a0ad561090035960c2f34a058562c7f2aadCraig Stout } 170558f7b70035c50045908efca5f4f3d65df685cacCraig Stout invalidateSelf(); 171558f7b70035c50045908efca5f4f3d65df685cacCraig Stout } 172e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet 173ed57770cf2211040bffe0bddd55e7c28fb47a42aJerome Poichet /** 174ed57770cf2211040bffe0bddd55e7c28fb47a42aJerome Poichet * Sets the intrinsic (default) width for this shape. 175ed57770cf2211040bffe0bddd55e7c28fb47a42aJerome Poichet * 176ed57770cf2211040bffe0bddd55e7c28fb47a42aJerome Poichet * @param width the intrinsic width (in pixels) 17744f004b2612b04510a09a4c1e33f5a109582d669Craig Stout */ 17844f004b2612b04510a09a4c1e33f5a109582d669Craig Stout public void setIntrinsicWidth(int width) { 179c62efa44831b1c60dcbdfd968735e27ac8294439Craig Stout mShapeState.mIntrinsicWidth = width; 180c62efa44831b1c60dcbdfd968735e27ac8294439Craig Stout invalidateSelf(); 181c62efa44831b1c60dcbdfd968735e27ac8294439Craig Stout } 182c62efa44831b1c60dcbdfd968735e27ac8294439Craig Stout 183ed57770cf2211040bffe0bddd55e7c28fb47a42aJerome Poichet /** 184937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet * Sets the intrinsic (default) height for this shape. 185937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet * 1865a59bde085588f95dc067bd1ed64a940f355343cbulic * @param height the intrinsic height (in pixels) 187f0e71182c62bba18e9d8098941a29c4f5031ce36Jerome Poichet */ 1885a59bde085588f95dc067bd1ed64a940f355343cbulic public void setIntrinsicHeight(int height) { 1895a59bde085588f95dc067bd1ed64a940f355343cbulic mShapeState.mIntrinsicHeight = height; 1905a59bde085588f95dc067bd1ed64a940f355343cbulic invalidateSelf(); 191937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet } 192f0e71182c62bba18e9d8098941a29c4f5031ce36Jerome Poichet 1935a59bde085588f95dc067bd1ed64a940f355343cbulic @Override 1945a59bde085588f95dc067bd1ed64a940f355343cbulic public int getIntrinsicWidth() { 1958b55ff20146055bb0c4c5544814fcf530e03649abulic return mShapeState.mIntrinsicWidth; 1964fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout } 1974fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout 1984fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout @Override 199f0e71182c62bba18e9d8098941a29c4f5031ce36Jerome Poichet public int getIntrinsicHeight() { 200f0e71182c62bba18e9d8098941a29c4f5031ce36Jerome Poichet return mShapeState.mIntrinsicHeight; 201e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet } 2024cf79b1c4d38a190317961891f9fd052836710fdCraig Stout 203e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet @Override 204c4788eac909f51e73e05885314d3ace65d0a2f26bulic public boolean getPadding(Rect padding) { 205c62efa44831b1c60dcbdfd968735e27ac8294439Craig Stout if (mShapeState.mPadding != null) { 206c62efa44831b1c60dcbdfd968735e27ac8294439Craig Stout padding.set(mShapeState.mPadding); 20769e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout return true; 208c62efa44831b1c60dcbdfd968735e27ac8294439Craig Stout } else { 209e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet return super.getPadding(padding); 210e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet } 2114c696a0ad561090035960c2f34a058562c7f2aadCraig Stout } 2124c696a0ad561090035960c2f34a058562c7f2aadCraig Stout 2134c696a0ad561090035960c2f34a058562c7f2aadCraig Stout private static int modulateAlpha(int paintAlpha, int alpha) { 2144c696a0ad561090035960c2f34a058562c7f2aadCraig Stout int scale = alpha + (alpha >>> 7); // convert to 0..256 2154c696a0ad561090035960c2f34a058562c7f2aadCraig Stout return paintAlpha * scale >>> 8; 2164c696a0ad561090035960c2f34a058562c7f2aadCraig Stout } 2174c696a0ad561090035960c2f34a058562c7f2aadCraig Stout 2184c696a0ad561090035960c2f34a058562c7f2aadCraig Stout /** 2194c696a0ad561090035960c2f34a058562c7f2aadCraig Stout * Called from the drawable's draw() method after the canvas has been set to 2204c696a0ad561090035960c2f34a058562c7f2aadCraig Stout * draw the shape at (0,0). Subclasses can override for special effects such 2214c696a0ad561090035960c2f34a058562c7f2aadCraig Stout * as multiple layers, stroking, etc. 2224c696a0ad561090035960c2f34a058562c7f2aadCraig Stout */ 2234c696a0ad561090035960c2f34a058562c7f2aadCraig Stout protected void onDraw(Shape shape, Canvas canvas, Paint paint) { 224e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet shape.draw(canvas, paint); 225e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet } 226e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet 227e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet @Override 228e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet public void draw(Canvas canvas) { 229e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet final Rect r = getBounds(); 230e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet final ShapeState state = mShapeState; 23169e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout final Paint paint = state.mPaint; 23269e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout 23369e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout final int prevAlpha = paint.getAlpha(); 23469e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout paint.setAlpha(modulateAlpha(prevAlpha, state.mAlpha)); 23569e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout 23669e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout // only draw shape if it may affect output 23769e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout if (paint.getAlpha() != 0 || paint.getXfermode() != null || paint.hasShadowLayer()) { 23869e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout final boolean clearColorFilter; 23969e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout if (mTintFilter != null && paint.getColorFilter() == null) { 24069e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout paint.setColorFilter(mTintFilter); 24169e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout clearColorFilter = true; 24269e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout } else { 24369e74bd8956577d9a3414b81ec661fd5fee42e19Craig Stout clearColorFilter = false; 244e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet } 245e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet 246e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet if (state.mShape != null) { 2474c696a0ad561090035960c2f34a058562c7f2aadCraig Stout // need the save both for the translate, and for the (unknown) 2484c696a0ad561090035960c2f34a058562c7f2aadCraig Stout // Shape 2494c696a0ad561090035960c2f34a058562c7f2aadCraig Stout final int count = canvas.save(); 2504c696a0ad561090035960c2f34a058562c7f2aadCraig Stout canvas.translate(r.left, r.top); 2514c696a0ad561090035960c2f34a058562c7f2aadCraig Stout onDraw(state.mShape, canvas, paint); 25244f004b2612b04510a09a4c1e33f5a109582d669Craig Stout canvas.restoreToCount(count); 2534c696a0ad561090035960c2f34a058562c7f2aadCraig Stout } else { 2544c696a0ad561090035960c2f34a058562c7f2aadCraig Stout canvas.drawRect(r, paint); 255aadb8928f5920c758c1a385bbc8b8b128a60657cCraig Stout } 256aadb8928f5920c758c1a385bbc8b8b128a60657cCraig Stout 257937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet if (clearColorFilter) { 258a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout paint.setColorFilter(null); 259937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet } 260937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet } 261937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet 262937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet // restore 263937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet paint.setAlpha(prevAlpha); 264aadb8928f5920c758c1a385bbc8b8b128a60657cCraig Stout } 265aadb8928f5920c758c1a385bbc8b8b128a60657cCraig Stout 266937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet @Override 267937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet public int getChangingConfigurations() { 268937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet return super.getChangingConfigurations() 269937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet | mShapeState.mChangingConfigurations; 270937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet } 271937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet 272e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet /** 273e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet * Set the alpha level for this drawable [0..255]. Note that this drawable 274e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet * also has a color in its paint, which has an alpha as well. These two 275a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout * values are automatically combined during drawing. Thus if the color's 276e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet * alpha is 75% (i.e. 192) and the drawable's alpha is 50% (i.e. 128), then 277e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet * the combined alpha that will be used during drawing will be 37.5% (i.e. 278e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet * 96). 279e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet */ 280e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet @Override 281e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet public void setAlpha(int alpha) { 282937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet mShapeState.mAlpha = alpha; 2834fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout invalidateSelf(); 2844fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout } 285c4788eac909f51e73e05885314d3ace65d0a2f26bulic 286937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet @Override 287937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet public int getAlpha() { 2884cf79b1c4d38a190317961891f9fd052836710fdCraig Stout return mShapeState.mAlpha; 2894fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout } 2908b55ff20146055bb0c4c5544814fcf530e03649abulic 2918b55ff20146055bb0c4c5544814fcf530e03649abulic @Override 2924fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout public void setTint(ColorStateList tint, Mode tintMode) { 2934fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout if (mShapeState.mTint != tint || mShapeState.mTintMode != tintMode) { 2944fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout mShapeState.mTint = tint; 2954fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout mShapeState.mTintMode = tintMode; 2964fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout updateTintFilter(); 2974fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout invalidateSelf(); 29870acb0c19be3831a2080e4f902324de16bfbf62eTor Norbye } 2994fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout } 3004cf79b1c4d38a190317961891f9fd052836710fdCraig Stout 301731066a59e10ddc7bb6c95d0b91b3e0e11e10396Craig Stout /** 302937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet * Ensures the tint filter is consistent with the current tint color and 303937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet * mode. 304937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet */ 305937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet private void updateTintFilter() { 30670acb0c19be3831a2080e4f902324de16bfbf62eTor Norbye final ColorStateList tint = mShapeState.mTint; 307731066a59e10ddc7bb6c95d0b91b3e0e11e10396Craig Stout final Mode tintMode = mShapeState.mTintMode; 3084fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout if (tint != null && tintMode != null) { 3094fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout if (mTintFilter == null) { 3104fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout mTintFilter = new PorterDuffColorFilter(0, tintMode); 3114fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout } else { 312a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout mTintFilter.setMode(tintMode); 3134fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout } 3144fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout } else { 3154fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout mTintFilter = null; 3164fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout } 3174fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout } 3184fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout 3194fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout @Override 3204fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout public void setColorFilter(ColorFilter cf) { 3214fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout mShapeState.mPaint.setColorFilter(cf); 3224fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout invalidateSelf(); 323731066a59e10ddc7bb6c95d0b91b3e0e11e10396Craig Stout } 324937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet 3254fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout @Override 3264fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout public int getOpacity() { 3274fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout if (mShapeState.mShape == null) { 3284fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout final Paint p = mShapeState.mPaint; 3294fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout if (p.getXfermode() == null) { 330937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet final int alpha = p.getAlpha(); 331937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet if (alpha == 0) { 332d5dff7f2ff07d060e052083f1e4d9d01c2b7ee8eCraig Stout return PixelFormat.TRANSPARENT; 333d5dff7f2ff07d060e052083f1e4d9d01c2b7ee8eCraig Stout } 334c4788eac909f51e73e05885314d3ace65d0a2f26bulic if (alpha == 255) { 335d5dff7f2ff07d060e052083f1e4d9d01c2b7ee8eCraig Stout return PixelFormat.OPAQUE; 336d5dff7f2ff07d060e052083f1e4d9d01c2b7ee8eCraig Stout } 337d5dff7f2ff07d060e052083f1e4d9d01c2b7ee8eCraig Stout } 338d5dff7f2ff07d060e052083f1e4d9d01c2b7ee8eCraig Stout } 339d5dff7f2ff07d060e052083f1e4d9d01c2b7ee8eCraig Stout // not sure, so be safe 340d5dff7f2ff07d060e052083f1e4d9d01c2b7ee8eCraig Stout return PixelFormat.TRANSLUCENT; 341d5dff7f2ff07d060e052083f1e4d9d01c2b7ee8eCraig Stout } 34277b750bc2094fbe921058d8748fe26f830fbc6c8Dmitri Plotnikov 34377b750bc2094fbe921058d8748fe26f830fbc6c8Dmitri Plotnikov @Override 34477b750bc2094fbe921058d8748fe26f830fbc6c8Dmitri Plotnikov public void setDither(boolean dither) { 34577b750bc2094fbe921058d8748fe26f830fbc6c8Dmitri Plotnikov mShapeState.mPaint.setDither(dither); 34677b750bc2094fbe921058d8748fe26f830fbc6c8Dmitri Plotnikov invalidateSelf(); 347937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet } 348937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet 349937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet @Override 350937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet protected void onBoundsChange(Rect bounds) { 35177b750bc2094fbe921058d8748fe26f830fbc6c8Dmitri Plotnikov super.onBoundsChange(bounds); 352937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet updateShape(); 353937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet } 3544fdd3589c982860b831c0fad63c0082cb9079f47Craig Stout 355937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet @Override 356937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet protected boolean onStateChange(int[] stateSet) { 357937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet final ColorStateList tint = mShapeState.mTint; 358937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet if (tint != null) { 359937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet final int newColor = tint.getColorForState(stateSet, 0); 360937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet final int oldColor = mTintFilter.getColor(); 361937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet if (oldColor != newColor) { 36299ec8b0cb375f7e5577ea3ec9f09e6ff7a95de0dAurimas Liutikas mTintFilter.setColor(newColor); 363937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet invalidateSelf(); 364937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet return true; 365937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet } 366937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet } 367937f910ceb3364a1b84899c2b5f1ff5384f38f84Jerome Poichet 3686ced181ce948d1ae03c1a8402cf5ca31e2bef170Craig Stout return false; 36977b750bc2094fbe921058d8748fe26f830fbc6c8Dmitri Plotnikov } 37077b750bc2094fbe921058d8748fe26f830fbc6c8Dmitri Plotnikov 37177b750bc2094fbe921058d8748fe26f830fbc6c8Dmitri Plotnikov @Override 37277b750bc2094fbe921058d8748fe26f830fbc6c8Dmitri Plotnikov public boolean isStateful() { 37377b750bc2094fbe921058d8748fe26f830fbc6c8Dmitri Plotnikov final ShapeState s = mShapeState; 37477b750bc2094fbe921058d8748fe26f830fbc6c8Dmitri Plotnikov return super.isStateful() || (s.mTint != null && s.mTint.isStateful()); 37577b750bc2094fbe921058d8748fe26f830fbc6c8Dmitri Plotnikov } 3766ced181ce948d1ae03c1a8402cf5ca31e2bef170Craig Stout 37777b750bc2094fbe921058d8748fe26f830fbc6c8Dmitri Plotnikov /** 3786ced181ce948d1ae03c1a8402cf5ca31e2bef170Craig Stout * Subclasses override this to parse custom subelements. If you handle it, 37977b750bc2094fbe921058d8748fe26f830fbc6c8Dmitri Plotnikov * return true, else return <em>super.inflateTag(...)</em>. 3806ced181ce948d1ae03c1a8402cf5ca31e2bef170Craig Stout */ 3816ced181ce948d1ae03c1a8402cf5ca31e2bef170Craig Stout protected boolean inflateTag(String name, Resources r, XmlPullParser parser, 382e2679e4ccab0ce75f701629c22c179165df4f15eJerome Poichet AttributeSet attrs) { 383 384 if ("padding".equals(name)) { 385 TypedArray a = r.obtainAttributes(attrs, 386 com.android.internal.R.styleable.ShapeDrawablePadding); 387 setPadding( 388 a.getDimensionPixelOffset( 389 com.android.internal.R.styleable.ShapeDrawablePadding_left, 0), 390 a.getDimensionPixelOffset( 391 com.android.internal.R.styleable.ShapeDrawablePadding_top, 0), 392 a.getDimensionPixelOffset( 393 com.android.internal.R.styleable.ShapeDrawablePadding_right, 0), 394 a.getDimensionPixelOffset( 395 com.android.internal.R.styleable.ShapeDrawablePadding_bottom, 0)); 396 a.recycle(); 397 return true; 398 } 399 400 return false; 401 } 402 403 @Override 404 public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme) 405 throws XmlPullParserException, IOException { 406 super.inflate(r, parser, attrs, theme); 407 408 TypedArray a = r.obtainAttributes(attrs, com.android.internal.R.styleable.ShapeDrawable); 409 410 int color = mShapeState.mPaint.getColor(); 411 color = a.getColor(com.android.internal.R.styleable.ShapeDrawable_color, color); 412 mShapeState.mPaint.setColor(color); 413 414 boolean dither = a.getBoolean(com.android.internal.R.styleable.ShapeDrawable_dither, false); 415 mShapeState.mPaint.setDither(dither); 416 417 setIntrinsicWidth((int) 418 a.getDimension(com.android.internal.R.styleable.ShapeDrawable_width, 0f)); 419 setIntrinsicHeight((int) 420 a.getDimension(com.android.internal.R.styleable.ShapeDrawable_height, 0f)); 421 422 a.recycle(); 423 424 int type; 425 final int outerDepth = parser.getDepth(); 426 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 427 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 428 if (type != XmlPullParser.START_TAG) { 429 continue; 430 } 431 432 final String name = parser.getName(); 433 // call our subclass 434 if (!inflateTag(name, r, parser, attrs)) { 435 android.util.Log.w("drawable", "Unknown element: " + name + 436 " for ShapeDrawable " + this); 437 } 438 } 439 } 440 441 private void updateShape() { 442 if (mShapeState.mShape != null) { 443 final Rect r = getBounds(); 444 final int w = r.width(); 445 final int h = r.height(); 446 447 mShapeState.mShape.resize(w, h); 448 if (mShapeState.mShaderFactory != null) { 449 mShapeState.mPaint.setShader(mShapeState.mShaderFactory.resize(w, h)); 450 } 451 } 452 invalidateSelf(); 453 } 454 455 @Override 456 public boolean getOutline(Outline outline) { 457 if (mShapeState.mShape == null) { 458 // don't publish outline without a shape 459 return false; 460 } 461 462 return mShapeState.mShape.getOutline(outline); 463 } 464 465 @Override 466 public ConstantState getConstantState() { 467 mShapeState.mChangingConfigurations = getChangingConfigurations(); 468 return mShapeState; 469 } 470 471 @Override 472 public Drawable mutate() { 473 if (!mMutated && super.mutate() == this) { 474 if (mShapeState.mPaint != null) { 475 mShapeState.mPaint = new Paint(mShapeState.mPaint); 476 } else { 477 mShapeState.mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 478 } 479 if (mShapeState.mPadding != null) { 480 mShapeState.mPadding = new Rect(mShapeState.mPadding); 481 } else { 482 mShapeState.mPadding = new Rect(); 483 } 484 try { 485 mShapeState.mShape = mShapeState.mShape.clone(); 486 } catch (CloneNotSupportedException e) { 487 return null; 488 } 489 mMutated = true; 490 } 491 return this; 492 } 493 494 /** 495 * Defines the intrinsic properties of this ShapeDrawable's Shape. 496 */ 497 final static class ShapeState extends ConstantState { 498 int mChangingConfigurations; 499 Paint mPaint; 500 Shape mShape; 501 ColorStateList mTint; 502 Mode mTintMode = Mode.SRC_IN; 503 Rect mPadding; 504 int mIntrinsicWidth; 505 int mIntrinsicHeight; 506 int mAlpha = 255; 507 ShaderFactory mShaderFactory; 508 509 ShapeState(ShapeState orig) { 510 if (orig != null) { 511 mPaint = orig.mPaint; 512 mShape = orig.mShape; 513 mTint = orig.mTint; 514 mTintMode = orig.mTintMode; 515 mPadding = orig.mPadding; 516 mIntrinsicWidth = orig.mIntrinsicWidth; 517 mIntrinsicHeight = orig.mIntrinsicHeight; 518 mAlpha = orig.mAlpha; 519 mShaderFactory = orig.mShaderFactory; 520 } else { 521 mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 522 } 523 } 524 525 @Override 526 public Drawable newDrawable() { 527 return new ShapeDrawable(this); 528 } 529 530 @Override 531 public Drawable newDrawable(Resources res) { 532 return new ShapeDrawable(this); 533 } 534 535 @Override 536 public int getChangingConfigurations() { 537 return mChangingConfigurations; 538 } 539 } 540 541 /** 542 * Base class defines a factory object that is called each time the drawable 543 * is resized (has a new width or height). Its resize() method returns a 544 * corresponding shader, or null. Implement this class if you'd like your 545 * ShapeDrawable to use a special {@link android.graphics.Shader}, such as a 546 * {@link android.graphics.LinearGradient}. 547 */ 548 public static abstract class ShaderFactory { 549 /** 550 * Returns the Shader to be drawn when a Drawable is drawn. The 551 * dimensions of the Drawable are passed because they may be needed to 552 * adjust how the Shader is configured for drawing. This is called by 553 * ShapeDrawable.setShape(). 554 * 555 * @param width the width of the Drawable being drawn 556 * @param height the heigh of the Drawable being drawn 557 * @return the Shader to be drawn 558 */ 559 public abstract Shader resize(int width, int height); 560 } 561 562 // other subclass could wack the Shader's localmatrix based on the 563 // resize params (e.g. scaletofit, etc.). This could be used to scale 564 // a bitmap to fill the bounds without needing any other special casing. 565} 566