summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/java/android/view/View.java47
-rw-r--r--core/java/android/view/ViewGroup.java57
-rw-r--r--core/java/android/view/ViewParent.java10
-rw-r--r--core/java/android/view/ViewRootImpl.java4
-rw-r--r--core/java/android/widget/AbsListView.java119
-rw-r--r--core/java/android/widget/EdgeEffect.java2
-rw-r--r--core/java/android/widget/GridView.java1
-rw-r--r--core/java/android/widget/ListView.java1
8 files changed, 229 insertions, 12 deletions
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 32f5732..c16eb31 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1759,6 +1759,16 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
static final int LAYOUT_DIRECTION_RESOLVED = 0x00000008;
+ /**
+ * Indicates that the view is tracking some sort of transient state
+ * that the app should not need to be aware of, but that the framework
+ * should take special care to preserve.
+ *
+ * @hide
+ */
+ static final int HAS_TRANSIENT_STATE = 0x00000010;
+
+
/* End of masks for mPrivateFlags2 */
static final int DRAG_MASK = DRAG_CAN_ACCEPT | DRAG_HOVERED;
@@ -4889,6 +4899,43 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
}
/**
+ * Indicates whether the view is currently tracking transient state that the
+ * app should not need to concern itself with saving and restoring, but that
+ * the framework should take special note to preserve when possible.
+ *
+ * @return true if the view has transient state
+ *
+ * @hide
+ */
+ @ViewDebug.ExportedProperty(category = "layout")
+ public boolean hasTransientState() {
+ return (mPrivateFlags2 & HAS_TRANSIENT_STATE) == HAS_TRANSIENT_STATE;
+ }
+
+ /**
+ * Set whether this view is currently tracking transient state that the
+ * framework should attempt to preserve when possible.
+ *
+ * @param hasTransientState true if this view has transient state
+ *
+ * @hide
+ */
+ public void setHasTransientState(boolean hasTransientState) {
+ if (hasTransientState() == hasTransientState) return;
+
+ mPrivateFlags2 = (mPrivateFlags2 & ~HAS_TRANSIENT_STATE) |
+ (hasTransientState ? HAS_TRANSIENT_STATE : 0);
+ if (mParent != null) {
+ try {
+ mParent.childHasTransientStateChanged(this, hasTransientState);
+ } catch (AbstractMethodError e) {
+ Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() +
+ " does not fully implement ViewParent", e);
+ }
+ }
+ }
+
+ /**
* If this view doesn't do any drawing on its own, set this flag to
* allow further optimizations. By default, this flag is not set on
* View, but could be set on some View subclasses such as ViewGroup.
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index f2935e8..ac46d4e 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -77,6 +77,7 @@ import java.util.HashSet;
* @attr ref android.R.styleable#ViewGroup_animateLayoutChanges
*/
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
+ private static final String TAG = "ViewGroup";
private static final boolean DBG = false;
@@ -375,6 +376,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
@ViewDebug.ExportedProperty(category = "drawing")
boolean mDrawLayers = true;
+ // Indicates how many of this container's child subtrees contain transient state
+ @ViewDebug.ExportedProperty(category = "layout")
+ private int mChildCountWithTransientState = 0;
+
public ViewGroup(Context context) {
super(context);
initViewGroup();
@@ -653,6 +658,38 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
/**
+ * Called when a child view has changed whether or not it is tracking transient state.
+ *
+ * @hide
+ */
+ public void childHasTransientStateChanged(View child, boolean childHasTransientState) {
+ final boolean oldHasTransientState = hasTransientState();
+ if (childHasTransientState) {
+ mChildCountWithTransientState++;
+ } else {
+ mChildCountWithTransientState--;
+ }
+
+ final boolean newHasTransientState = hasTransientState();
+ if (mParent != null && oldHasTransientState != newHasTransientState) {
+ try {
+ mParent.childHasTransientStateChanged(this, newHasTransientState);
+ } catch (AbstractMethodError e) {
+ Log.e(TAG, mParent.getClass().getSimpleName() +
+ " does not fully implement ViewParent", e);
+ }
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public boolean hasTransientState() {
+ return mChildCountWithTransientState > 0 || super.hasTransientState();
+ }
+
+ /**
* {@inheritDoc}
*/
@Override
@@ -3099,6 +3136,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
if ((child.mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE) {
mGroupFlags |= FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE;
}
+
+ if (child.hasTransientState()) {
+ childHasTransientStateChanged(child, true);
+ }
}
private void addInArray(View child, int index) {
@@ -3295,6 +3336,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
view.dispatchDetachedFromWindow();
}
+ if (view.hasTransientState()) {
+ childHasTransientStateChanged(view, false);
+ }
+
onViewRemoved(view);
needGlobalAttributesUpdate(false);
@@ -3366,6 +3411,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
view.dispatchDetachedFromWindow();
}
+ if (view.hasTransientState()) {
+ childHasTransientStateChanged(view, false);
+ }
+
needGlobalAttributesUpdate(false);
onViewRemoved(view);
@@ -3431,6 +3480,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
view.dispatchDetachedFromWindow();
}
+ if (view.hasTransientState()) {
+ childHasTransientStateChanged(view, false);
+ }
+
onViewRemoved(view);
view.mParent = null;
@@ -3471,6 +3524,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
child.dispatchDetachedFromWindow();
}
+ if (child.hasTransientState()) {
+ childHasTransientStateChanged(child, false);
+ }
+
onViewRemoved(child);
}
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index 873d4bb..8395f1b 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -261,4 +261,14 @@ public interface ViewParent {
* @return True if the event was sent.
*/
public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event);
+
+ /**
+ * Called when a child view now has or no longer is tracking transient state.
+ *
+ * @param child Child view whose state has changed
+ * @param hasTransientState true if this child has transient state
+ *
+ * @hide
+ */
+ public void childHasTransientStateChanged(View child, boolean hasTransientState);
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index d41d168..84ce2a4 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -4144,6 +4144,10 @@ public final class ViewRootImpl implements ViewParent,
return scrollToRectOrFocus(rectangle, immediate);
}
+ public void childHasTransientStateChanged(View child, boolean hasTransientState) {
+ // Do nothing.
+ }
+
class TakenSurfaceHolder extends BaseSurfaceHolder {
@Override
public boolean onAllowLockCanvas() {
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index e94b1cb..e7bc1e1 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -36,6 +36,7 @@ import android.text.TextWatcher;
import android.util.AttributeSet;
import android.util.Log;
import android.util.LongSparseArray;
+import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.StateSet;
import android.view.ActionMode;
@@ -87,6 +88,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
ViewTreeObserver.OnTouchModeChangeListener,
RemoteViewsAdapter.RemoteAdapterConnectionCallback {
+ private static final String TAG = "AbsListView";
+
/**
* Disables the transcript mode.
*
@@ -263,6 +266,11 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
private RemoteViewsAdapter mRemoteAdapter;
/**
+ * If mAdapter != null, whenever this is true the adapter has stable IDs.
+ */
+ boolean mAdapterHasStableIds;
+
+ /**
* This flag indicates the a full notify is required when the RemoteViewsAdapter connects
*/
private boolean mDeferNotifyDataSetChanged = false;
@@ -812,7 +820,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
@Override
public void setAdapter(ListAdapter adapter) {
if (adapter != null) {
- if (mChoiceMode != CHOICE_MODE_NONE && mAdapter.hasStableIds() &&
+ mAdapterHasStableIds = mAdapter.hasStableIds();
+ if (mChoiceMode != CHOICE_MODE_NONE && mAdapterHasStableIds &&
mCheckedIdStates == null) {
mCheckedIdStates = new LongSparseArray<Integer>();
}
@@ -2011,6 +2020,11 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
isScrap[0] = false;
View scrapView;
+ scrapView = mRecycler.getTransientStateView(position);
+ if (scrapView != null) {
+ return scrapView;
+ }
+
scrapView = mRecycler.getScrapView(position);
View child;
@@ -2021,6 +2035,13 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
child = mAdapter.getView(position, scrapView, this);
+ if (mAdapterHasStableIds) {
+ LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ if (lp == null) {
+ lp = (LayoutParams) generateDefaultLayoutParams();
+ }
+ lp.itemId = mAdapter.getItemId(position);
+ }
if (ViewDebug.TRACE_RECYCLER) {
ViewDebug.trace(child, ViewDebug.RecyclerTraceType.BIND_VIEW,
@@ -4543,7 +4564,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
if (count > 0) {
detachViewsFromParent(start, count);
+ mRecycler.removeSkippedScrap();
}
+
offsetChildrenTopAndBottom(incrementalDeltaY);
if (down) {
@@ -4853,6 +4876,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
confirmCheckedPositionsById();
}
+ // TODO: In the future we can recycle these views based on stable ID instead.
+ mRecycler.clearTransientStateViews();
+
if (count > 0) {
int newPos;
int selectablePos;
@@ -5735,6 +5761,11 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
*/
int scrappedFromPosition;
+ /**
+ * The ID the view represents
+ */
+ long itemId = -1;
+
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
}
@@ -5807,6 +5838,10 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
private ArrayList<View> mCurrentScrap;
+ private ArrayList<View> mSkippedScrap;
+
+ private SparseArray<View> mTransientStateViews;
+
public void setViewTypeCount(int viewTypeCount) {
if (viewTypeCount < 1) {
throw new IllegalArgumentException("Can't have a viewTypeCount < 1");
@@ -5838,6 +5873,12 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
}
}
+ if (mTransientStateViews != null) {
+ final int count = mTransientStateViews.size();
+ for (int i = 0; i < count; i++) {
+ mTransientStateViews.valueAt(i).forceLayout();
+ }
+ }
}
public boolean shouldRecycleViewType(int viewType) {
@@ -5864,6 +5905,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
}
}
+ if (mTransientStateViews != null) {
+ mTransientStateViews.clear();
+ }
}
/**
@@ -5910,6 +5954,28 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
return null;
}
+ View getTransientStateView(int position) {
+ if (mTransientStateViews == null) {
+ return null;
+ }
+ final int index = mTransientStateViews.indexOfKey(position);
+ if (index < 0) {
+ return null;
+ }
+ final View result = mTransientStateViews.valueAt(index);
+ mTransientStateViews.removeAt(index);
+ return result;
+ }
+
+ /**
+ * Dump any currently saved views with transient state.
+ */
+ void clearTransientStateViews() {
+ if (mTransientStateViews != null) {
+ mTransientStateViews.clear();
+ }
+ }
+
/**
* @return A view from the ScrapViews collection. These are unordered.
*/
@@ -5926,7 +5992,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
/**
- * Put a view into the ScapViews list. These views are unordered.
+ * Put a view into the ScrapViews list. These views are unordered.
*
* @param scrap The view to add
*/
@@ -5936,23 +6002,32 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
return;
}
+ lp.scrappedFromPosition = position;
+
// Don't put header or footer views or views that should be ignored
// into the scrap heap
int viewType = lp.viewType;
- if (!shouldRecycleViewType(viewType)) {
- if (viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {
- removeDetachedView(scrap, false);
+ final boolean scrapHasTransientState = scrap.hasTransientState();
+ if (!shouldRecycleViewType(viewType) || scrapHasTransientState) {
+ if (viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER || scrapHasTransientState) {
+ if (mSkippedScrap == null) {
+ mSkippedScrap = new ArrayList<View>();
+ }
+ mSkippedScrap.add(scrap);
+ }
+ if (scrapHasTransientState) {
+ if (mTransientStateViews == null) {
+ mTransientStateViews = new SparseArray<View>();
+ }
+ mTransientStateViews.put(position, scrap);
}
return;
}
- lp.scrappedFromPosition = position;
-
+ scrap.dispatchStartTemporaryDetach();
if (mViewTypeCount == 1) {
- scrap.dispatchStartTemporaryDetach();
mCurrentScrap.add(scrap);
} else {
- scrap.dispatchStartTemporaryDetach();
mScrapViews[viewType].add(scrap);
}
@@ -5962,6 +6037,20 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
/**
+ * Finish the removal of any views that skipped the scrap heap.
+ */
+ void removeSkippedScrap() {
+ if (mSkippedScrap == null) {
+ return;
+ }
+ final int count = mSkippedScrap.size();
+ for (int i = 0; i < count; i++) {
+ removeDetachedView(mSkippedScrap.get(i), false);
+ }
+ mSkippedScrap.clear();
+ }
+
+ /**
* Move all views remaining in mActiveViews to mScrapViews.
*/
void scrapActiveViews() {
@@ -5980,11 +6069,19 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
activeViews[i] = null;
- if (!shouldRecycleViewType(whichScrap)) {
+ final boolean scrapHasTransientState = victim.hasTransientState();
+ if (!shouldRecycleViewType(whichScrap) || scrapHasTransientState) {
// Do not move views that should be ignored
- if (whichScrap != ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {
+ if (whichScrap != ITEM_VIEW_TYPE_HEADER_OR_FOOTER ||
+ scrapHasTransientState) {
removeDetachedView(victim, false);
}
+ if (scrapHasTransientState) {
+ if (mTransientStateViews == null) {
+ mTransientStateViews = new SparseArray<View>();
+ }
+ mTransientStateViews.put(mFirstActivePosition + i, victim);
+ }
continue;
}
diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java
index 326587e..83aa8ba 100644
--- a/core/java/android/widget/EdgeEffect.java
+++ b/core/java/android/widget/EdgeEffect.java
@@ -56,7 +56,7 @@ public class EdgeEffect {
// Time it will take in ms for a pulled glow to decay to partial strength before release
private static final int PULL_DECAY_TIME = 1000;
- private static final float MAX_ALPHA = 0.8f;
+ private static final float MAX_ALPHA = 1.f;
private static final float HELD_EDGE_ALPHA = 0.7f;
private static final float HELD_EDGE_SCALE_Y = 0.5f;
private static final float HELD_GLOW_ALPHA = 0.5f;
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index be2df8e..6bc5a15 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -1205,6 +1205,7 @@ public class GridView extends AbsListView {
// Clear out old views
//removeAllViewsInLayout();
detachAllViewsFromParent();
+ recycleBin.removeSkippedScrap();
switch (mLayoutMode) {
case LAYOUT_SET_SELECTION:
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 67fd059..46c2c07 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -1591,6 +1591,7 @@ public class ListView extends AbsListView {
// Clear out old views
detachAllViewsFromParent();
+ recycleBin.removeSkippedScrap();
switch (mLayoutMode) {
case LAYOUT_SET_SELECTION: