diff options
20 files changed, 485 insertions, 77 deletions
diff --git a/api/current.xml b/api/current.xml index d682155..f14df1d 100644 --- a/api/current.xml +++ b/api/current.xml @@ -1905,7 +1905,7 @@ type="int" transient="false" volatile="false" - value="16843550" + value="16843552" static="true" final="true" deprecated="not deprecated" @@ -1916,7 +1916,7 @@ type="int" transient="false" volatile="false" - value="16843549" + value="16843551" static="true" final="true" deprecated="not deprecated" @@ -1927,7 +1927,7 @@ type="int" transient="false" volatile="false" - value="16843551" + value="16843553" static="true" final="true" deprecated="not deprecated" @@ -3804,6 +3804,17 @@ visibility="public" > </field> +<field name="enterFadeDuration" + type="int" + transient="false" + volatile="false" + value="16843549" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="entries" type="int" transient="false" @@ -3848,6 +3859,17 @@ visibility="public" > </field> +<field name="exitFadeDuration" + type="int" + transient="false" + volatile="false" + value="16843550" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="expandableListPreferredChildIndicatorLeft" type="int" transient="false" @@ -9308,7 +9330,7 @@ type="int" transient="false" volatile="false" - value="16843553" + value="16843555" static="true" final="true" deprecated="not deprecated" @@ -9319,7 +9341,7 @@ type="int" transient="false" volatile="false" - value="16843552" + value="16843554" static="true" final="true" deprecated="not deprecated" @@ -83932,6 +83954,17 @@ visibility="public" > </method> +<method name="jumpToCurrentState" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="mutate" return="android.graphics.drawable.Drawable" abstract="false" @@ -84421,6 +84454,32 @@ <parameter name="state" type="android.graphics.drawable.DrawableContainer.DrawableContainerState"> </parameter> </method> +<method name="setEnterFadeDuration" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="ms" type="int"> +</parameter> +</method> +<method name="setExitFadeDuration" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="ms" type="int"> +</parameter> +</method> <method name="unscheduleDrawable" return="void" abstract="false" @@ -84568,6 +84627,28 @@ visibility="public" > </method> +<method name="getEnterFadeDuration" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getExitFadeDuration" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getOpacity" return="int" abstract="false" @@ -84629,6 +84710,32 @@ <parameter name="constant" type="boolean"> </parameter> </method> +<method name="setEnterFadeDuration" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +<parameter name="duration" type="int"> +</parameter> +</method> +<method name="setExitFadeDuration" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +<parameter name="duration" type="int"> +</parameter> +</method> <method name="setVariablePadding" return="void" abstract="false" @@ -190423,6 +190530,17 @@ <parameter name="newSize" type="int"> </parameter> </method> +<field name="NOTHING" + type="int[]" + transient="false" + volatile="false" + value="null" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="WILD_CARD" type="int[]" transient="false" @@ -203765,6 +203883,17 @@ visibility="public" > </method> +<method name="jumpDrawablesToCurrentState" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="layout" return="void" abstract="false" diff --git a/core/java/android/util/StateSet.java b/core/java/android/util/StateSet.java index f3d8159..21d8e45 100644 --- a/core/java/android/util/StateSet.java +++ b/core/java/android/util/StateSet.java @@ -38,6 +38,7 @@ import com.android.internal.R; public class StateSet { public static final int[] WILD_CARD = new int[0]; + public static final int[] NOTHING = new int[] { 0 }; /** * Return whether the stateSetOrSpec is matched by all StateSets. diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index fd7f4a4..340678d 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -8620,6 +8620,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** + * Call {@link Drawable#jumpToCurrentState() Drawable.jumpToCurrentState()} + * on all Drawable objects associated with this view. + */ + public void jumpDrawablesToCurrentState() { + if (mBGDrawable != null) { + mBGDrawable.jumpToCurrentState(); + } + } + + /** * Sets the background color for this view. * @param color the color of the background */ diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 4e90ecd..7629673 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -36,6 +36,7 @@ import android.util.AttributeSet; import android.util.Log; import android.util.LongSparseArray; import android.util.SparseBooleanArray; +import android.util.StateSet; import android.view.ActionMode; import android.view.Gravity; import android.view.HapticFeedbackConstants; @@ -254,6 +255,17 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te Drawable mSelector; /** + * Set to true if we would like to have the selector showing itself. + * We still need to draw and position it even if this is false. + */ + boolean mSelectorShowing; + + /** + * The current position of the selector in the list. + */ + int mSelectorPosition = INVALID_POSITION; + + /** * Defines the selector's location and dimension at drawing time */ Rect mSelectorRect = new Rect(); @@ -1324,6 +1336,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te setSelectedPositionInt(INVALID_POSITION); // Do this before setting mNeedSync since setNextSelectedPosition looks at mNeedSync setNextSelectedPositionInt(INVALID_POSITION); + mSelectorPosition = INVALID_POSITION; mNeedSync = true; mSyncRowId = ss.firstId; mSyncPosition = ss.position; @@ -1416,6 +1429,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te setSelectedPositionInt(INVALID_POSITION); setNextSelectedPositionInt(INVALID_POSITION); mSelectedTop = 0; + mSelectorShowing = false; + mSelectorPosition = INVALID_POSITION; mSelectorRect.setEmpty(); invalidate(); } @@ -1708,7 +1723,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } if (child != scrapView) { - mRecycler.addScrapView(scrapView); + mRecycler.addScrapView(scrapView, position); if (mCacheColorHint != 0) { child.setDrawingCacheBackgroundColor(mCacheColorHint); } @@ -1734,7 +1749,11 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te return child; } - void positionSelector(View sel) { + void positionSelector(int position, View sel) { + if (position != INVALID_POSITION) { + mSelectorPosition = position; + } + final Rect selectorRect = mSelectorRect; selectorRect.set(sel.getLeft(), sel.getTop(), sel.getRight(), sel.getBottom()); positionSelector(selectorRect.left, selectorRect.top, selectorRect.right, @@ -1743,7 +1762,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te final boolean isChildViewEnabled = mIsChildViewEnabled; if (sel.isEnabled() != isChildViewEnabled) { mIsChildViewEnabled = !isChildViewEnabled; - refreshDrawableState(); + if (mSelectorShowing) { + refreshDrawableState(); + } } } @@ -1822,7 +1843,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } private void drawSelector(Canvas canvas) { - if (shouldShowSelector() && mSelectorRect != null && !mSelectorRect.isEmpty()) { + if (!mSelectorRect.isEmpty()) { final Drawable selector = mSelector; selector.setBounds(mSelectorRect); selector.draw(canvas); @@ -1866,7 +1887,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mSelectionRightPadding = padding.right; mSelectionBottomPadding = padding.bottom; sel.setCallback(this); - sel.setState(getDrawableState()); + updateSelectorState(); } /** @@ -1891,7 +1912,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te Drawable selector = mSelector; Rect selectorRect = mSelectorRect; if (selector != null && (isFocused() || touchModeDrawsInPressedState()) - && selectorRect != null && !selectorRect.isEmpty()) { + && !selectorRect.isEmpty()) { final View v = getChildAt(mSelectedPosition - mFirstPosition); @@ -1926,12 +1947,20 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mScrollDown = down; } + void updateSelectorState() { + if (mSelector != null) { + if (shouldShowSelector()) { + mSelector.setState(getDrawableState()); + } else { + mSelector.setState(StateSet.NOTHING); + } + } + } + @Override protected void drawableStateChanged() { super.drawableStateChanged(); - if (mSelector != null) { - mSelector.setState(getDrawableState()); - } + updateSelectorState(); } @Override @@ -2141,7 +2170,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } else { mTouchMode = TOUCH_MODE_DONE_WAITING; } - } } } @@ -2316,10 +2344,10 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mLayoutMode = LAYOUT_NORMAL; if (!mDataChanged) { - layoutChildren(); child.setPressed(true); - positionSelector(child); setPressed(true); + layoutChildren(); + positionSelector(mMotionPosition, child); final int longPressTimeout = ViewConfiguration.getLongPressTimeout(); final boolean longClickable = isLongClickable(); @@ -2566,7 +2594,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te setSelectedPositionInt(mMotionPosition); layoutChildren(); child.setPressed(true); - positionSelector(child); + positionSelector(mMotionPosition, child); setPressed(true); if (mSelector != null) { Drawable d = mSelector.getCurrent(); @@ -2576,16 +2604,17 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } postDelayed(new Runnable() { public void run() { + mTouchMode = TOUCH_MODE_REST; child.setPressed(false); setPressed(false); if (!mDataChanged) { post(performClick); } - mTouchMode = TOUCH_MODE_REST; } }, ViewConfiguration.getPressedStateDuration()); } else { mTouchMode = TOUCH_MODE_REST; + updateSelectorState(); } return true; } else if (!mDataChanged && mAdapter.isEnabled(motionPosition)) { @@ -2593,6 +2622,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } mTouchMode = TOUCH_MODE_REST; + updateSelectorState(); break; case TOUCH_MODE_SCROLL: final int childCount = getChildCount(); @@ -3507,7 +3537,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te count++; int position = firstPosition + i; if (position >= headerViewsCount && position < footerViewsStart) { - mRecycler.addScrapView(child); + mRecycler.addScrapView(child, position); if (ViewDebug.TRACE_RECYCLER) { ViewDebug.trace(child, @@ -3528,7 +3558,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te count++; int position = firstPosition + i; if (position >= headerViewsCount && position < footerViewsStart) { - mRecycler.addScrapView(child); + mRecycler.addScrapView(child, position); if (ViewDebug.TRACE_RECYCLER) { ViewDebug.trace(child, @@ -3563,8 +3593,15 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te if (!inTouchMode && mSelectedPosition != INVALID_POSITION) { final int childIndex = mSelectedPosition - mFirstPosition; if (childIndex >= 0 && childIndex < getChildCount()) { - positionSelector(getChildAt(childIndex)); + positionSelector(mSelectedPosition, getChildAt(childIndex)); + } + } else if (mSelectorPosition != INVALID_POSITION) { + final int childIndex = mSelectorPosition - mFirstPosition; + if (childIndex >= 0 && childIndex < getChildCount()) { + positionSelector(INVALID_POSITION, getChildAt(childIndex)); } + } else { + mSelectorRect.setEmpty(); } mBlockLayoutRequests = false; @@ -3616,7 +3653,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te setSelectedPositionInt(INVALID_POSITION); setNextSelectedPositionInt(INVALID_POSITION); mSelectedTop = 0; - mSelectorRect.setEmpty(); + mSelectorShowing = false; } } @@ -3876,6 +3913,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mNextSelectedPosition = INVALID_POSITION; mNextSelectedRowId = INVALID_ROW_ID; mNeedSync = false; + mSelectorPosition = INVALID_POSITION; checkSelectionChanged(); } @@ -4562,6 +4600,13 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te @ViewDebug.ExportedProperty(category = "list") boolean forceAdd; + /** + * The position the view was removed from when pulled out of the + * scrap heap. + * @hide + */ + int scrappedFromPosition; + public LayoutParams(Context c, AttributeSet attrs) { super(c, attrs); } @@ -4741,23 +4786,12 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te * @return A view from the ScrapViews collection. These are unordered. */ View getScrapView(int position) { - ArrayList<View> scrapViews; if (mViewTypeCount == 1) { - scrapViews = mCurrentScrap; - int size = scrapViews.size(); - if (size > 0) { - return scrapViews.remove(size - 1); - } else { - return null; - } + return retrieveFromScrap(mCurrentScrap, position); } else { int whichScrap = mAdapter.getItemViewType(position); if (whichScrap >= 0 && whichScrap < mScrapViews.length) { - scrapViews = mScrapViews[whichScrap]; - int size = scrapViews.size(); - if (size > 0) { - return scrapViews.remove(size - 1); - } + return retrieveFromScrap(mScrapViews[whichScrap], position); } } return null; @@ -4768,7 +4802,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te * * @param scrap The view to add */ - void addScrapView(View scrap) { + void addScrapView(View scrap, int position) { AbsListView.LayoutParams lp = (AbsListView.LayoutParams) scrap.getLayoutParams(); if (lp == null) { return; @@ -4784,6 +4818,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te return; } + lp.scrappedFromPosition = position; + if (mViewTypeCount == 1) { scrap.dispatchStartTemporaryDetach(); mCurrentScrap.add(scrap); @@ -4810,7 +4846,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te for (int i = count - 1; i >= 0; i--) { final View victim = activeViews[i]; if (victim != null) { - int whichScrap = ((AbsListView.LayoutParams) victim.getLayoutParams()).viewType; + final AbsListView.LayoutParams lp + = (AbsListView.LayoutParams) victim.getLayoutParams(); + int whichScrap = lp.viewType; activeViews[i] = null; @@ -4826,6 +4864,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te scrapViews = mScrapViews[whichScrap]; } victim.dispatchStartTemporaryDetach(); + lp.scrappedFromPosition = mFirstActivePosition + i; scrapViews.add(victim); if (hasListener) { @@ -4911,4 +4950,22 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } } + + static View retrieveFromScrap(ArrayList<View> scrapViews, int position) { + int size = scrapViews.size(); + if (size > 0) { + // See if we still have a view for this position. + for (int i=0; i<size; i++) { + View view = scrapViews.get(i); + if (((AbsListView.LayoutParams)view.getLayoutParams()) + .scrappedFromPosition == position) { + scrapViews.remove(i); + return view; + } + } + return scrapViews.remove(size - 1); + } else { + return null; + } + } } diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java index f5afb94..f16efbd 100644 --- a/core/java/android/widget/AdapterView.java +++ b/core/java/android/widget/AdapterView.java @@ -18,7 +18,6 @@ package android.widget; import android.content.Context; import android.database.DataSetObserver; -import android.os.Handler; import android.os.Parcelable; import android.os.SystemClock; import android.util.AttributeSet; diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java index 46c7d33..936a97d 100644 --- a/core/java/android/widget/GridView.java +++ b/core/java/android/widget/GridView.java @@ -1009,7 +1009,7 @@ public class GridView extends AbsListView { childHeight = child.getMeasuredHeight(); if (mRecycler.shouldRecycleViewType(p.viewType)) { - mRecycler.addScrapView(child); + mRecycler.addScrapView(child, -1); } } @@ -1148,7 +1148,7 @@ public class GridView extends AbsListView { if (dataChanged) { for (int i = 0; i < childCount; i++) { - recycleBin.addScrapView(getChildAt(i)); + recycleBin.addScrapView(getChildAt(i), firstPosition+i); } } else { recycleBin.fillActiveViews(childCount, firstPosition); @@ -1215,11 +1215,11 @@ public class GridView extends AbsListView { recycleBin.scrapActiveViews(); if (sel != null) { - positionSelector(sel); + positionSelector(INVALID_POSITION, sel); mSelectedTop = sel.getTop(); } else if (mTouchMode > TOUCH_MODE_DOWN && mTouchMode < TOUCH_MODE_SCROLL) { View child = getChildAt(mMotionPosition - mFirstPosition); - if (child != null) positionSelector(child); + if (child != null) positionSelector(mMotionPosition, child); } else { mSelectedTop = 0; mSelectorRect.setEmpty(); @@ -1391,6 +1391,11 @@ public class GridView extends AbsListView { if (mCachingStarted) { child.setDrawingCacheEnabled(true); } + + if (recycled && (((AbsListView.LayoutParams)child.getLayoutParams()).scrappedFromPosition) + != position) { + child.jumpDrawablesToCurrentState(); + } } /** diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index b5e103f..e0119e9 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -581,7 +581,7 @@ public class ListView extends AbsListView { final boolean scroll = scrollYDelta != 0; if (scroll) { scrollListItemsBy(-scrollYDelta); - positionSelector(child); + positionSelector(INVALID_POSITION, child); mSelectedTop = child.getTop(); invalidate(); } @@ -1086,7 +1086,7 @@ public class ListView extends AbsListView { if (recycleOnMeasure() && mRecycler.shouldRecycleViewType( ((LayoutParams) child.getLayoutParams()).viewType)) { - mRecycler.addScrapView(child); + mRecycler.addScrapView(child, -1); } } @@ -1203,7 +1203,7 @@ public class ListView extends AbsListView { // Recycle the view before we possibly return from the method if (recyle && recycleBin.shouldRecycleViewType( ((LayoutParams) child.getLayoutParams()).viewType)) { - recycleBin.addScrapView(child); + recycleBin.addScrapView(child, -1); } returnedHeight += child.getMeasuredHeight(); @@ -1507,7 +1507,7 @@ public class ListView extends AbsListView { // already cached in mHeaderViews; if (dataChanged) { for (int i = 0; i < childCount; i++) { - recycleBin.addScrapView(getChildAt(i)); + recycleBin.addScrapView(getChildAt(i), firstPosition+i); if (ViewDebug.TRACE_RECYCLER) { ViewDebug.trace(getChildAt(i), ViewDebug.RecyclerTraceType.MOVE_TO_SCRAP_HEAP, index, i); @@ -1610,19 +1610,19 @@ public class ListView extends AbsListView { if (focused != null) { focused.clearFocus(); } - positionSelector(sel); + positionSelector(INVALID_POSITION, sel); } else { sel.setSelected(false); mSelectorRect.setEmpty(); } } else { - positionSelector(sel); + positionSelector(INVALID_POSITION, sel); } mSelectedTop = sel.getTop(); } else { if (mTouchMode > TOUCH_MODE_DOWN && mTouchMode < TOUCH_MODE_SCROLL) { View child = getChildAt(mMotionPosition - mFirstPosition); - if (child != null) positionSelector(child); + if (child != null) positionSelector(mMotionPosition, child); } else { mSelectedTop = 0; mSelectorRect.setEmpty(); @@ -1703,7 +1703,7 @@ public class ListView extends AbsListView { if (!mDataChanged) { - // Try to use an exsiting view for this position + // Try to use an existing view for this position child = mRecycler.getActiveView(position); if (child != null) { if (ViewDebug.TRACE_RECYCLER) { @@ -1820,6 +1820,11 @@ public class ListView extends AbsListView { if (mCachingStarted && !child.isDrawingCacheEnabled()) { child.setDrawingCacheEnabled(true); } + + if (recycled && (((AbsListView.LayoutParams)child.getLayoutParams()).scrappedFromPosition) + != position) { + child.jumpDrawablesToCurrentState(); + } } @Override @@ -2288,6 +2293,7 @@ public class ListView extends AbsListView { } View selectedView = getSelectedView(); + int selectedPos = mSelectedPosition; int nextSelectedPosition = lookForSelectablePositionOnScreen(direction); int amountToScroll = amountToScroll(direction, nextSelectedPosition); @@ -2305,6 +2311,7 @@ public class ListView extends AbsListView { setSelectedPositionInt(nextSelectedPosition); setNextSelectedPositionInt(nextSelectedPosition); selectedView = getSelectedView(); + selectedPos = nextSelectedPosition; if (mItemsCanFocus && focusResult == null) { // there was no new view found to take focus, make sure we // don't leave focus with the old selection @@ -2345,7 +2352,7 @@ public class ListView extends AbsListView { if (needToRedraw) { if (selectedView != null) { - positionSelector(selectedView); + positionSelector(selectedPos, selectedView); mSelectedTop = selectedView.getTop(); } if (!awakenScrollBars()) { @@ -2841,7 +2848,7 @@ public class ListView extends AbsListView { AbsListView.LayoutParams layoutParams = (LayoutParams) first.getLayoutParams(); if (recycleBin.shouldRecycleViewType(layoutParams.viewType)) { detachViewFromParent(first); - recycleBin.addScrapView(first); + recycleBin.addScrapView(first, mFirstPosition); } else { removeViewInLayout(first); } @@ -2872,7 +2879,7 @@ public class ListView extends AbsListView { AbsListView.LayoutParams layoutParams = (LayoutParams) last.getLayoutParams(); if (recycleBin.shouldRecycleViewType(layoutParams.viewType)) { detachViewFromParent(last); - recycleBin.addScrapView(last); + recycleBin.addScrapView(last, mFirstPosition+lastIndex); } else { removeViewInLayout(last); } diff --git a/core/res/res/drawable/activated_background.xml b/core/res/res/drawable/activated_background.xml index d92fba1..1047e5b 100644 --- a/core/res/res/drawable/activated_background.xml +++ b/core/res/res/drawable/activated_background.xml @@ -14,7 +14,8 @@ limitations under the License. --> -<selector xmlns:android="http://schemas.android.com/apk/res/android"> +<selector xmlns:android="http://schemas.android.com/apk/res/android" + android:exitFadeDuration="@android:integer/config_mediumAnimTime"> <item android:state_activated="true" android:drawable="@android:drawable/list_selector_background_selected" /> <item android:drawable="@color/transparent" /> </selector> diff --git a/core/res/res/drawable/activated_background_holo_dark.xml b/core/res/res/drawable/activated_background_holo_dark.xml index febf2c4..a29bcb9 100644 --- a/core/res/res/drawable/activated_background_holo_dark.xml +++ b/core/res/res/drawable/activated_background_holo_dark.xml @@ -14,7 +14,8 @@ limitations under the License. --> -<selector xmlns:android="http://schemas.android.com/apk/res/android"> +<selector xmlns:android="http://schemas.android.com/apk/res/android" + android:exitFadeDuration="@android:integer/config_mediumAnimTime"> <item android:state_activated="true" android:drawable="@android:drawable/list_activated_holo" /> <item android:drawable="@color/transparent" /> </selector> diff --git a/core/res/res/drawable/activated_background_holo_light.xml b/core/res/res/drawable/activated_background_holo_light.xml index febf2c4..a29bcb9 100644 --- a/core/res/res/drawable/activated_background_holo_light.xml +++ b/core/res/res/drawable/activated_background_holo_light.xml @@ -14,7 +14,8 @@ limitations under the License. --> -<selector xmlns:android="http://schemas.android.com/apk/res/android"> +<selector xmlns:android="http://schemas.android.com/apk/res/android" + android:exitFadeDuration="@android:integer/config_mediumAnimTime"> <item android:state_activated="true" android:drawable="@android:drawable/list_activated_holo" /> <item android:drawable="@color/transparent" /> </selector> diff --git a/core/res/res/drawable/activated_background_light.xml b/core/res/res/drawable/activated_background_light.xml index 5d5681d..7d737db 100644 --- a/core/res/res/drawable/activated_background_light.xml +++ b/core/res/res/drawable/activated_background_light.xml @@ -14,7 +14,8 @@ limitations under the License. --> -<selector xmlns:android="http://schemas.android.com/apk/res/android"> +<selector xmlns:android="http://schemas.android.com/apk/res/android" + android:exitFadeDuration="@android:integer/config_mediumAnimTime"> <item android:state_activated="true" android:drawable="@drawable/list_selector_background_selected_light" /> <item android:drawable="@color/transparent" /> </selector> diff --git a/core/res/res/drawable/list_selector_background.xml b/core/res/res/drawable/list_selector_background.xml index 6fb0661..f5eb12d 100644 --- a/core/res/res/drawable/list_selector_background.xml +++ b/core/res/res/drawable/list_selector_background.xml @@ -14,7 +14,8 @@ limitations under the License. --> -<selector xmlns:android="http://schemas.android.com/apk/res/android"> +<selector xmlns:android="http://schemas.android.com/apk/res/android" + android:exitFadeDuration="@android:integer/config_mediumAnimTime"> <item android:state_window_focused="false" android:drawable="@color/transparent" /> diff --git a/core/res/res/drawable/list_selector_background_light.xml b/core/res/res/drawable/list_selector_background_light.xml index 4da7e21..50a821b 100644 --- a/core/res/res/drawable/list_selector_background_light.xml +++ b/core/res/res/drawable/list_selector_background_light.xml @@ -14,7 +14,8 @@ limitations under the License. --> -<selector xmlns:android="http://schemas.android.com/apk/res/android"> +<selector xmlns:android="http://schemas.android.com/apk/res/android" + android:exitFadeDuration="@android:integer/config_mediumAnimTime"> <item android:state_window_focused="false" android:drawable="@color/transparent" /> diff --git a/core/res/res/drawable/list_selector_holo_dark.xml b/core/res/res/drawable/list_selector_holo_dark.xml index e4c5c52..9a6cb89 100644 --- a/core/res/res/drawable/list_selector_holo_dark.xml +++ b/core/res/res/drawable/list_selector_holo_dark.xml @@ -14,7 +14,8 @@ limitations under the License. --> -<selector xmlns:android="http://schemas.android.com/apk/res/android"> +<selector xmlns:android="http://schemas.android.com/apk/res/android" + android:exitFadeDuration="@android:integer/config_mediumAnimTime"> <item android:state_window_focused="false" android:drawable="@color/transparent" /> diff --git a/core/res/res/drawable/list_selector_holo_light.xml b/core/res/res/drawable/list_selector_holo_light.xml index 17631bd..844259e 100644 --- a/core/res/res/drawable/list_selector_holo_light.xml +++ b/core/res/res/drawable/list_selector_holo_light.xml @@ -14,7 +14,8 @@ limitations under the License. --> -<selector xmlns:android="http://schemas.android.com/apk/res/android"> +<selector xmlns:android="http://schemas.android.com/apk/res/android" + android:exitFadeDuration="@android:integer/config_mediumAnimTime"> <item android:state_window_focused="false" android:drawable="@color/transparent" /> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index a2fa1a3..1083452 100755 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -2786,6 +2786,10 @@ same pixel configuration as the screen (for instance: a ARGB 8888 bitmap with an RGB 565 screen). --> <attr name="dither" format="boolean" /> + <!-- Amount of time (in milliseconds) to fade in a new state drawable. --> + <attr name="enterFadeDuration" format="integer" /> + <!-- Amount of time (in milliseconds) to fade out an old state drawable. --> + <attr name="exitFadeDuration" format="integer" /> </declare-styleable> <declare-styleable name="AnimationDrawable"> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index fd7e984..f8752d3 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -1358,6 +1358,8 @@ <public type="attr" name="buttonGroupStyle" /> <public type="attr" name="alertDialogButtonGroupStyle" /> <public type="attr" name="homeAsUpIndicator" /> + <public type="attr" name="enterFadeDuration" /> + <public type="attr" name="exitFadeDuration" /> <public type="anim" name="animator_fade_in" /> <public type="anim" name="animator_fade_out" /> diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java index 7b2d9d7..baa9d62 100644 --- a/graphics/java/android/graphics/drawable/Drawable.java +++ b/graphics/java/android/graphics/drawable/Drawable.java @@ -423,6 +423,13 @@ public abstract class Drawable { } /** + * If this Drawable does transition animations between states, ask that + * it immediately jump to the current state and skip any active animations. + */ + public void jumpToCurrentState() { + } + + /** * @return The current drawable that will be used by this drawable. For simple drawables, this * is just the drawable itself. For drawables that change state like * {@link StateListDrawable} and {@link LevelListDrawable} this will be the child drawable diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java index 124d907..e55a746 100644 --- a/graphics/java/android/graphics/drawable/DrawableContainer.java +++ b/graphics/java/android/graphics/drawable/DrawableContainer.java @@ -21,6 +21,7 @@ import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.PixelFormat; import android.graphics.Rect; +import android.os.SystemClock; /** * A helper class that contains several {@link Drawable}s and selects which one to use. @@ -28,6 +29,8 @@ import android.graphics.Rect; * You can subclass it to create your own DrawableContainers or directly use one its child classes. */ public class DrawableContainer extends Drawable implements Drawable.Callback { + private static final boolean DEBUG = false; + private static final String TAG = "DrawableContainer"; /** * To be proper, we should have a getter for dither (and alpha, etc.) @@ -48,6 +51,12 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { private int mCurIndex = -1; private boolean mMutated; + // Animations. + private Runnable mAnimationRunnable; + private long mEnterAnimationEnd; + private long mExitAnimationEnd; + private Drawable mLastDrawable; + // overrides from Drawable @Override @@ -55,6 +64,9 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { if (mCurrDrawable != null) { mCurrDrawable.draw(canvas); } + if (mLastDrawable != null) { + mLastDrawable.draw(canvas); + } } @Override @@ -83,7 +95,11 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { if (mAlpha != alpha) { mAlpha = alpha; if (mCurrDrawable != null) { - mCurrDrawable.setAlpha(alpha); + if (mEnterAnimationEnd == 0) { + mCurrDrawable.setAlpha(alpha); + } else { + animate(false); + } } } } @@ -108,8 +124,29 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { } } + /** + * Change the global fade duration when a new drawable is entering + * the scene. + * @param ms The amount of time to fade in milliseconds. + */ + public void setEnterFadeDuration(int ms) { + mDrawableContainerState.mEnterFadeDuration = ms; + } + + /** + * Change the global fade duration when a new drawable is leaving + * the scene. + * @param ms The amount of time to fade in milliseconds. + */ + public void setExitFadeDuration(int ms) { + mDrawableContainerState.mExitFadeDuration = ms; + } + @Override protected void onBoundsChange(Rect bounds) { + if (mLastDrawable != null) { + mLastDrawable.setBounds(bounds); + } if (mCurrDrawable != null) { mCurrDrawable.setBounds(bounds); } @@ -121,7 +158,34 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { } @Override + public void jumpToCurrentState() { + boolean changed = false; + if (mLastDrawable != null) { + mLastDrawable.jumpToCurrentState(); + mLastDrawable = null; + changed = true; + } + if (mCurrDrawable != null) { + mCurrDrawable.jumpToCurrentState(); + } + if (mExitAnimationEnd != 0) { + mExitAnimationEnd = 0; + changed = true; + } + if (mEnterAnimationEnd != 0) { + mEnterAnimationEnd = 0; + changed = true; + } + if (changed) { + invalidateSelf(); + } + } + + @Override protected boolean onStateChange(int[] state) { + if (mLastDrawable != null) { + return mLastDrawable.setState(state); + } if (mCurrDrawable != null) { return mCurrDrawable.setState(state); } @@ -130,6 +194,9 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { @Override protected boolean onLevelChange(int level) { + if (mLastDrawable != null) { + return mLastDrawable.setLevel(level); + } if (mCurrDrawable != null) { return mCurrDrawable.setLevel(level); } @@ -168,22 +235,19 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { return mCurrDrawable != null ? mCurrDrawable.getMinimumHeight() : 0; } - public void invalidateDrawable(Drawable who) - { + public void invalidateDrawable(Drawable who) { if (who == mCurrDrawable && mCallback != null) { mCallback.invalidateDrawable(this); } } - public void scheduleDrawable(Drawable who, Runnable what, long when) - { + public void scheduleDrawable(Drawable who, Runnable what, long when) { if (who == mCurrDrawable && mCallback != null) { mCallback.scheduleDrawable(this, what, when); } } - public void unscheduleDrawable(Drawable who, Runnable what) - { + public void unscheduleDrawable(Drawable who, Runnable what) { if (who == mCurrDrawable && mCallback != null) { mCallback.unscheduleDrawable(this, what); } @@ -192,6 +256,9 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { @Override public boolean setVisible(boolean visible, boolean restart) { boolean changed = super.setVisible(visible, restart); + if (mLastDrawable != null) { + mLastDrawable.setVisible(visible, restart); + } if (mCurrDrawable != null) { mCurrDrawable.setVisible(visible, restart); } @@ -208,16 +275,39 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { if (idx == mCurIndex) { return false; } - if (idx >= 0 && idx < mDrawableContainerState.mNumChildren) { - Drawable d = mDrawableContainerState.mDrawables[idx]; + + final long now = SystemClock.uptimeMillis(); + + if (DEBUG) android.util.Log.i(TAG, toString() + " from " + mCurIndex + " to " + idx + + ": exit=" + mDrawableContainerState.mExitFadeDuration + + " enter=" + mDrawableContainerState.mEnterFadeDuration); + + if (mDrawableContainerState.mExitFadeDuration > 0) { + if (mLastDrawable != null) { + mLastDrawable.setVisible(false, false); + } if (mCurrDrawable != null) { - mCurrDrawable.setVisible(false, false); + mLastDrawable = mCurrDrawable; + mExitAnimationEnd = now + mDrawableContainerState.mExitFadeDuration; + } else { + mLastDrawable = null; + mExitAnimationEnd = 0; } + } else if (mCurrDrawable != null) { + mCurrDrawable.setVisible(false, false); + } + + if (idx >= 0 && idx < mDrawableContainerState.mNumChildren) { + Drawable d = mDrawableContainerState.mDrawables[idx]; mCurrDrawable = d; mCurIndex = idx; if (d != null) { + if (mDrawableContainerState.mEnterFadeDuration > 0) { + mEnterAnimationEnd = now + mDrawableContainerState.mEnterFadeDuration; + } else { + d.setAlpha(mAlpha); + } d.setVisible(isVisible(), true); - d.setAlpha(mAlpha); d.setDither(mDrawableContainerState.mDither); d.setColorFilter(mColorFilter); d.setState(getState()); @@ -225,16 +315,72 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { d.setBounds(getBounds()); } } else { - if (mCurrDrawable != null) { - mCurrDrawable.setVisible(false, false); - } mCurrDrawable = null; mCurIndex = -1; } + + if (mEnterAnimationEnd != 0 || mExitAnimationEnd != 0) { + if (mAnimationRunnable == null) { + mAnimationRunnable = new Runnable() { + @Override public void run() { + animate(true); + invalidateSelf(); + } + }; + } else { + unscheduleSelf(mAnimationRunnable); + } + // Compute first frame and schedule next animation. + animate(true); + } + invalidateSelf(); + return true; } + void animate(boolean schedule) { + final long now = SystemClock.uptimeMillis(); + boolean animating = false; + if (mCurrDrawable != null) { + if (mEnterAnimationEnd != 0) { + if (mEnterAnimationEnd <= now) { + mCurrDrawable.setAlpha(mAlpha); + mEnterAnimationEnd = 0; + } else { + int animAlpha = (int)((mEnterAnimationEnd-now)*255) + / mDrawableContainerState.mEnterFadeDuration; + if (DEBUG) android.util.Log.i(TAG, toString() + " cur alpha " + animAlpha); + mCurrDrawable.setAlpha(((255-animAlpha)*mAlpha)/255); + animating = true; + } + } + } else { + mEnterAnimationEnd = 0; + } + if (mLastDrawable != null) { + if (mExitAnimationEnd != 0) { + if (mExitAnimationEnd <= now) { + mLastDrawable.setVisible(false, false); + mLastDrawable = null; + mExitAnimationEnd = 0; + } else { + int animAlpha = (int)((mExitAnimationEnd-now)*255) + / mDrawableContainerState.mExitFadeDuration; + if (DEBUG) android.util.Log.i(TAG, toString() + " last alpha " + animAlpha); + mLastDrawable.setAlpha((animAlpha*mAlpha)/255); + animating = true; + } + } + } else { + mExitAnimationEnd = 0; + } + + if (schedule && animating) { + scheduleSelf(mAnimationRunnable, now + 1000/60); + } + } + @Override public Drawable getCurrent() { return mCurrDrawable; @@ -300,6 +446,9 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { boolean mDither = DEFAULT_DITHER; + int mEnterFadeDuration; + int mExitFadeDuration; + DrawableContainerState(DrawableContainerState orig, DrawableContainer owner, Resources res) { mOwner = owner; @@ -340,6 +489,9 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { mDither = orig.mDither; + mEnterFadeDuration = orig.mEnterFadeDuration; + mExitFadeDuration = orig.mExitFadeDuration; + } else { mDrawables = new Drawable[10]; mNumChildren = 0; @@ -476,6 +628,22 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { } } + public final void setEnterFadeDuration(int duration) { + mEnterFadeDuration = duration; + } + + public final int getEnterFadeDuration() { + return mEnterFadeDuration; + } + + public final void setExitFadeDuration(int duration) { + mExitFadeDuration = duration; + } + + public final int getExitFadeDuration() { + return mExitFadeDuration; + } + public final int getOpacity() { if (mHaveOpacity) { return mOpacity; diff --git a/graphics/java/android/graphics/drawable/StateListDrawable.java b/graphics/java/android/graphics/drawable/StateListDrawable.java index 239be40..384ca81 100644 --- a/graphics/java/android/graphics/drawable/StateListDrawable.java +++ b/graphics/java/android/graphics/drawable/StateListDrawable.java @@ -20,6 +20,7 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; +import java.util.Arrays; import android.content.res.Resources; import android.content.res.TypedArray; @@ -44,6 +45,7 @@ import android.util.StateSet; * @attr ref android.R.styleable#DrawableStates_state_checkable * @attr ref android.R.styleable#DrawableStates_state_checked * @attr ref android.R.styleable#DrawableStates_state_selected + * @attr ref android.R.styleable#DrawableStates_state_activated * @attr ref android.R.styleable#DrawableStates_state_active * @attr ref android.R.styleable#DrawableStates_state_single * @attr ref android.R.styleable#DrawableStates_state_first @@ -52,6 +54,9 @@ import android.util.StateSet; * @attr ref android.R.styleable#DrawableStates_state_pressed */ public class StateListDrawable extends DrawableContainer { + private static final boolean DEBUG = false; + private static final String TAG = "StateListDrawable"; + /** * To be proper, we should have a getter for dither (and alpha, etc.) * so that proxy classes like this can save/restore their delegates' @@ -93,6 +98,8 @@ public class StateListDrawable extends DrawableContainer { @Override protected boolean onStateChange(int[] stateSet) { int idx = mStateListState.indexOfStateSet(stateSet); + if (DEBUG) android.util.Log.i(TAG, "onStateChange " + this + " states " + + Arrays.toString(stateSet) + " found " + idx); if (idx < 0) { idx = mStateListState.indexOfStateSet(StateSet.WILD_CARD); } @@ -117,6 +124,10 @@ public class StateListDrawable extends DrawableContainer { com.android.internal.R.styleable.StateListDrawable_variablePadding, false)); mStateListState.setConstantSize(a.getBoolean( com.android.internal.R.styleable.StateListDrawable_constantSize, false)); + mStateListState.setEnterFadeDuration(a.getInt( + com.android.internal.R.styleable.StateListDrawable_enterFadeDuration, 0)); + mStateListState.setExitFadeDuration(a.getInt( + com.android.internal.R.styleable.StateListDrawable_exitFadeDuration, 0)); setDither(a.getBoolean(com.android.internal.R.styleable.StateListDrawable_dither, DEFAULT_DITHER)); @@ -251,7 +262,7 @@ public class StateListDrawable extends DrawableContainer { } static final class StateListState extends DrawableContainerState { - private int[][] mStateSets; + int[][] mStateSets; StateListState(StateListState orig, StateListDrawable owner, Resources res) { super(orig, owner, res); |