Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bfb5d4b9 authored by Adam Powell's avatar Adam Powell
Browse files

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
parent 232c9d5b
Loading
Loading
Loading
Loading
+70 −0
Original line number Diff line number Diff line
@@ -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"
+143 −13
Original line number Diff line number Diff line
@@ -118,6 +118,9 @@ public class ListView extends AbsListView {
    Drawable mDivider;
    int mDividerHeight;
    
    Drawable mOverscrollHeader;
    Drawable mOverscrollFooter;

    private boolean mIsCacheColorOpaque;
    private boolean mDividerIsOpaque;
    private boolean mClipDivider;
@@ -172,6 +175,16 @@ public class ListView extends AbsListView {
            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(
                com.android.internal.R.styleable.ListView_dividerHeight, 0);
@@ -2934,12 +2947,51 @@ public class ListView extends AbsListView {
        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 @@ public class ListView extends AbsListView {

            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 @@ public class ListView extends AbsListView {
            // 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,16 +3018,23 @@ public class ListView extends AbsListView {
            }
            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) {
                // 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++) {
                    if ((headerDividers || first + i >= headerCount) &&
@@ -2982,7 +3042,8 @@ public class ListView extends AbsListView {
                        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 @@ public class ListView extends AbsListView {
                        }
                    }
                }
                
                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 @@ public class ListView extends AbsListView {
                        }
                    }
                }
                
                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);
                    }
                }
            }
        }

@@ -3133,6 +3224,45 @@ public class ListView extends AbsListView {
        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) {
        super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
+4 −0
Original line number Diff line number Diff line
@@ -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. -->
+2 −0
Original line number Diff line number Diff line
@@ -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" />