- Add new dialog themes without a title bar. - Add new Theme.Holo.NoActionBar.DialogWhenLarge. - Remove old Extended and Theme.Light.Holo themes. - Reorder the public definitions to keep things nice. Also @hide the MipmapDrawable class. Change-Id: Ic69a56e9b28aacf441780633f37f0bc6a475d08a
313 lines
12 KiB
Java
313 lines
12 KiB
Java
/*
|
|
* Copyright (C) 2006 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package android.graphics.drawable;
|
|
|
|
import org.xmlpull.v1.XmlPullParser;
|
|
import org.xmlpull.v1.XmlPullParserException;
|
|
|
|
import android.content.res.Resources;
|
|
import android.content.res.TypedArray;
|
|
import android.graphics.Rect;
|
|
import android.util.AttributeSet;
|
|
|
|
import java.io.IOException;
|
|
|
|
/**
|
|
* @hide -- we are probably moving to do MipMaps in another way (more integrated
|
|
* with the resource system).
|
|
*
|
|
* A resource that manages a number of alternate Drawables, and which actually draws the one which
|
|
* size matches the most closely the drawing bounds. Providing several pre-scaled version of the
|
|
* drawable helps minimizing the aliasing artifacts that can be introduced by the scaling.
|
|
*
|
|
* <p>
|
|
* Use {@link #addDrawable(Drawable)} to define the different Drawables that will represent the
|
|
* mipmap levels of this MipmapDrawable. The mipmap Drawable that will actually be used when this
|
|
* MipmapDrawable is drawn is the one which has the smallest intrinsic height greater or equal than
|
|
* the bounds' height. This selection ensures that the best available mipmap level is scaled down to
|
|
* draw this MipmapDrawable.
|
|
* </p>
|
|
*
|
|
* If the bounds' height is larger than the largest mipmap, the largest mipmap will be scaled up.
|
|
* Note that Drawables without intrinsic height (i.e. with a negative value, such as Color) will
|
|
* only be used if no other mipmap Drawable are provided. The Drawables' intrinsic heights should
|
|
* not be changed after the Drawable has been added to this MipmapDrawable.
|
|
*
|
|
* <p>
|
|
* The different mipmaps' parameters (opacity, padding, color filter, gravity...) should typically
|
|
* be similar to ensure a continuous visual appearance when the MipmapDrawable is scaled. The aspect
|
|
* ratio of the different mipmaps should especially be equal.
|
|
* </p>
|
|
*
|
|
* A typical example use of a MipmapDrawable would be for an image which is intended to be scaled at
|
|
* various sizes, and for which one wants to provide pre-scaled versions to precisely control its
|
|
* appearance.
|
|
*
|
|
* <p>
|
|
* The intrinsic size of a MipmapDrawable are inferred from those of the largest mipmap (in terms of
|
|
* {@link Drawable#getIntrinsicHeight()}). On the opposite, its minimum
|
|
* size is defined by the smallest provided mipmap.
|
|
* </p>
|
|
|
|
* It can be defined in an XML file with the <code><mipmap></code> element.
|
|
* Each mipmap Drawable is defined in a nested <code><item></code>. For example:
|
|
* <pre>
|
|
* <mipmap xmlns:android="http://schemas.android.com/apk/res/android">
|
|
* <item android:drawable="@drawable/my_image_8" />
|
|
* <item android:drawable="@drawable/my_image_32" />
|
|
* <item android:drawable="@drawable/my_image_128" />
|
|
* </mipmap>
|
|
*</pre>
|
|
* <p>
|
|
* With this XML saved into the res/drawable/ folder of the project, it can be referenced as
|
|
* the drawable for an {@link android.widget.ImageView}. Assuming that the heights of the provided
|
|
* drawables are respectively 8, 32 and 128 pixels, the first one will be scaled down when the
|
|
* bounds' height is lower or equal than 8 pixels. The second drawable will then be used up to a
|
|
* height of 32 pixels and the largest drawable will be used for greater heights.
|
|
* </p>
|
|
* @attr ref android.R.styleable#MipmapDrawableItem_drawable
|
|
*/
|
|
public class MipmapDrawable extends DrawableContainer {
|
|
private final MipmapContainerState mMipmapContainerState;
|
|
private boolean mMutated;
|
|
|
|
public MipmapDrawable() {
|
|
this(null, null);
|
|
}
|
|
|
|
/**
|
|
* Adds a Drawable to the list of available mipmap Drawables. The Drawable actually used when
|
|
* this MipmapDrawable is drawn is determined from its bounds.
|
|
*
|
|
* This method has no effect if drawable is null.
|
|
*
|
|
* @param drawable The Drawable that will be added to list of available mipmap Drawables.
|
|
*/
|
|
|
|
public void addDrawable(Drawable drawable) {
|
|
if (drawable != null) {
|
|
mMipmapContainerState.addDrawable(drawable);
|
|
onDrawableAdded();
|
|
}
|
|
}
|
|
|
|
private void onDrawableAdded() {
|
|
// selectDrawable assumes that the container content does not change.
|
|
// When a Drawable is added, the same index can correspond to a new Drawable, and since
|
|
// selectDrawable has a fast exit case when oldIndex==newIndex, the new drawable could end
|
|
// up not being used in place of the previous one if they happen to share the same index.
|
|
// This make sure the new computed index can actually replace the previous one.
|
|
selectDrawable(-1);
|
|
onBoundsChange(getBounds());
|
|
}
|
|
|
|
// overrides from Drawable
|
|
|
|
@Override
|
|
protected void onBoundsChange(Rect bounds) {
|
|
final int index = mMipmapContainerState.indexForBounds(bounds);
|
|
|
|
// Will call invalidateSelf() if needed
|
|
selectDrawable(index);
|
|
|
|
super.onBoundsChange(bounds);
|
|
}
|
|
|
|
@Override
|
|
public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs)
|
|
throws XmlPullParserException, IOException {
|
|
|
|
super.inflate(r, parser, attrs);
|
|
|
|
int type;
|
|
|
|
final int innerDepth = parser.getDepth() + 1;
|
|
int depth;
|
|
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
|
|
&& ((depth = parser.getDepth()) >= innerDepth
|
|
|| type != XmlPullParser.END_TAG)) {
|
|
if (type != XmlPullParser.START_TAG) {
|
|
continue;
|
|
}
|
|
|
|
if (depth > innerDepth || !parser.getName().equals("item")) {
|
|
continue;
|
|
}
|
|
|
|
TypedArray a = r.obtainAttributes(attrs,
|
|
com.android.internal.R.styleable.MipmapDrawableItem);
|
|
|
|
int drawableRes = a.getResourceId(
|
|
com.android.internal.R.styleable.MipmapDrawableItem_drawable, 0);
|
|
|
|
a.recycle();
|
|
|
|
Drawable dr;
|
|
if (drawableRes != 0) {
|
|
dr = r.getDrawable(drawableRes);
|
|
} else {
|
|
while ((type = parser.next()) == XmlPullParser.TEXT) {
|
|
}
|
|
if (type != XmlPullParser.START_TAG) {
|
|
throw new XmlPullParserException(
|
|
parser.getPositionDescription()
|
|
+ ": <item> tag requires a 'drawable' attribute or "
|
|
+ "child tag defining a drawable");
|
|
}
|
|
dr = Drawable.createFromXmlInner(r, parser, attrs);
|
|
}
|
|
|
|
mMipmapContainerState.addDrawable(dr);
|
|
}
|
|
|
|
onDrawableAdded();
|
|
}
|
|
|
|
@Override
|
|
public Drawable mutate() {
|
|
if (!mMutated && super.mutate() == this) {
|
|
mMipmapContainerState.mMipmapHeights = mMipmapContainerState.mMipmapHeights.clone();
|
|
mMutated = true;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
private final static class MipmapContainerState extends DrawableContainerState {
|
|
private int[] mMipmapHeights;
|
|
|
|
MipmapContainerState(MipmapContainerState orig, MipmapDrawable owner, Resources res) {
|
|
super(orig, owner, res);
|
|
|
|
if (orig != null) {
|
|
mMipmapHeights = orig.mMipmapHeights;
|
|
} else {
|
|
mMipmapHeights = new int[getChildren().length];
|
|
}
|
|
|
|
// Change the default value
|
|
setConstantSize(true);
|
|
}
|
|
|
|
/**
|
|
* Returns the index of the child mipmap drawable that will best fit the provided bounds.
|
|
* This index is determined by comparing bounds' height and children intrinsic heights.
|
|
* The returned mipmap index is the smallest mipmap which height is greater or equal than
|
|
* the bounds' height. If the bounds' height is larger than the largest mipmap, the largest
|
|
* mipmap index is returned.
|
|
*
|
|
* @param bounds The bounds of the MipMapDrawable.
|
|
* @return The index of the child Drawable that will best fit these bounds, or -1 if there
|
|
* are no children mipmaps.
|
|
*/
|
|
public int indexForBounds(Rect bounds) {
|
|
final int boundsHeight = bounds.height();
|
|
final int N = getChildCount();
|
|
for (int i = 0; i < N; i++) {
|
|
if (boundsHeight <= mMipmapHeights[i]) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
// No mipmap larger than bounds found. Use largest one which will be scaled up.
|
|
if (N > 0) {
|
|
return N - 1;
|
|
}
|
|
// No Drawable mipmap at all
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* Adds a Drawable to the list of available mipmap Drawables. This list can be retrieved
|
|
* using {@link DrawableContainer.DrawableContainerState#getChildren()} and this method
|
|
* ensures that it is always sorted by increasing {@link Drawable#getIntrinsicHeight()}.
|
|
*
|
|
* @param drawable The Drawable that will be added to children list
|
|
*/
|
|
public void addDrawable(Drawable drawable) {
|
|
// Insert drawable in last position, correctly resetting cached values and
|
|
// especially mComputedConstantSize
|
|
int pos = addChild(drawable);
|
|
|
|
// Bubble sort the last drawable to restore the sort by intrinsic height
|
|
final int drawableHeight = drawable.getIntrinsicHeight();
|
|
|
|
while (pos > 0) {
|
|
final Drawable previousDrawable = mDrawables[pos-1];
|
|
final int previousIntrinsicHeight = previousDrawable.getIntrinsicHeight();
|
|
|
|
if (drawableHeight < previousIntrinsicHeight) {
|
|
mDrawables[pos] = previousDrawable;
|
|
mMipmapHeights[pos] = previousIntrinsicHeight;
|
|
|
|
mDrawables[pos-1] = drawable;
|
|
mMipmapHeights[pos-1] = drawableHeight;
|
|
pos--;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Intrinsic sizes are those of the largest available mipmap.
|
|
* Minimum sizes are those of the smallest available mipmap.
|
|
*/
|
|
@Override
|
|
protected void computeConstantSize() {
|
|
final int N = getChildCount();
|
|
if (N > 0) {
|
|
final Drawable smallestDrawable = mDrawables[0];
|
|
mConstantMinimumWidth = smallestDrawable.getMinimumWidth();
|
|
mConstantMinimumHeight = smallestDrawable.getMinimumHeight();
|
|
|
|
final Drawable largestDrawable = mDrawables[N-1];
|
|
mConstantWidth = largestDrawable.getIntrinsicWidth();
|
|
mConstantHeight = largestDrawable.getIntrinsicHeight();
|
|
} else {
|
|
mConstantWidth = mConstantHeight = -1;
|
|
mConstantMinimumWidth = mConstantMinimumHeight = 0;
|
|
}
|
|
mComputedConstantSize = true;
|
|
}
|
|
|
|
@Override
|
|
public Drawable newDrawable() {
|
|
return new MipmapDrawable(this, null);
|
|
}
|
|
|
|
@Override
|
|
public Drawable newDrawable(Resources res) {
|
|
return new MipmapDrawable(this, res);
|
|
}
|
|
|
|
@Override
|
|
public void growArray(int oldSize, int newSize) {
|
|
super.growArray(oldSize, newSize);
|
|
int[] newInts = new int[newSize];
|
|
System.arraycopy(mMipmapHeights, 0, newInts, 0, oldSize);
|
|
mMipmapHeights = newInts;
|
|
}
|
|
}
|
|
|
|
private MipmapDrawable(MipmapContainerState state, Resources res) {
|
|
MipmapContainerState as = new MipmapContainerState(state, this, res);
|
|
mMipmapContainerState = as;
|
|
setConstantState(as);
|
|
onDrawableAdded();
|
|
}
|
|
}
|