Merge "Unify touch feedback drawable and reveal drawable"
This commit is contained in:
committed by
Android (Google) Code Review
commit
9b3c038c92
@@ -825,6 +825,7 @@ package android {
|
||||
field public static final int persistent = 16842765; // 0x101000d
|
||||
field public static final int persistentDrawingCache = 16842990; // 0x10100ee
|
||||
field public static final deprecated int phoneNumber = 16843111; // 0x1010167
|
||||
field public static final int pinned = 16843776; // 0x1010400
|
||||
field public static final int pivotX = 16843189; // 0x10101b5
|
||||
field public static final int pivotY = 16843190; // 0x10101b6
|
||||
field public static final int popupAnimationStyle = 16843465; // 0x10102c9
|
||||
@@ -2196,6 +2197,8 @@ package android {
|
||||
field public static final int Widget_Quantum_Button_Borderless = 16974393; // 0x1030239
|
||||
field public static final int Widget_Quantum_Button_Borderless_Small = 16974394; // 0x103023a
|
||||
field public static final int Widget_Quantum_Button_Inset = 16974395; // 0x103023b
|
||||
field public static final int Widget_Quantum_Button_Paper = 16974503; // 0x10302a7
|
||||
field public static final int Widget_Quantum_Button_Paper_Color = 16974504; // 0x10302a8
|
||||
field public static final int Widget_Quantum_Button_Small = 16974396; // 0x103023c
|
||||
field public static final int Widget_Quantum_Button_Toggle = 16974397; // 0x103023d
|
||||
field public static final int Widget_Quantum_CalendarView = 16974398; // 0x103023e
|
||||
@@ -2232,6 +2235,8 @@ package android {
|
||||
field public static final int Widget_Quantum_Light_Button = 16974452; // 0x1030274
|
||||
field public static final int Widget_Quantum_Light_Button_Borderless_Small = 16974453; // 0x1030275
|
||||
field public static final int Widget_Quantum_Light_Button_Inset = 16974454; // 0x1030276
|
||||
field public static final int Widget_Quantum_Light_Button_Paper = 16974505; // 0x10302a9
|
||||
field public static final int Widget_Quantum_Light_Button_Paper_Color = 16974506; // 0x10302aa
|
||||
field public static final int Widget_Quantum_Light_Button_Small = 16974455; // 0x1030277
|
||||
field public static final int Widget_Quantum_Light_Button_Toggle = 16974456; // 0x1030278
|
||||
field public static final int Widget_Quantum_Light_CalendarView = 16974457; // 0x1030279
|
||||
|
||||
@@ -14,10 +14,6 @@
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<reveal xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@color/transparent" />
|
||||
<item>
|
||||
<nine-patch android:src="@drawable/btn_qntm_alpha"
|
||||
android:tint="@color/btn_default_pressed_quantum_dark" />
|
||||
</item>
|
||||
</reveal>
|
||||
<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:tint="@color/btn_default_pressed_quantum_dark"
|
||||
android:mask="@drawable/btn_qntm_alpha" />
|
||||
|
||||
@@ -14,10 +14,6 @@
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<reveal xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@color/transparent" />
|
||||
<item>
|
||||
<nine-patch android:src="@drawable/btn_qntm_alpha"
|
||||
android:tint="@color/btn_default_pressed_quantum_light" />
|
||||
</item>
|
||||
</reveal>
|
||||
<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:tint="@color/btn_default_pressed_quantum_light"
|
||||
android:mask="@drawable/btn_qntm_alpha" />
|
||||
|
||||
@@ -14,21 +14,16 @@
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<reveal xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<selector>
|
||||
<item android:state_enabled="false">
|
||||
<nine-patch android:src="@drawable/btn_qntm_alpha"
|
||||
android:tint="@color/btn_default_normal_quantum_light" />
|
||||
</item>
|
||||
<item>
|
||||
<nine-patch android:src="@drawable/btn_qntm_alpha"
|
||||
android:tint="@color/theme_color_500" />
|
||||
</item>
|
||||
</selector>
|
||||
</item>
|
||||
<item>
|
||||
<nine-patch android:src="@drawable/btn_qntm_alpha"
|
||||
android:tint="@color/theme_color_300" />
|
||||
</item>
|
||||
</reveal>
|
||||
<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:tint="@color/theme_color_300">
|
||||
<selector>
|
||||
<item android:state_enabled="false">
|
||||
<nine-patch android:src="@drawable/btn_qntm_alpha"
|
||||
android:tint="@color/btn_default_normal_quantum_dark" />
|
||||
</item>
|
||||
<item>
|
||||
<nine-patch android:src="@drawable/btn_qntm_alpha"
|
||||
android:tint="@color/theme_color_500" />
|
||||
</item>
|
||||
</selector>
|
||||
</touch-feedback>
|
||||
|
||||
@@ -14,21 +14,16 @@
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<reveal xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<selector>
|
||||
<item android:state_enabled="false">
|
||||
<nine-patch android:src="@drawable/btn_qntm_alpha"
|
||||
android:tint="@color/btn_default_normal_quantum_dark" />
|
||||
</item>
|
||||
<item>
|
||||
<nine-patch android:src="@drawable/btn_qntm_alpha"
|
||||
android:tint="@color/theme_color_500" />
|
||||
</item>
|
||||
</selector>
|
||||
</item>
|
||||
<item>
|
||||
<nine-patch android:src="@drawable/btn_qntm_alpha"
|
||||
android:tint="@color/theme_color_700" />
|
||||
</item>
|
||||
</reveal>
|
||||
<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:tint="@color/theme_color_700">
|
||||
<selector>
|
||||
<item android:state_enabled="false">
|
||||
<nine-patch android:src="@drawable/btn_qntm_alpha"
|
||||
android:tint="@color/btn_default_normal_quantum_dark" />
|
||||
</item>
|
||||
<item>
|
||||
<nine-patch android:src="@drawable/btn_qntm_alpha"
|
||||
android:tint="@color/theme_color_500" />
|
||||
</item>
|
||||
</selector>
|
||||
</touch-feedback>
|
||||
|
||||
@@ -14,13 +14,8 @@
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<reveal xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<nine-patch android:src="@drawable/btn_qntm_alpha"
|
||||
android:tint="@color/btn_default_normal_quantum_dark" />
|
||||
</item>
|
||||
<item>
|
||||
<nine-patch android:src="@drawable/btn_qntm_alpha"
|
||||
android:tint="@color/btn_default_pressed_quantum_dark" />
|
||||
</item>
|
||||
</reveal>
|
||||
<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:tint="@color/btn_default_pressed_quantum_dark">
|
||||
<nine-patch android:src="@drawable/btn_qntm_alpha"
|
||||
android:tint="@color/btn_default_normal_quantum_dark" />
|
||||
</touch-feedback>
|
||||
|
||||
@@ -14,13 +14,8 @@
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<reveal xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<nine-patch android:src="@drawable/btn_qntm_alpha"
|
||||
android:tint="@color/btn_default_normal_quantum_light" />
|
||||
</item>
|
||||
<item>
|
||||
<nine-patch android:src="@drawable/btn_qntm_alpha"
|
||||
android:tint="@color/btn_default_pressed_quantum_light" />
|
||||
</item>
|
||||
</reveal>
|
||||
<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:tint="@color/btn_default_pressed_quantum_light">
|
||||
<nine-patch android:src="@drawable/btn_qntm_alpha"
|
||||
android:tint="@color/btn_default_normal_quantum_light" />
|
||||
</touch-feedback>
|
||||
|
||||
@@ -15,4 +15,4 @@
|
||||
-->
|
||||
|
||||
<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/lighter_gray" />
|
||||
android:tint="@color/lighter_gray" />
|
||||
|
||||
@@ -15,4 +15,4 @@
|
||||
-->
|
||||
|
||||
<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/darker_gray" />
|
||||
android:tint="@color/darker_gray" />
|
||||
|
||||
@@ -14,7 +14,5 @@
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<reveal xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@color/transparent" />
|
||||
<item android:drawable="@color/lighter_gray" />
|
||||
</reveal>
|
||||
<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:tint="@color/lighter_gray" />
|
||||
|
||||
@@ -14,7 +14,5 @@
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<reveal xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@color/transparent" />
|
||||
<item android:drawable="@color/darker_gray" />
|
||||
</reveal>
|
||||
<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:tint="@color/darker_gray" />
|
||||
|
||||
@@ -4429,6 +4429,20 @@
|
||||
<attr name="color" />
|
||||
</declare-styleable>
|
||||
|
||||
<!-- Drawable used to show animated touch feedback. -->
|
||||
<declare-styleable name="TouchFeedbackDrawable">
|
||||
<!-- The tint to use for feedback ripples. This attribute is mandatory. -->
|
||||
<attr name="tint" />
|
||||
<!-- Specifies the Porter-Duff blending mode used to apply the tint. The default vlaue is src_atop, which draws over the opaque parts of the drawable. -->
|
||||
<attr name="tintMode" />
|
||||
<!-- Whether to pin feedback ripples to the center of the drawable. Default value is false. -->
|
||||
<attr name="pinned" format="boolean" />
|
||||
<!-- Optional drawable used to mask ripple bounds before projection. -->
|
||||
<attr name="mask" format="reference" />
|
||||
<!-- Optional drawable onto which ripples are projected. -->
|
||||
<attr name="drawable" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="ScaleDrawable">
|
||||
<!-- Scale width, expressed as a percentage of the drawable's bound. The value's
|
||||
format is XX%. For instance: 100%, 12.5%, etc.-->
|
||||
|
||||
@@ -2106,6 +2106,7 @@
|
||||
<public type="attr" name="transitionGroup" />
|
||||
<public type="attr" name="castsShadow" />
|
||||
<public type="attr" name="requiredForProfile"/>
|
||||
<public type="attr" name="pinned" />
|
||||
|
||||
<public type="id" name="shared_element_name" />
|
||||
|
||||
@@ -2311,4 +2312,10 @@
|
||||
<public type="style" name="Quantum.Light.ButtonBar.AlertDialog" />
|
||||
<public type="style" name="Quantum.Light.ButtonBar" />
|
||||
<public type="style" name="Quantum.Light.SegmentedButton" />
|
||||
|
||||
<public type="style" name="Widget.Quantum.Button.Paper" />
|
||||
<public type="style" name="Widget.Quantum.Button.Paper.Color" />
|
||||
|
||||
<public type="style" name="Widget.Quantum.Light.Button.Paper" />
|
||||
<public type="style" name="Widget.Quantum.Light.Button.Paper.Color" />
|
||||
</resources>
|
||||
|
||||
317
graphics/java/android/graphics/drawable/DrawableWrapper.java
Normal file
317
graphics/java/android/graphics/drawable/DrawableWrapper.java
Normal file
@@ -0,0 +1,317 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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 android.content.res.Resources;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Xfermode;
|
||||
|
||||
/**
|
||||
* A Drawable that wraps another Drawable.
|
||||
*/
|
||||
class DrawableWrapper extends Drawable implements Drawable.Callback {
|
||||
private WrapperState mWrapperState;
|
||||
|
||||
/** Local drawable backed by its own constant state. */
|
||||
private Drawable mWrappedDrawable;
|
||||
|
||||
private boolean mMutated;
|
||||
|
||||
/** @hide */
|
||||
@Override
|
||||
public boolean isProjected() {
|
||||
return mWrappedDrawable.isProjected();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAutoMirrored(boolean mirrored) {
|
||||
mWrappedDrawable.setAutoMirrored(mirrored);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAutoMirrored() {
|
||||
return mWrappedDrawable.isAutoMirrored();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinimumWidth() {
|
||||
return mWrappedDrawable.getMinimumWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinimumHeight() {
|
||||
return mWrappedDrawable.getMinimumHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIntrinsicWidth() {
|
||||
return mWrappedDrawable.getIntrinsicWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIntrinsicHeight() {
|
||||
return mWrappedDrawable.getIntrinsicHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getCurrent() {
|
||||
return mWrappedDrawable.getCurrent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateDrawable(Drawable who) {
|
||||
final Callback callback = getCallback();
|
||||
if (callback != null) {
|
||||
callback.invalidateDrawable(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scheduleDrawable(Drawable who, Runnable what, long when) {
|
||||
final Callback callback = getCallback();
|
||||
if (callback != null) {
|
||||
callback.scheduleDrawable(this, what, when);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unscheduleDrawable(Drawable who, Runnable what) {
|
||||
final Callback callback = getCallback();
|
||||
if (callback != null) {
|
||||
callback.unscheduleDrawable(this, what);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
mWrappedDrawable.draw(canvas);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getChangingConfigurations() {
|
||||
return mWrappedDrawable.getChangingConfigurations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getPadding(Rect padding) {
|
||||
return mWrappedDrawable.getPadding(padding);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rect getDirtyBounds() {
|
||||
return mWrappedDrawable.getDirtyBounds();
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
public boolean supportsHotspots() {
|
||||
return mWrappedDrawable.supportsHotspots();
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
public void setHotspot(int id, float x, float y) {
|
||||
mWrappedDrawable.setHotspot(id, x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
public void removeHotspot(int id) {
|
||||
mWrappedDrawable.removeHotspot(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
public void clearHotspots() {
|
||||
mWrappedDrawable.clearHotspots();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setVisible(boolean visible, boolean restart) {
|
||||
// Must call through to super().
|
||||
super.setVisible(visible, restart);
|
||||
return mWrappedDrawable.setVisible(visible, restart);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAlpha(int alpha) {
|
||||
mWrappedDrawable.setAlpha(alpha);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAlpha() {
|
||||
return mWrappedDrawable.getAlpha();
|
||||
}
|
||||
|
||||
/** {@hide} */
|
||||
@Override
|
||||
public void setLayoutDirection(int layoutDirection) {
|
||||
mWrappedDrawable.setLayoutDirection(layoutDirection);
|
||||
}
|
||||
|
||||
/** {@hide} */
|
||||
@Override
|
||||
public int getLayoutDirection() {
|
||||
return mWrappedDrawable.getLayoutDirection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColorFilter(ColorFilter cf) {
|
||||
mWrappedDrawable.setColorFilter(cf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColorFilter getColorFilter() {
|
||||
return mWrappedDrawable.getColorFilter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFilterBitmap(boolean filter) {
|
||||
mWrappedDrawable.setFilterBitmap(filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setXfermode(Xfermode mode) {
|
||||
mWrappedDrawable.setXfermode(mode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOpacity() {
|
||||
return mWrappedDrawable.getOpacity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStateful() {
|
||||
return mWrappedDrawable.isStateful();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean setState(int[] stateSet) {
|
||||
return super.setState(stateSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int[] getState() {
|
||||
return super.getState();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onStateChange(int[] state) {
|
||||
// Don't override setState(), getState().
|
||||
return mWrappedDrawable.setState(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onLevelChange(int level) {
|
||||
// Don't override setLevel(), getLevel().
|
||||
return mWrappedDrawable.setLevel(level);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setBounds(int left, int top, int right, int bottom) {
|
||||
super.setBounds(left, top, right, bottom);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setBounds(Rect bounds) {
|
||||
super.setBounds(bounds);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBoundsChange(Rect bounds) {
|
||||
// Don't override setBounds(), getBounds().
|
||||
mWrappedDrawable.setBounds(bounds);
|
||||
}
|
||||
|
||||
protected void setConstantState(WrapperState wrapperState, Resources res) {
|
||||
mWrapperState = wrapperState;
|
||||
|
||||
// Load a new drawable from the constant state.
|
||||
if (wrapperState == null || wrapperState.mWrappedConstantState == null) {
|
||||
mWrappedDrawable = null;
|
||||
} else if (res != null) {
|
||||
mWrappedDrawable = wrapperState.mWrappedConstantState.newDrawable(res);
|
||||
} else {
|
||||
mWrappedDrawable = wrapperState.mWrappedConstantState.newDrawable();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConstantState getConstantState() {
|
||||
return mWrapperState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable mutate() {
|
||||
if (!mMutated) {
|
||||
mWrappedDrawable = mWrappedDrawable.mutate();
|
||||
mMutated = true;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the wrapped drawable and update the constant state.
|
||||
*
|
||||
* @param drawable
|
||||
* @param res
|
||||
*/
|
||||
protected final void setDrawable(Drawable drawable, Resources res) {
|
||||
if (mWrappedDrawable != null) {
|
||||
mWrappedDrawable.setCallback(null);
|
||||
}
|
||||
|
||||
mWrappedDrawable = drawable;
|
||||
|
||||
if (drawable != null) {
|
||||
drawable.setCallback(this);
|
||||
|
||||
mWrapperState.mWrappedConstantState = drawable.getConstantState();
|
||||
} else {
|
||||
mWrapperState.mWrappedConstantState = null;
|
||||
}
|
||||
}
|
||||
|
||||
protected final Drawable getDrawable() {
|
||||
return mWrappedDrawable;
|
||||
}
|
||||
|
||||
static abstract class WrapperState extends ConstantState {
|
||||
ConstantState mWrappedConstantState;
|
||||
|
||||
WrapperState(WrapperState orig) {
|
||||
if (orig != null) {
|
||||
mWrappedConstantState = orig.mWrappedConstantState;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getChangingConfigurations() {
|
||||
return mWrappedConstantState.getChangingConfigurations();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,15 +21,18 @@ import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.PorterDuff.Mode;
|
||||
import android.graphics.PorterDuffXfermode;
|
||||
import android.graphics.Rect;
|
||||
import android.os.SystemClock;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.android.internal.R;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
@@ -37,23 +40,9 @@ import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* An extension of LayerDrawable that is intended to react to touch hotspots
|
||||
* and reveal the second layer atop the first.
|
||||
* <p>
|
||||
* It can be defined in an XML file with the <code><reveal></code> element.
|
||||
* Each Drawable in the transition is defined in a nested <code><item></code>.
|
||||
* For more information, see the guide to <a href="{@docRoot}
|
||||
* guide/topics/resources/drawable-resource.html">Drawable Resources</a>.
|
||||
*
|
||||
* @attr ref android.R.styleable#LayerDrawableItem_left
|
||||
* @attr ref android.R.styleable#LayerDrawableItem_top
|
||||
* @attr ref android.R.styleable#LayerDrawableItem_right
|
||||
* @attr ref android.R.styleable#LayerDrawableItem_bottom
|
||||
* @attr ref android.R.styleable#LayerDrawableItem_drawable
|
||||
* @attr ref android.R.styleable#LayerDrawableItem_id
|
||||
* @hide
|
||||
*/
|
||||
public class TouchFeedbackDrawable extends Drawable {
|
||||
public class TouchFeedbackDrawable extends DrawableWrapper {
|
||||
private final Rect mTempRect = new Rect();
|
||||
private final Rect mPaddingRect = new Rect();
|
||||
|
||||
@@ -77,32 +66,43 @@ public class TouchFeedbackDrawable extends Drawable {
|
||||
/** Paint used to control appearance of ripples. */
|
||||
private Paint mRipplePaint;
|
||||
|
||||
/** Paint used to control reveal layer masking. */
|
||||
private Paint mMaskingPaint;
|
||||
|
||||
/** Target density of the display into which ripples are drawn. */
|
||||
private float mDensity = 1.0f;
|
||||
|
||||
/** Whether the animation runnable has been posted. */
|
||||
private boolean mAnimating;
|
||||
|
||||
TouchFeedbackDrawable() {
|
||||
this(new TouchFeedbackState(null), null);
|
||||
/** The drawable to use as the mask. */
|
||||
private Drawable mMask;
|
||||
|
||||
/* package */TouchFeedbackDrawable() {
|
||||
this(null, null);
|
||||
}
|
||||
|
||||
TouchFeedbackDrawable(TouchFeedbackState state, Resources res) {
|
||||
mState = new TouchFeedbackState(state);
|
||||
|
||||
setConstantState(mState, res);
|
||||
|
||||
if (res != null) {
|
||||
mDensity = res.getDisplayMetrics().density;
|
||||
}
|
||||
|
||||
mState = state;
|
||||
}
|
||||
|
||||
private void setConstantState(TouchFeedbackState wrapperState, Resources res) {
|
||||
super.setConstantState(wrapperState, res);
|
||||
|
||||
@Override
|
||||
public void setColorFilter(ColorFilter cf) {
|
||||
// Not supported.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAlpha(int alpha) {
|
||||
// Not supported.
|
||||
// Load a new mask drawable from the constant state.
|
||||
if (wrapperState == null || wrapperState.mMaskState == null) {
|
||||
mMask = null;
|
||||
} else if (res != null) {
|
||||
mMask = wrapperState.mMaskState.newDrawable(res);
|
||||
} else {
|
||||
mMask = wrapperState.mMaskState.newDrawable();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -111,10 +111,21 @@ public class TouchFeedbackDrawable extends Drawable {
|
||||
PixelFormat.TRANSLUCENT : PixelFormat.TRANSPARENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBoundsChange(Rect bounds) {
|
||||
super.onBoundsChange(bounds);
|
||||
|
||||
if (mMask != null) {
|
||||
mMask.setBounds(bounds);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onStateChange(int[] stateSet) {
|
||||
final ColorStateList stateList = mState.mColorStateList;
|
||||
if (stateList != null && mRipplePaint != null) {
|
||||
super.onStateChange(stateSet);
|
||||
|
||||
if (mRipplePaint != null) {
|
||||
final ColorStateList stateList = mState.mTint;
|
||||
final int newColor = stateList.getColorForState(stateSet, 0);
|
||||
final int oldColor = mRipplePaint.getColor();
|
||||
if (oldColor != newColor) {
|
||||
@@ -132,12 +143,12 @@ public class TouchFeedbackDrawable extends Drawable {
|
||||
*/
|
||||
@Override
|
||||
public boolean isProjected() {
|
||||
return true;
|
||||
return mState.mProjected;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStateful() {
|
||||
return mState.mColorStateList != null && mState.mColorStateList.isStateful();
|
||||
return super.isStateful() || mState.mTint.isStateful();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -145,15 +156,77 @@ public class TouchFeedbackDrawable extends Drawable {
|
||||
throws XmlPullParserException, IOException {
|
||||
super.inflate(r, parser, attrs);
|
||||
|
||||
final TypedArray a = r.obtainAttributes(
|
||||
attrs, com.android.internal.R.styleable.ColorDrawable);
|
||||
mState.mColorStateList = a.getColorStateList(
|
||||
com.android.internal.R.styleable.ColorDrawable_color);
|
||||
final TypedArray a = r.obtainAttributes(attrs, R.styleable.TouchFeedbackDrawable);
|
||||
|
||||
mState.mTint = a.getColorStateList(R.styleable.TouchFeedbackDrawable_tint);
|
||||
mState.mTintMode = Drawable.parseTintMode(
|
||||
a.getInt(R.styleable.TouchFeedbackDrawable_tintMode, -1), Mode.SRC_ATOP);
|
||||
mState.mPinned = a.getBoolean(R.styleable.TouchFeedbackDrawable_pinned, false);
|
||||
|
||||
if (mState.mTint == null) {
|
||||
throw new XmlPullParserException(parser.getPositionDescription()
|
||||
+ ": <touch-feedback> tag requires a 'tint' attribute");
|
||||
}
|
||||
|
||||
Drawable mask = a.getDrawable(R.styleable.TouchFeedbackDrawable_mask);
|
||||
final int drawableRes = a.getResourceId(R.styleable.TouchFeedbackDrawable_drawable, 0);
|
||||
a.recycle();
|
||||
|
||||
final Drawable dr;
|
||||
if (drawableRes != 0) {
|
||||
dr = r.getDrawable(drawableRes);
|
||||
} else {
|
||||
int type;
|
||||
while ((type = parser.next()) == XmlPullParser.TEXT) {
|
||||
// Find the next non-text element.
|
||||
}
|
||||
|
||||
if (type == XmlPullParser.START_TAG) {
|
||||
dr = Drawable.createFromXmlInner(r, parser, attrs);
|
||||
} else {
|
||||
dr = null;
|
||||
}
|
||||
}
|
||||
|
||||
// If no mask is set, implicitly use the lower drawable.
|
||||
if (mask == null) {
|
||||
mask = dr;
|
||||
}
|
||||
|
||||
// If neither a mask not a bottom layer was specified, assume we're
|
||||
// projecting onto a parent surface.
|
||||
mState.mProjected = mask == null && dr == null;
|
||||
|
||||
if (dr != null) {
|
||||
setDrawable(dr, r);
|
||||
} else {
|
||||
// For now at least, we MUST have a wrapped drawable.
|
||||
setDrawable(new ColorDrawable(Color.TRANSPARENT), r);
|
||||
}
|
||||
|
||||
setMaskDrawable(mask, r);
|
||||
setTargetDensity(r.getDisplayMetrics());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the wrapped drawable and update the constant state.
|
||||
*
|
||||
* @param drawable
|
||||
* @param res
|
||||
*/
|
||||
void setMaskDrawable(Drawable drawable, Resources res) {
|
||||
mMask = drawable;
|
||||
|
||||
if (drawable != null) {
|
||||
// Nobody cares if the mask has a callback.
|
||||
drawable.setCallback(null);
|
||||
|
||||
mState.mMaskState = drawable.getConstantState();
|
||||
} else {
|
||||
mState.mMaskState = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the density at which this drawable will be rendered.
|
||||
*
|
||||
@@ -175,6 +248,9 @@ public class TouchFeedbackDrawable extends Drawable {
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Maybe we should set hotspots for state/id combinations? So touch
|
||||
* would be state_pressed and the pointer ID.
|
||||
*
|
||||
* @hide until hotspot APIs are finalized
|
||||
*/
|
||||
@Override
|
||||
@@ -186,19 +262,22 @@ public class TouchFeedbackDrawable extends Drawable {
|
||||
|
||||
final Ripple ripple = mTouchedRipples.get(id);
|
||||
if (ripple == null) {
|
||||
final Rect bounds = getBounds();
|
||||
final Rect padding = mPaddingRect;
|
||||
getPadding(padding);
|
||||
|
||||
final Rect bounds = getBounds();
|
||||
final Ripple newRipple = new Ripple(bounds, padding, bounds.exactCenterX(),
|
||||
bounds.exactCenterY(), mDensity);
|
||||
if (mState.mPinned) {
|
||||
x = bounds.exactCenterX();
|
||||
y = bounds.exactCenterY();
|
||||
}
|
||||
|
||||
final Ripple newRipple = new Ripple(bounds, padding, x, y, mDensity);
|
||||
newRipple.enter();
|
||||
|
||||
mActiveRipples.add(newRipple);
|
||||
mTouchedRipples.put(id, newRipple);
|
||||
} else {
|
||||
// TODO: How do we want to respond to movement?
|
||||
//ripple.move(x, y);
|
||||
} else if (!mState.mPinned) {
|
||||
ripple.move(x, y);
|
||||
}
|
||||
|
||||
scheduleAnimation();
|
||||
@@ -254,7 +333,7 @@ public class TouchFeedbackDrawable extends Drawable {
|
||||
|
||||
if (mAnimationRunnable == null) {
|
||||
mAnimationRunnable = new Runnable() {
|
||||
@Override
|
||||
@Override
|
||||
public void run() {
|
||||
mAnimating = false;
|
||||
scheduleAnimation();
|
||||
@@ -269,47 +348,77 @@ public class TouchFeedbackDrawable extends Drawable {
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
// The lower layer always draws normally.
|
||||
super.draw(canvas);
|
||||
|
||||
if (mActiveRipples == null || mActiveRipples.size() == 0) {
|
||||
// No ripples to draw.
|
||||
return;
|
||||
}
|
||||
|
||||
final ArrayList<Ripple> activeRipples = mActiveRipples;
|
||||
if (activeRipples == null || activeRipples.isEmpty()) {
|
||||
// Nothing to draw, we're done here.
|
||||
return;
|
||||
}
|
||||
final Drawable mask = mMask;
|
||||
final Rect bounds = mask == null ? null : mask.getBounds();
|
||||
|
||||
final ColorStateList stateList = mState.mColorStateList;
|
||||
if (stateList == null) {
|
||||
// No color, we're done here.
|
||||
return;
|
||||
}
|
||||
|
||||
final int color = stateList.getColorForState(getState(), Color.TRANSPARENT);
|
||||
if (color == Color.TRANSPARENT) {
|
||||
// No color, we're done here.
|
||||
return;
|
||||
}
|
||||
|
||||
if (mRipplePaint == null) {
|
||||
mRipplePaint = new Paint();
|
||||
mRipplePaint.setAntiAlias(true);
|
||||
}
|
||||
|
||||
mRipplePaint.setColor(color);
|
||||
|
||||
final int restoreCount = canvas.save();
|
||||
|
||||
// Draw ripples directly onto the canvas.
|
||||
// Draw ripples into a layer that merges using SRC_IN.
|
||||
boolean hasRipples = false;
|
||||
int rippleRestoreCount = -1;
|
||||
int n = activeRipples.size();
|
||||
for (int i = 0; i < n; i++) {
|
||||
final Ripple ripple = activeRipples.get(i);
|
||||
if (!ripple.active()) {
|
||||
// TODO: Mark and sweep is more efficient.
|
||||
activeRipples.remove(i);
|
||||
i--;
|
||||
n--;
|
||||
} else {
|
||||
ripple.draw(canvas, mRipplePaint);
|
||||
// If we're masking the ripple layer, make sure we have a layer first.
|
||||
if (mask != null && rippleRestoreCount < 0) {
|
||||
rippleRestoreCount = canvas.saveLayer(bounds.left, bounds.top,
|
||||
bounds.right, bounds.bottom, getMaskingPaint(SRC_ATOP), 0);
|
||||
canvas.clipRect(bounds);
|
||||
}
|
||||
|
||||
hasRipples |= ripple.draw(canvas, getRipplePaint());
|
||||
}
|
||||
}
|
||||
|
||||
canvas.restoreToCount(restoreCount);
|
||||
// If we have ripples, mask them.
|
||||
if (mask != null && hasRipples) {
|
||||
canvas.saveLayer(bounds.left, bounds.top, bounds.right,
|
||||
bounds.bottom, getMaskingPaint(DST_IN), 0);
|
||||
mask.draw(canvas);
|
||||
}
|
||||
|
||||
// Composite the layers if needed:
|
||||
// 1. Mask DST_IN
|
||||
// 2. Ripples SRC_ATOP
|
||||
// 3. Lower n/a
|
||||
if (rippleRestoreCount > 0) {
|
||||
canvas.restoreToCount(rippleRestoreCount);
|
||||
}
|
||||
}
|
||||
|
||||
private Paint getRipplePaint() {
|
||||
if (mRipplePaint == null) {
|
||||
mRipplePaint = new Paint();
|
||||
mRipplePaint.setAntiAlias(true);
|
||||
|
||||
final int color = mState.mTint.getColorForState(getState(), Color.TRANSPARENT);
|
||||
mRipplePaint.setColor(color);
|
||||
}
|
||||
return mRipplePaint;
|
||||
}
|
||||
|
||||
private static final PorterDuffXfermode SRC_ATOP = new PorterDuffXfermode(Mode.SRC_ATOP);
|
||||
private static final PorterDuffXfermode DST_IN = new PorterDuffXfermode(Mode.DST_IN);
|
||||
|
||||
private Paint getMaskingPaint(PorterDuffXfermode mode) {
|
||||
if (mMaskingPaint == null) {
|
||||
mMaskingPaint = new Paint();
|
||||
}
|
||||
mMaskingPaint.setXfermode(mode);
|
||||
return mMaskingPaint;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -322,39 +431,46 @@ public class TouchFeedbackDrawable extends Drawable {
|
||||
final Rect rippleBounds = mTempRect;
|
||||
final ArrayList<Ripple> activeRipples = mActiveRipples;
|
||||
if (activeRipples != null) {
|
||||
final int N = activeRipples.size();
|
||||
for (int i = 0; i < N; i++) {
|
||||
activeRipples.get(i).getBounds(rippleBounds);
|
||||
drawingBounds.union(rippleBounds);
|
||||
}
|
||||
final int N = activeRipples.size();
|
||||
for (int i = 0; i < N; i++) {
|
||||
activeRipples.get(i).getBounds(rippleBounds);
|
||||
drawingBounds.union(rippleBounds);
|
||||
}
|
||||
}
|
||||
|
||||
dirtyBounds.union(drawingBounds);
|
||||
dirtyBounds.union(super.getDirtyBounds());
|
||||
return dirtyBounds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConstantState getConstantState() {
|
||||
// TODO: Can we just rely on super.getConstantState()?
|
||||
return mState;
|
||||
}
|
||||
|
||||
static class TouchFeedbackState extends ConstantState {
|
||||
ColorStateList mColorStateList;
|
||||
static class TouchFeedbackState extends WrapperState {
|
||||
ConstantState mMaskState;
|
||||
ColorStateList mTint;
|
||||
Mode mTintMode;
|
||||
boolean mPinned;
|
||||
boolean mProjected;
|
||||
|
||||
public TouchFeedbackState(TouchFeedbackState orig) {
|
||||
super(orig);
|
||||
|
||||
if (orig != null) {
|
||||
mColorStateList = orig.mColorStateList;
|
||||
mTint = orig.mTint;
|
||||
mTintMode = orig.mTintMode;
|
||||
mMaskState = orig.mMaskState;
|
||||
mPinned = orig.mPinned;
|
||||
mProjected = orig.mProjected;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getChangingConfigurations() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable newDrawable() {
|
||||
return newDrawable(null);
|
||||
return new TouchFeedbackDrawable(this, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user