public class

HorizontalGridView

extends BaseGridView

 java.lang.Object

↳ViewGroup

androidx.recyclerview.widget.RecyclerView

androidx.leanback.widget.BaseGridView

↳androidx.leanback.widget.HorizontalGridView

Gradle dependencies

compile group: 'androidx.leanback', name: 'leanback-grid', version: '1.0.0-alpha03'

  • groupId: androidx.leanback
  • artifactId: leanback-grid
  • version: 1.0.0-alpha03

Artifact androidx.leanback:leanback-grid:1.0.0-alpha03 it located at Google repository (https://maven.google.com/)

Androidx class mapping:

androidx.leanback.widget.HorizontalGridView android.support.v17.leanback.widget.HorizontalGridView

Overview

A android.view.ViewGroup that shows items in a horizontal scrolling list. The items come from the associated with this view.

can optionally implement FacetProviderAdapter which provides FacetProvider for a given view type; can also implement FacetProvider. Facet from ViewHolder has a higher priority than the one from FacetProviderAdapter associated with viewType. Supported optional facets are:

  1. ItemAlignmentFacet When this facet is provided by ViewHolder or FacetProviderAdapter, it will override the item alignment settings set on HorizontalGridView. This facet also allows multiple alignment positions within one ViewHolder.

Summary

Fields
from BaseGridViewFOCUS_SCROLL_ALIGNED, FOCUS_SCROLL_ITEM, FOCUS_SCROLL_PAGE, ITEM_ALIGN_OFFSET_PERCENT_DISABLED, SAVE_ALL_CHILD, SAVE_LIMITED_CHILD, SAVE_NO_CHILD, SAVE_ON_SCREEN_CHILD, WINDOW_ALIGN_BOTH_EDGE, WINDOW_ALIGN_HIGH_EDGE, WINDOW_ALIGN_LOW_EDGE, WINDOW_ALIGN_NO_EDGE, WINDOW_ALIGN_OFFSET_PERCENT_DISABLED
from RecyclerViewHORIZONTAL, INVALID_TYPE, NO_ID, NO_POSITION, SCROLL_STATE_DRAGGING, SCROLL_STATE_IDLE, SCROLL_STATE_SETTLING, TOUCH_SLOP_DEFAULT, TOUCH_SLOP_PAGING, UNDEFINED_DURATION, VERTICAL
Constructors
publicHorizontalGridView(Context context)

publicHorizontalGridView(Context context, AttributeSet attrs)

publicHorizontalGridView(Context context, AttributeSet attrs, int defStyle)

Methods
public voiddraw(Canvas canvas)

public final booleangetFadingLeftEdge()

Returns true if left edge fading is enabled.

public final intgetFadingLeftEdgeLength()

Returns the left edge fading length in pixels.

public final intgetFadingLeftEdgeOffset()

Returns the distance in pixels between fading start position and left padding edge.

public final booleangetFadingRightEdge()

Returns true if fading right edge is enabled.

public final intgetFadingRightEdgeLength()

Returns the right edge fading length in pixels.

public final intgetFadingRightEdgeOffset()

Sets the distance in pixels between fading start position and right padding edge.

protected voidinitAttributes(Context context, AttributeSet attrs)

public final voidsetFadingLeftEdge(boolean fading)

Sets the fade out left edge to transparent.

public final voidsetFadingLeftEdgeLength(int fadeLength)

Sets the left edge fading length in pixels.

public final voidsetFadingLeftEdgeOffset(int fadeOffset)

Sets the distance in pixels between fading start position and left padding edge.

public final voidsetFadingRightEdge(boolean fading)

Sets the fade out right edge to transparent.

public final voidsetFadingRightEdgeLength(int fadeLength)

Sets the right edge fading length in pixels.

public final voidsetFadingRightEdgeOffset(int fadeOffset)

Returns the distance in pixels between fading start position and right padding edge.

public voidsetNumRows(int numRows)

Sets the number of rows.

from BaseGridViewaddOnChildViewHolderSelectedListener, addOnLayoutCompletedListener, animateIn, animateOut, dispatchGenericFocusedEvent, dispatchKeyEvent, dispatchTouchEvent, focusSearch, getChildDrawingOrder, getExtraLayoutSpace, getFocusScrollStrategy, getHorizontalMargin, getHorizontalSpacing, getInitialPrefetchItemCount, getItemAlignmentOffset, getItemAlignmentOffsetPercent, getItemAlignmentViewId, getOnUnhandledKeyListener, getSaveChildrenLimitNumber, getSaveChildrenPolicy, getSelectedPosition, getSelectedSubPosition, getSmoothScrollByBehavior, getSmoothScrollMaxPendingMoves, getSmoothScrollSpeedFactor, getVerticalMargin, getVerticalSpacing, getViewSelectedOffsets, getWindowAlignment, getWindowAlignmentOffset, getWindowAlignmentOffsetPercent, hasOverlappingRendering, hasPreviousViewInSameRow, isChildLayoutAnimated, isFocusDrawingOrderEnabled, isFocusSearchDisabled, isItemAlignmentOffsetWithPadding, isScrollEnabled, isWindowAlignmentPreferKeyLineOverHighEdge, isWindowAlignmentPreferKeyLineOverLowEdge, onFocusChanged, onRequestFocusInDescendants, onRtlPropertiesChanged, removeOnChildViewHolderSelectedListener, removeOnLayoutCompletedListener, removeView, removeViewAt, scrollToPosition, setAnimateChildLayout, setChildrenVisibility, setExtraLayoutSpace, setFocusDrawingOrderEnabled, setFocusScrollStrategy, setFocusSearchDisabled, setGravity, setHasOverlappingRendering, setHorizontalMargin, setHorizontalSpacing, setInitialPrefetchItemCount, setItemAlignmentOffset, setItemAlignmentOffsetPercent, setItemAlignmentOffsetWithPadding, setItemAlignmentViewId, setItemMargin, setItemSpacing, setLayoutEnabled, setLayoutManager, setOnChildLaidOutListener, setOnChildSelectedListener, setOnChildViewHolderSelectedListener, setOnKeyInterceptListener, setOnMotionInterceptListener, setOnTouchInterceptListener, setOnUnhandledKeyListener, setPruneChild, setSaveChildrenLimitNumber, setSaveChildrenPolicy, setScrollEnabled, setSelectedPosition, setSelectedPosition, setSelectedPosition, setSelectedPositionSmooth, setSelectedPositionSmooth, setSelectedPositionSmoothWithSub, setSelectedPositionWithSub, setSelectedPositionWithSub, setSmoothScrollByBehavior, setSmoothScrollMaxPendingMoves, setSmoothScrollSpeedFactor, setVerticalMargin, setVerticalSpacing, setWindowAlignment, setWindowAlignmentOffset, setWindowAlignmentOffsetPercent, setWindowAlignmentPreferKeyLineOverHighEdge, setWindowAlignmentPreferKeyLineOverLowEdge, smoothScrollBy, smoothScrollBy, smoothScrollToPosition
from RecyclerViewaddFocusables, addItemDecoration, addItemDecoration, addOnChildAttachStateChangeListener, addOnItemTouchListener, addOnScrollListener, addRecyclerListener, checkLayoutParams, clearOnChildAttachStateChangeListeners, clearOnScrollListeners, computeHorizontalScrollExtent, computeHorizontalScrollOffset, computeHorizontalScrollRange, computeVerticalScrollExtent, computeVerticalScrollOffset, computeVerticalScrollRange, dispatchNestedFling, dispatchNestedPreFling, dispatchNestedPreScroll, dispatchNestedPreScroll, dispatchNestedScroll, dispatchNestedScroll, dispatchNestedScroll, dispatchPopulateAccessibilityEvent, dispatchRestoreInstanceState, dispatchSaveInstanceState, drawChild, findChildViewUnder, findContainingItemView, findContainingViewHolder, findViewHolderForAdapterPosition, findViewHolderForItemId, findViewHolderForLayoutPosition, findViewHolderForPosition, fling, focusSearch, generateDefaultLayoutParams, generateLayoutParams, generateLayoutParams, getAccessibilityClassName, getAdapter, getBaseline, getChildAdapterPosition, getChildItemId, getChildLayoutPosition, getChildPosition, getChildViewHolder, getClipToPadding, getCompatAccessibilityDelegate, getDecoratedBoundsWithMargins, getEdgeEffectFactory, getItemAnimator, getItemDecorationAt, getItemDecorationCount, getLayoutManager, getMaxFlingVelocity, getMinFlingVelocity, getOnFlingListener, getPreserveFocusAfterLayout, getRecycledViewPool, getScrollState, hasFixedSize, hasNestedScrollingParent, hasNestedScrollingParent, hasPendingAdapterUpdates, invalidateItemDecorations, isAnimating, isAttachedToWindow, isComputingLayout, isLayoutFrozen, isLayoutSuppressed, isNestedScrollingEnabled, nestedScrollBy, offsetChildrenHorizontal, offsetChildrenVertical, onAttachedToWindow, onChildAttachedToWindow, onChildDetachedFromWindow, onDetachedFromWindow, onDraw, onGenericMotionEvent, onInterceptTouchEvent, onLayout, onMeasure, onRestoreInstanceState, onSaveInstanceState, onScrolled, onScrollStateChanged, onSizeChanged, onTouchEvent, removeDetachedView, removeItemDecoration, removeItemDecorationAt, removeOnChildAttachStateChangeListener, removeOnItemTouchListener, removeOnScrollListener, removeRecyclerListener, requestChildFocus, requestChildRectangleOnScreen, requestDisallowInterceptTouchEvent, requestLayout, scrollBy, scrollTo, sendAccessibilityEventUnchecked, setAccessibilityDelegateCompat, setAdapter, setChildDrawingOrderCallback, setClipToPadding, setDebugAssertionsEnabled, setEdgeEffectFactory, setHasFixedSize, setItemAnimator, setItemViewCacheSize, setLayoutFrozen, setLayoutTransition, setNestedScrollingEnabled, setOnFlingListener, setOnScrollListener, setPreserveFocusAfterLayout, setRecycledViewPool, setRecyclerListener, setScrollingTouchSlop, setVerboseLoggingEnabled, setViewCacheExtension, smoothScrollBy, startNestedScroll, startNestedScroll, stopNestedScroll, stopNestedScroll, stopScroll, suppressLayout, swapAdapter
from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Constructors

public HorizontalGridView(Context context)

public HorizontalGridView(Context context, AttributeSet attrs)

public HorizontalGridView(Context context, AttributeSet attrs, int defStyle)

Methods

protected void initAttributes(Context context, AttributeSet attrs)

public void setNumRows(int numRows)

Sets the number of rows. Defaults to one.

public final void setFadingLeftEdge(boolean fading)

Sets the fade out left edge to transparent. Note turn on fading edge is very expensive that you should turn off when HorizontalGridView is scrolling.

public final boolean getFadingLeftEdge()

Returns true if left edge fading is enabled.

public final void setFadingLeftEdgeLength(int fadeLength)

Sets the left edge fading length in pixels.

public final int getFadingLeftEdgeLength()

Returns the left edge fading length in pixels.

public final void setFadingLeftEdgeOffset(int fadeOffset)

Sets the distance in pixels between fading start position and left padding edge. The fading start position is positive when start position is inside left padding area. Default value is 0, means that the fading starts from left padding edge.

public final int getFadingLeftEdgeOffset()

Returns the distance in pixels between fading start position and left padding edge. The fading start position is positive when start position is inside left padding area. Default value is 0, means that the fading starts from left padding edge.

public final void setFadingRightEdge(boolean fading)

Sets the fade out right edge to transparent. Note turn on fading edge is very expensive that you should turn off when HorizontalGridView is scrolling.

public final boolean getFadingRightEdge()

Returns true if fading right edge is enabled.

public final void setFadingRightEdgeLength(int fadeLength)

Sets the right edge fading length in pixels.

public final int getFadingRightEdgeLength()

Returns the right edge fading length in pixels.

public final void setFadingRightEdgeOffset(int fadeOffset)

Returns the distance in pixels between fading start position and right padding edge. The fading start position is positive when start position is inside right padding area. Default value is 0, means that the fading starts from right padding edge.

public final int getFadingRightEdgeOffset()

Sets the distance in pixels between fading start position and right padding edge. The fading start position is positive when start position is inside right padding area. Default value is 0, means that the fading starts from right padding edge.

public void draw(Canvas canvas)

Source

/*
 * Copyright 2021 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 androidx.leanback.widget;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.view.ViewCompat;
import androidx.recyclerview.widget.RecyclerView;

/**
 * A {@link android.view.ViewGroup} that shows items in a horizontal scrolling list. The items
 * come from
 * the {@link RecyclerView.Adapter} associated with this view.
 * <p>
 * {@link RecyclerView.Adapter} can optionally implement {@link FacetProviderAdapter} which
 * provides {@link FacetProvider} for a given view type;  {@link RecyclerView.ViewHolder}
 * can also implement {@link FacetProvider}.  Facet from ViewHolder
 * has a higher priority than the one from FacetProviderAdapter associated with viewType.
 * Supported optional facets are:
 * <ol>
 * <li> {@link ItemAlignmentFacet}
 * When this facet is provided by ViewHolder or FacetProviderAdapter,  it will
 * override the item alignment settings set on HorizontalGridView.  This facet also allows multiple
 * alignment positions within one ViewHolder.
 * </li>
 * </ol>
 */
public class HorizontalGridView extends BaseGridView {

    private boolean mFadingLowEdge;
    private boolean mFadingHighEdge;

    private Paint mTempPaint = new Paint();
    private Bitmap mTempBitmapLow;
    private LinearGradient mLowFadeShader;
    private int mLowFadeShaderLength;
    private int mLowFadeShaderOffset;
    private Bitmap mTempBitmapHigh;
    private LinearGradient mHighFadeShader;
    private int mHighFadeShaderLength;
    private int mHighFadeShaderOffset;
    private final Rect mTempRect = new Rect();

    public HorizontalGridView(@NonNull Context context) {
        this(context, null);
    }

    public HorizontalGridView(@NonNull Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public HorizontalGridView(@NonNull Context context, @Nullable AttributeSet attrs,
            int defStyle) {
        super(context, attrs, defStyle);
        mLayoutManager.setOrientation(RecyclerView.HORIZONTAL);
        initAttributes(context, attrs);
    }

    @SuppressLint("CustomViewStyleable")
    protected void initAttributes(@NonNull Context context, @Nullable AttributeSet attrs) {
        initBaseGridViewAttributes(context, attrs);
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.lbHorizontalGridView);
        ViewCompat.saveAttributeDataForStyleable(this,
                context, R.styleable.lbHorizontalGridView, attrs, a, 0, 0);
        setRowHeight(a);
        setNumRows(a.getInt(R.styleable.lbHorizontalGridView_numberOfRows, 1));
        a.recycle();
        updateLayerType();
        mTempPaint = new Paint();
        mTempPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
    }

    void setRowHeight(TypedArray array) {
        TypedValue typedValue = array.peekValue(R.styleable.lbHorizontalGridView_rowHeight);
        if (typedValue != null) {
            int size = array.getLayoutDimension(R.styleable.lbHorizontalGridView_rowHeight, 0);
            setRowHeight(size);
        }
    }

    /**
     * Sets the number of rows.  Defaults to one.
     */
    public void setNumRows(int numRows) {
        mLayoutManager.setNumRows(numRows);
        requestLayout();
    }

    /**
     * Sets the row height.
     *
     * @param height May be {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT WRAP_CONTENT},
     *               or a size in pixels. If zero, row height will be fixed based on number of
     *               rows and view height.
     */
    public void setRowHeight(int height) {
        mLayoutManager.setRowHeight(height);
        requestLayout();
    }

    /**
     * Sets the fade out left edge to transparent.   Note turn on fading edge is very expensive
     * that you should turn off when HorizontalGridView is scrolling.
     */
    public final void setFadingLeftEdge(boolean fading) {
        if (mFadingLowEdge != fading) {
            mFadingLowEdge = fading;
            if (!mFadingLowEdge) {
                mTempBitmapLow = null;
            }
            invalidate();
            updateLayerType();
        }
    }

    /**
     * Returns true if left edge fading is enabled.
     */
    @SuppressLint("GetterSetterNames")
    public final boolean getFadingLeftEdge() {
        return mFadingLowEdge;
    }

    /**
     * Sets the left edge fading length in pixels.
     */
    public final void setFadingLeftEdgeLength(int fadeLength) {
        if (mLowFadeShaderLength != fadeLength) {
            mLowFadeShaderLength = fadeLength;
            if (mLowFadeShaderLength != 0) {
                mLowFadeShader = new LinearGradient(0, 0, mLowFadeShaderLength, 0,
                        Color.TRANSPARENT, Color.BLACK, Shader.TileMode.CLAMP);
            } else {
                mLowFadeShader = null;
            }
            invalidate();
        }
    }

    /**
     * Returns the left edge fading length in pixels.
     */
    public final int getFadingLeftEdgeLength() {
        return mLowFadeShaderLength;
    }

    /**
     * Sets the distance in pixels between fading start position and left padding edge.
     * The fading start position is positive when start position is inside left padding
     * area.  Default value is 0, means that the fading starts from left padding edge.
     */
    public final void setFadingLeftEdgeOffset(int fadeOffset) {
        if (mLowFadeShaderOffset != fadeOffset) {
            mLowFadeShaderOffset = fadeOffset;
            invalidate();
        }
    }

    /**
     * Returns the distance in pixels between fading start position and left padding edge.
     * The fading start position is positive when start position is inside left padding
     * area.  Default value is 0, means that the fading starts from left padding edge.
     */
    public final int getFadingLeftEdgeOffset() {
        return mLowFadeShaderOffset;
    }

    /**
     * Sets the fade out right edge to transparent.   Note turn on fading edge is very expensive
     * that you should turn off when HorizontalGridView is scrolling.
     */
    public final void setFadingRightEdge(boolean fading) {
        if (mFadingHighEdge != fading) {
            mFadingHighEdge = fading;
            if (!mFadingHighEdge) {
                mTempBitmapHigh = null;
            }
            invalidate();
            updateLayerType();
        }
    }

    /**
     * Returns true if fading right edge is enabled.
     */
    @SuppressLint("GetterSetterNames")
    public final boolean getFadingRightEdge() {
        return mFadingHighEdge;
    }

    /**
     * Sets the right edge fading length in pixels.
     */
    public final void setFadingRightEdgeLength(int fadeLength) {
        if (mHighFadeShaderLength != fadeLength) {
            mHighFadeShaderLength = fadeLength;
            if (mHighFadeShaderLength != 0) {
                mHighFadeShader = new LinearGradient(0, 0, mHighFadeShaderLength, 0,
                        Color.BLACK, Color.TRANSPARENT, Shader.TileMode.CLAMP);
            } else {
                mHighFadeShader = null;
            }
            invalidate();
        }
    }

    /**
     * Returns the right edge fading length in pixels.
     */
    public final int getFadingRightEdgeLength() {
        return mHighFadeShaderLength;
    }

    /**
     * Returns the distance in pixels between fading start position and right padding edge.
     * The fading start position is positive when start position is inside right padding
     * area.  Default value is 0, means that the fading starts from right padding edge.
     */
    public final void setFadingRightEdgeOffset(int fadeOffset) {
        if (mHighFadeShaderOffset != fadeOffset) {
            mHighFadeShaderOffset = fadeOffset;
            invalidate();
        }
    }

    /**
     * Sets the distance in pixels between fading start position and right padding edge.
     * The fading start position is positive when start position is inside right padding
     * area.  Default value is 0, means that the fading starts from right padding edge.
     */
    public final int getFadingRightEdgeOffset() {
        return mHighFadeShaderOffset;
    }

    private boolean needsFadingLowEdge() {
        if (!mFadingLowEdge) {
            return false;
        }
        final int c = getChildCount();
        for (int i = 0; i < c; i++) {
            View view = getChildAt(i);
            if (mLayoutManager.getOpticalLeft(view) < getPaddingLeft() - mLowFadeShaderOffset) {
                return true;
            }
        }
        return false;
    }

    private boolean needsFadingHighEdge() {
        if (!mFadingHighEdge) {
            return false;
        }
        final int c = getChildCount();
        for (int i = c - 1; i >= 0; i--) {
            View view = getChildAt(i);
            if (mLayoutManager.getOpticalRight(view) > getWidth()
                    - getPaddingRight() + mHighFadeShaderOffset) {
                return true;
            }
        }
        return false;
    }

    private Bitmap getTempBitmapLow() {
        if (mTempBitmapLow == null
                || mTempBitmapLow.getWidth() != mLowFadeShaderLength
                || mTempBitmapLow.getHeight() != getHeight()) {
            mTempBitmapLow = Bitmap.createBitmap(mLowFadeShaderLength, getHeight(),
                    Bitmap.Config.ARGB_8888);
        }
        return mTempBitmapLow;
    }

    private Bitmap getTempBitmapHigh() {
        if (mTempBitmapHigh == null
                || mTempBitmapHigh.getWidth() != mHighFadeShaderLength
                || mTempBitmapHigh.getHeight() != getHeight()) {
            // TODO: fix logic for sharing mTempBitmapLow
            //if (mTempBitmapLow != null
            //        && mTempBitmapLow.getWidth() == mHighFadeShaderLength
            //        && mTempBitmapLow.getHeight() == getHeight()) {
            //    // share same bitmap for low edge fading and high edge fading.
            //    mTempBitmapHigh = mTempBitmapLow;
            //} else {
            mTempBitmapHigh = Bitmap.createBitmap(mHighFadeShaderLength, getHeight(),
                    Bitmap.Config.ARGB_8888);
            //}
        }
        return mTempBitmapHigh;
    }

    @Override
    public void draw(@NonNull Canvas canvas) {
        final boolean needsFadingLow = needsFadingLowEdge();
        final boolean needsFadingHigh = needsFadingHighEdge();
        if (!needsFadingLow) {
            mTempBitmapLow = null;
        }
        if (!needsFadingHigh) {
            mTempBitmapHigh = null;
        }
        if (!needsFadingLow && !needsFadingHigh) {
            super.draw(canvas);
            return;
        }

        int lowEdge =
                mFadingLowEdge ? getPaddingLeft() - mLowFadeShaderOffset - mLowFadeShaderLength : 0;
        int highEdge = mFadingHighEdge ? getWidth() - getPaddingRight()
                + mHighFadeShaderOffset + mHighFadeShaderLength : getWidth();

        // draw not-fade content
        int save = canvas.save();
        canvas.clipRect(lowEdge + (mFadingLowEdge ? mLowFadeShaderLength : 0), 0,
                highEdge - (mFadingHighEdge ? mHighFadeShaderLength : 0), getHeight());
        super.draw(canvas);
        canvas.restoreToCount(save);

        Canvas tmpCanvas = new Canvas();
        mTempRect.top = 0;
        mTempRect.bottom = getHeight();
        if (needsFadingLow && mLowFadeShaderLength > 0) {
            Bitmap tempBitmap = getTempBitmapLow();
            tempBitmap.eraseColor(Color.TRANSPARENT);
            tmpCanvas.setBitmap(tempBitmap);
            // draw original content
            int tmpSave = tmpCanvas.save();
            tmpCanvas.clipRect(0, 0, mLowFadeShaderLength, getHeight());
            tmpCanvas.translate(-lowEdge, 0);
            super.draw(tmpCanvas);
            tmpCanvas.restoreToCount(tmpSave);
            // draw fading out
            mTempPaint.setShader(mLowFadeShader);
            tmpCanvas.drawRect(0, 0, mLowFadeShaderLength, getHeight(), mTempPaint);
            // copy back to canvas
            mTempRect.left = 0;
            mTempRect.right = mLowFadeShaderLength;
            canvas.translate(lowEdge, 0);
            canvas.drawBitmap(tempBitmap, mTempRect, mTempRect, null);
            canvas.translate(-lowEdge, 0);
        }
        if (needsFadingHigh && mHighFadeShaderLength > 0) {
            Bitmap tempBitmap = getTempBitmapHigh();
            tempBitmap.eraseColor(Color.TRANSPARENT);
            tmpCanvas.setBitmap(tempBitmap);
            // draw original content
            int tmpSave = tmpCanvas.save();
            tmpCanvas.clipRect(0, 0, mHighFadeShaderLength, getHeight());
            tmpCanvas.translate(-(highEdge - mHighFadeShaderLength), 0);
            super.draw(tmpCanvas);
            tmpCanvas.restoreToCount(tmpSave);
            // draw fading out
            mTempPaint.setShader(mHighFadeShader);
            tmpCanvas.drawRect(0, 0, mHighFadeShaderLength, getHeight(), mTempPaint);
            // copy back to canvas
            mTempRect.left = 0;
            mTempRect.right = mHighFadeShaderLength;
            canvas.translate(highEdge - mHighFadeShaderLength, 0);
            canvas.drawBitmap(tempBitmap, mTempRect, mTempRect, null);
            canvas.translate(-(highEdge - mHighFadeShaderLength), 0);
        }
    }

    /**
     * Updates the layer type for this view.
     * If fading edges are needed, use a hardware layer.  This works around the problem
     * that when a child invalidates itself (for example has an animated background),
     * the parent view must also be invalidated to refresh the display list which
     * updates the the caching bitmaps used to draw the fading edges.
     */
    private void updateLayerType() {
        if (mFadingLowEdge || mFadingHighEdge) {
            setLayerType(View.LAYER_TYPE_HARDWARE, null);
            setWillNotDraw(false);
        } else {
            setLayerType(View.LAYER_TYPE_NONE, null);
            setWillNotDraw(true);
        }
    }
}