Added overscroll headers and footers to ListView.
These let developers set a drawable for the list header and footer
to be drawn above and below list content.
Change-Id: Ideddec854cb0bc11f83efb3c000c217844be82c7
diff --git a/api/current.xml b/api/current.xml
index a20997b..d792a41 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -5971,6 +5971,28 @@
visibility="public"
>
</field>
+<field name="overscrollFooter"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843456"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="overscrollHeader"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843455"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="overscrollMode"
type="int"
transient="false"
@@ -204557,6 +204579,28 @@
visibility="public"
>
</method>
+<method name="getOverscrollFooter"
+ return="android.graphics.drawable.Drawable"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getOverscrollHeader"
+ return="android.graphics.drawable.Drawable"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="isItemChecked"
return="boolean"
abstract="false"
@@ -204702,6 +204746,32 @@
<parameter name="itemsCanFocus" type="boolean">
</parameter>
</method>
+<method name="setOverscrollFooter"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="footer" type="android.graphics.drawable.Drawable">
+</parameter>
+</method>
+<method name="setOverscrollHeader"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="header" type="android.graphics.drawable.Drawable">
+</parameter>
+</method>
<method name="setSelection"
return="void"
abstract="false"
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index d4552e3..0594844 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -117,6 +117,9 @@
Drawable mDivider;
int mDividerHeight;
+
+ Drawable mOverscrollHeader;
+ Drawable mOverscrollFooter;
private boolean mIsCacheColorOpaque;
private boolean mDividerIsOpaque;
@@ -171,6 +174,16 @@
// If a divider is specified use its intrinsic height for divider height
setDivider(d);
}
+
+ final Drawable osHeader = a.getDrawable(com.android.internal.R.styleable.ListView_overscrollHeader);
+ if (osHeader != null) {
+ setOverscrollHeader(osHeader);
+ }
+
+ final Drawable osFooter = a.getDrawable(com.android.internal.R.styleable.ListView_overscrollFooter);
+ if (osFooter != null) {
+ setOverscrollFooter(osFooter);
+ }
// Use the height specified, zero being the default
final int dividerHeight = a.getDimensionPixelSize(
@@ -2934,12 +2947,51 @@
super.setCacheColorHint(color);
}
+ void drawOverscrollHeader(Canvas canvas, Drawable drawable, Rect bounds) {
+ final int height = drawable.getMinimumHeight();
+
+ canvas.save();
+ canvas.clipRect(bounds);
+
+ final int span = bounds.bottom - bounds.top;
+ if (span < height) {
+ bounds.top = bounds.bottom - height;
+ }
+
+ drawable.setBounds(bounds);
+ drawable.draw(canvas);
+
+ canvas.restore();
+ }
+
+ void drawOverscrollFooter(Canvas canvas, Drawable drawable, Rect bounds) {
+ final int height = drawable.getMinimumHeight();
+
+ canvas.save();
+ canvas.clipRect(bounds);
+
+ final int span = bounds.bottom - bounds.top;
+ if (span < height) {
+ bounds.bottom = bounds.top + height;
+ }
+
+ drawable.setBounds(bounds);
+ drawable.draw(canvas);
+
+ canvas.restore();
+ }
+
@Override
protected void dispatchDraw(Canvas canvas) {
// Draw the dividers
final int dividerHeight = mDividerHeight;
+ final Drawable overscrollHeader = mOverscrollHeader;
+ final Drawable overscrollFooter = mOverscrollFooter;
+ final boolean drawOverscrollHeader = overscrollHeader != null;
+ final boolean drawOverscrollFooter = overscrollFooter != null;
+ final boolean drawDividers = dividerHeight > 0 && mDivider != null;
- if (dividerHeight > 0 && mDivider != null) {
+ if (drawDividers || drawOverscrollHeader || drawOverscrollFooter) {
// Only modify the top and bottom in the loop, we set the left and right here
final Rect bounds = mTempRect;
bounds.left = mPaddingLeft;
@@ -2947,7 +2999,8 @@
final int count = getChildCount();
final int headerCount = mHeaderViewInfos.size();
- final int footerLimit = mItemCount - mFooterViewInfos.size() - 1;
+ final int itemCount = mItemCount;
+ final int footerLimit = itemCount - mFooterViewInfos.size() - 1;
final boolean headerDividers = mHeaderDividersEnabled;
final boolean footerDividers = mFooterDividersEnabled;
final int first = mFirstPosition;
@@ -2957,7 +3010,7 @@
// fill a rect where the dividers would be for non-selectable items
// If the list is opaque and the background is also opaque, we don't
// need to draw anything since the background will do it for us
- final boolean fillForMissingDividers = isOpaque() && !super.isOpaque();
+ final boolean fillForMissingDividers = drawDividers && isOpaque() && !super.isOpaque();
if (fillForMissingDividers && mDividerPaint == null && mIsCacheColorOpaque) {
mDividerPaint = new Paint();
@@ -2965,15 +3018,22 @@
}
final Paint paint = mDividerPaint;
+ final int listBottom = mBottom - mTop - mListPadding.bottom + mScrollY;
if (!mStackFromBottom) {
- int bottom;
- int listBottom = mBottom - mTop - mListPadding.bottom + mScrollY;
+ int bottom = 0;
- // Draw top divider for overscroll
- if (count > 0 && mScrollY < 0) {
- bounds.bottom = 0;
- bounds.top = -dividerHeight;
- drawDivider(canvas, bounds, -1);
+ // Draw top divider or header for overscroll
+ final int scrollY = mScrollY;
+ if (count > 0 && scrollY < 0) {
+ if (drawOverscrollHeader) {
+ bounds.bottom = 0;
+ bounds.top = scrollY;
+ drawOverscrollHeader(canvas, overscrollHeader, bounds);
+ } else if (drawDividers) {
+ bounds.bottom = 0;
+ bounds.top = -dividerHeight;
+ drawDivider(canvas, bounds, -1);
+ }
}
for (int i = 0; i < count; i++) {
@@ -2982,7 +3042,8 @@
View child = getChildAt(i);
bottom = child.getBottom();
// Don't draw dividers next to items that are not enabled
- if (bottom < listBottom) {
+ if (drawDividers &&
+ (bottom < listBottom && !(drawOverscrollFooter && i == count - 1))) {
if ((areAllItemsSelectable ||
(adapter.isEnabled(first + i) && (i == count - 1 ||
adapter.isEnabled(first + i + 1))))) {
@@ -2997,17 +3058,34 @@
}
}
}
+
+ final int overFooterBottom = mBottom + mScrollY;
+ if (drawOverscrollFooter && first + count == itemCount &&
+ overFooterBottom > bottom) {
+ bounds.top = bottom;
+ bounds.bottom = overFooterBottom;
+ drawOverscrollFooter(canvas, overscrollFooter, bounds);
+ }
} else {
int top;
int listTop = mListPadding.top;
- for (int i = 0; i < count; i++) {
+ final int scrollY = mScrollY;
+
+ if (count > 0 && drawOverscrollHeader) {
+ bounds.top = scrollY;
+ bounds.bottom = getChildAt(0).getTop();
+ drawOverscrollHeader(canvas, overscrollHeader, bounds);
+ }
+
+ final int start = drawOverscrollHeader ? 1 : 0;
+ for (int i = start; i < count; i++) {
if ((headerDividers || first + i >= headerCount) &&
(footerDividers || first + i < footerLimit)) {
View child = getChildAt(i);
top = child.getTop();
// Don't draw dividers next to items that are not enabled
- if (top > listTop) {
+ if (drawDividers && top > listTop) {
if ((areAllItemsSelectable ||
(adapter.isEnabled(first + i) && (i == count - 1 ||
adapter.isEnabled(first + i + 1))))) {
@@ -3026,6 +3104,19 @@
}
}
}
+
+ if (count > 0 && scrollY > 0) {
+ if (drawOverscrollFooter) {
+ final int absListBottom = mBottom;
+ bounds.top = absListBottom;
+ bounds.bottom = absListBottom + scrollY;
+ drawOverscrollFooter(canvas, overscrollFooter, bounds);
+ } else if (drawDividers) {
+ bounds.top = listBottom;
+ bounds.bottom = listBottom + dividerHeight;
+ drawDivider(canvas, bounds, -1);
+ }
+ }
}
}
@@ -3132,6 +3223,45 @@
mFooterDividersEnabled = footerDividersEnabled;
invalidate();
}
+
+ /**
+ * Sets the drawable that will be drawn above all other list content.
+ * This area can become visible when the user overscrolls the list.
+ *
+ * @param header The drawable to use
+ */
+ public void setOverscrollHeader(Drawable header) {
+ mOverscrollHeader = header;
+ if (mScrollY < 0) {
+ invalidate();
+ }
+ }
+
+ /**
+ * @return The drawable that will be drawn above all other list content
+ */
+ public Drawable getOverscrollHeader() {
+ return mOverscrollHeader;
+ }
+
+ /**
+ * Sets the drawable that will be drawn below all other list content.
+ * This area can become visible when the user overscrolls the list,
+ * or when the list's content does not fully fill the container area.
+ *
+ * @param footer The drawable to use
+ */
+ public void setOverscrollFooter(Drawable footer) {
+ mOverscrollFooter = footer;
+ invalidate();
+ }
+
+ /**
+ * @return The drawable that will be drawn below all other list content
+ */
+ public Drawable getOverscrollFooter() {
+ return mOverscrollFooter;
+ }
@Override
protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 1ced121..f5e2f1d 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1747,6 +1747,10 @@
<!-- When set to false, the ListView will not draw the divider before each footer view.
The default value is true. -->
<attr name="footerDividersEnabled" format="boolean" />
+ <!-- Drawable to draw above list content. -->
+ <attr name="overscrollHeader" format="reference" />
+ <!-- Drawable to draw below list content. -->
+ <attr name="overscrollFooter" format="reference" />
</declare-styleable>
<declare-styleable name="MenuView">
<!-- Default appearance of menu item text. -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 36a8f5b..8c00884 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1235,6 +1235,8 @@
<public type="attr" name="stripLeft" id="0x010102bc" />
<public type="attr" name="stripRight" id="0x010102bd" />
<public type="attr" name="stripEnabled" id="0x010102be" />
+ <public type="attr" name="overscrollHeader" id="0x010102bf" />
+ <public type="attr" name="overscrollFooter" id="0x010102c0" />
<public type="anim" name="cycle_interpolator" id="0x010a000c" />