19106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn/*
29106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn * Copyright (C) 2014 The Android Open Source Project
39106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn *
49106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
59106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn * in compliance with the License. You may obtain a copy of the License at
69106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn *
79106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn * http://www.apache.org/licenses/LICENSE-2.0
89106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn *
99106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn * Unless required by applicable law or agreed to in writing, software distributed under the License
109106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
119106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn * or implied. See the License for the specific language governing permissions and limitations under
129106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn * the License.
139106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn */
149106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbournpackage android.support.v17.leanback.widget;
159106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn
169106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbournimport android.content.Context;
179106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbournimport android.content.res.TypedArray;
189240e796bc63422c28f2707840bd99c48573279bDake Guimport android.graphics.Bitmap;
199240e796bc63422c28f2707840bd99c48573279bDake Guimport android.graphics.Canvas;
209240e796bc63422c28f2707840bd99c48573279bDake Guimport android.graphics.Color;
219240e796bc63422c28f2707840bd99c48573279bDake Guimport android.graphics.LinearGradient;
229240e796bc63422c28f2707840bd99c48573279bDake Guimport android.graphics.Paint;
239240e796bc63422c28f2707840bd99c48573279bDake Guimport android.graphics.PorterDuff;
249240e796bc63422c28f2707840bd99c48573279bDake Guimport android.graphics.PorterDuffXfermode;
259240e796bc63422c28f2707840bd99c48573279bDake Guimport android.graphics.Rect;
269240e796bc63422c28f2707840bd99c48573279bDake Guimport android.graphics.Shader;
279106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbournimport android.support.v17.leanback.R;
289106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbournimport android.support.v7.widget.RecyclerView;
299106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbournimport android.util.AttributeSet;
30f272f7533fcb5aba341e9ab2f4ff0421d668a8caCraig Stoutimport android.util.TypedValue;
319240e796bc63422c28f2707840bd99c48573279bDake Guimport android.view.View;
329106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn
339106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn/**
34a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout * A {@link android.view.ViewGroup} that shows items in a horizontal scrolling list. The items come from
359106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn * the {@link RecyclerView.Adapter} associated with this view.
3608c56822b71ab0aa0b9bb03e5fd45e28f6e358b8Dake Gu * <p>
3708c56822b71ab0aa0b9bb03e5fd45e28f6e358b8Dake Gu * {@link RecyclerView.Adapter} can optionally implement {@link FacetProviderAdapter} which
3808c56822b71ab0aa0b9bb03e5fd45e28f6e358b8Dake Gu * provides {@link FacetProvider} for a given view type;  {@link RecyclerView.ViewHolder}
3908c56822b71ab0aa0b9bb03e5fd45e28f6e358b8Dake Gu * can also implement {@link FacetProvider}.  Facet from ViewHolder
4008c56822b71ab0aa0b9bb03e5fd45e28f6e358b8Dake Gu * has a higher priority than the one from FacetProiderAdapter associated with viewType.
4108c56822b71ab0aa0b9bb03e5fd45e28f6e358b8Dake Gu * Supported optional facets are:
4208c56822b71ab0aa0b9bb03e5fd45e28f6e358b8Dake Gu * <ol>
4308c56822b71ab0aa0b9bb03e5fd45e28f6e358b8Dake Gu * <li> {@link ItemAlignmentFacet}
4408c56822b71ab0aa0b9bb03e5fd45e28f6e358b8Dake Gu * When this facet is provided by ViewHolder or FacetProviderAdapter,  it will
4508c56822b71ab0aa0b9bb03e5fd45e28f6e358b8Dake Gu * override the item alignment settings set on HorizontalGridView.  This facet also allows multiple
4608c56822b71ab0aa0b9bb03e5fd45e28f6e358b8Dake Gu * alignment positions within one ViewHolder.
4708c56822b71ab0aa0b9bb03e5fd45e28f6e358b8Dake Gu * </li>
4808c56822b71ab0aa0b9bb03e5fd45e28f6e358b8Dake Gu * </ol>
499106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn */
50a8a3b898da49324e83ea32c3f08776a481312166Tim Kilbournpublic class HorizontalGridView extends BaseGridView {
519106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn
529240e796bc63422c28f2707840bd99c48573279bDake Gu    private boolean mFadingLowEdge;
539240e796bc63422c28f2707840bd99c48573279bDake Gu    private boolean mFadingHighEdge;
549240e796bc63422c28f2707840bd99c48573279bDake Gu
559240e796bc63422c28f2707840bd99c48573279bDake Gu    private Paint mTempPaint = new Paint();
569240e796bc63422c28f2707840bd99c48573279bDake Gu    private Bitmap mTempBitmapLow;
579240e796bc63422c28f2707840bd99c48573279bDake Gu    private LinearGradient mLowFadeShader;
589240e796bc63422c28f2707840bd99c48573279bDake Gu    private int mLowFadeShaderLength;
599240e796bc63422c28f2707840bd99c48573279bDake Gu    private int mLowFadeShaderOffset;
609240e796bc63422c28f2707840bd99c48573279bDake Gu    private Bitmap mTempBitmapHigh;
619240e796bc63422c28f2707840bd99c48573279bDake Gu    private LinearGradient mHighFadeShader;
629240e796bc63422c28f2707840bd99c48573279bDake Gu    private int mHighFadeShaderLength;
639240e796bc63422c28f2707840bd99c48573279bDake Gu    private int mHighFadeShaderOffset;
649240e796bc63422c28f2707840bd99c48573279bDake Gu    private Rect mTempRect = new Rect();
659240e796bc63422c28f2707840bd99c48573279bDake Gu
669106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn    public HorizontalGridView(Context context) {
679106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn        this(context, null);
689106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn    }
699106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn
709106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn    public HorizontalGridView(Context context, AttributeSet attrs) {
719106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn        this(context, attrs, 0);
729106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn    }
739106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn
749106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn    public HorizontalGridView(Context context, AttributeSet attrs, int defStyle) {
759106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn        super(context, attrs, defStyle);
769106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn        mLayoutManager.setOrientation(RecyclerView.HORIZONTAL);
779106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn        initAttributes(context, attrs);
789106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn    }
799106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn
809106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn    protected void initAttributes(Context context, AttributeSet attrs) {
81a8a3b898da49324e83ea32c3f08776a481312166Tim Kilbourn        initBaseGridViewAttributes(context, attrs);
829106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.lbHorizontalGridView);
83f272f7533fcb5aba341e9ab2f4ff0421d668a8caCraig Stout        setRowHeight(a);
849106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn        setNumRows(a.getInt(R.styleable.lbHorizontalGridView_numberOfRows, 1));
859106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn        a.recycle();
863e91a20766ee7aea2bd2a8282425b0d61cd44376Craig Stout        updateLayerType();
879240e796bc63422c28f2707840bd99c48573279bDake Gu        mTempPaint = new Paint();
889240e796bc63422c28f2707840bd99c48573279bDake Gu        mTempPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
899106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn    }
909106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn
91f272f7533fcb5aba341e9ab2f4ff0421d668a8caCraig Stout    void setRowHeight(TypedArray array) {
92f272f7533fcb5aba341e9ab2f4ff0421d668a8caCraig Stout        TypedValue typedValue = array.peekValue(R.styleable.lbHorizontalGridView_rowHeight);
9346443cb5b092f1d9156342645088eead9da026f6Dake Gu        if (typedValue != null) {
9446443cb5b092f1d9156342645088eead9da026f6Dake Gu            int size = array.getLayoutDimension(R.styleable.lbHorizontalGridView_rowHeight, 0);
9546443cb5b092f1d9156342645088eead9da026f6Dake Gu            setRowHeight(size);
96f272f7533fcb5aba341e9ab2f4ff0421d668a8caCraig Stout        }
97f272f7533fcb5aba341e9ab2f4ff0421d668a8caCraig Stout    }
98f272f7533fcb5aba341e9ab2f4ff0421d668a8caCraig Stout
999106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn    /**
100a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     * Sets the number of rows.  Defaults to one.
1019106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn     */
1029106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn    public void setNumRows(int numRows) {
1039106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn        mLayoutManager.setNumRows(numRows);
1049106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn        requestLayout();
1059106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn    }
1069106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn
1079106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn    /**
108a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     * Sets the row height.
109f272f7533fcb5aba341e9ab2f4ff0421d668a8caCraig Stout     *
110a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     * @param height May be {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT WRAP_CONTENT},
111a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     *               or a size in pixels. If zero, row height will be fixed based on number of
112a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     *               rows and view height.
1139106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn     */
1149106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn    public void setRowHeight(int height) {
1159106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn        mLayoutManager.setRowHeight(height);
1169106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn        requestLayout();
1179106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn    }
1189240e796bc63422c28f2707840bd99c48573279bDake Gu
1199240e796bc63422c28f2707840bd99c48573279bDake Gu    /**
120a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     * Sets the fade out left edge to transparent.   Note turn on fading edge is very expensive
1219240e796bc63422c28f2707840bd99c48573279bDake Gu     * that you should turn off when HorizontalGridView is scrolling.
1229240e796bc63422c28f2707840bd99c48573279bDake Gu     */
1239240e796bc63422c28f2707840bd99c48573279bDake Gu    public final void setFadingLeftEdge(boolean fading) {
1249240e796bc63422c28f2707840bd99c48573279bDake Gu        if (mFadingLowEdge != fading) {
1259240e796bc63422c28f2707840bd99c48573279bDake Gu            mFadingLowEdge = fading;
1269240e796bc63422c28f2707840bd99c48573279bDake Gu            if (!mFadingLowEdge) {
1279240e796bc63422c28f2707840bd99c48573279bDake Gu                mTempBitmapLow = null;
1289240e796bc63422c28f2707840bd99c48573279bDake Gu            }
1299240e796bc63422c28f2707840bd99c48573279bDake Gu            invalidate();
130f0e485de16a48547b1c6b272cf005d0b80b92e79Craig Stout            updateLayerType();
1319240e796bc63422c28f2707840bd99c48573279bDake Gu        }
1329240e796bc63422c28f2707840bd99c48573279bDake Gu    }
1339240e796bc63422c28f2707840bd99c48573279bDake Gu
1349240e796bc63422c28f2707840bd99c48573279bDake Gu    /**
135a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     * Returns true if left edge fading is enabled.
1369240e796bc63422c28f2707840bd99c48573279bDake Gu     */
1379240e796bc63422c28f2707840bd99c48573279bDake Gu    public final boolean getFadingLeftEdge() {
1389240e796bc63422c28f2707840bd99c48573279bDake Gu        return mFadingLowEdge;
1399240e796bc63422c28f2707840bd99c48573279bDake Gu    }
1409240e796bc63422c28f2707840bd99c48573279bDake Gu
1419240e796bc63422c28f2707840bd99c48573279bDake Gu    /**
142a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     * Sets the left edge fading length in pixels.
1439240e796bc63422c28f2707840bd99c48573279bDake Gu     */
1449240e796bc63422c28f2707840bd99c48573279bDake Gu    public final void setFadingLeftEdgeLength(int fadeLength) {
1459240e796bc63422c28f2707840bd99c48573279bDake Gu        if (mLowFadeShaderLength != fadeLength) {
1469240e796bc63422c28f2707840bd99c48573279bDake Gu            mLowFadeShaderLength = fadeLength;
1479240e796bc63422c28f2707840bd99c48573279bDake Gu            if (mLowFadeShaderLength != 0) {
1489240e796bc63422c28f2707840bd99c48573279bDake Gu                mLowFadeShader = new LinearGradient(0, 0, mLowFadeShaderLength, 0,
1499240e796bc63422c28f2707840bd99c48573279bDake Gu                        Color.TRANSPARENT, Color.BLACK, Shader.TileMode.CLAMP);
1509240e796bc63422c28f2707840bd99c48573279bDake Gu            } else {
1519240e796bc63422c28f2707840bd99c48573279bDake Gu                mLowFadeShader = null;
1529240e796bc63422c28f2707840bd99c48573279bDake Gu            }
1539240e796bc63422c28f2707840bd99c48573279bDake Gu            invalidate();
1549240e796bc63422c28f2707840bd99c48573279bDake Gu        }
1559240e796bc63422c28f2707840bd99c48573279bDake Gu    }
1569240e796bc63422c28f2707840bd99c48573279bDake Gu
1579240e796bc63422c28f2707840bd99c48573279bDake Gu    /**
158a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     * Returns the left edge fading length in pixels.
1599240e796bc63422c28f2707840bd99c48573279bDake Gu     */
1609240e796bc63422c28f2707840bd99c48573279bDake Gu    public final int getFadingLeftEdgeLength() {
1619240e796bc63422c28f2707840bd99c48573279bDake Gu        return mLowFadeShaderLength;
1629240e796bc63422c28f2707840bd99c48573279bDake Gu    }
1639240e796bc63422c28f2707840bd99c48573279bDake Gu
1649240e796bc63422c28f2707840bd99c48573279bDake Gu    /**
165a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     * Sets the distance in pixels between fading start position and left padding edge.
1669240e796bc63422c28f2707840bd99c48573279bDake Gu     * The fading start position is positive when start position is inside left padding
1679240e796bc63422c28f2707840bd99c48573279bDake Gu     * area.  Default value is 0, means that the fading starts from left padding edge.
1689240e796bc63422c28f2707840bd99c48573279bDake Gu     */
1699240e796bc63422c28f2707840bd99c48573279bDake Gu    public final void setFadingLeftEdgeOffset(int fadeOffset) {
1709240e796bc63422c28f2707840bd99c48573279bDake Gu        if (mLowFadeShaderOffset != fadeOffset) {
1719240e796bc63422c28f2707840bd99c48573279bDake Gu            mLowFadeShaderOffset = fadeOffset;
1729240e796bc63422c28f2707840bd99c48573279bDake Gu            invalidate();
1739240e796bc63422c28f2707840bd99c48573279bDake Gu        }
1749240e796bc63422c28f2707840bd99c48573279bDake Gu    }
1759240e796bc63422c28f2707840bd99c48573279bDake Gu
1769240e796bc63422c28f2707840bd99c48573279bDake Gu    /**
177a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     * Returns the distance in pixels between fading start position and left padding edge.
1789240e796bc63422c28f2707840bd99c48573279bDake Gu     * The fading start position is positive when start position is inside left padding
1799240e796bc63422c28f2707840bd99c48573279bDake Gu     * area.  Default value is 0, means that the fading starts from left padding edge.
1809240e796bc63422c28f2707840bd99c48573279bDake Gu     */
1819240e796bc63422c28f2707840bd99c48573279bDake Gu    public final int getFadingLeftEdgeOffset() {
1829240e796bc63422c28f2707840bd99c48573279bDake Gu        return mLowFadeShaderOffset;
1839240e796bc63422c28f2707840bd99c48573279bDake Gu    }
1849240e796bc63422c28f2707840bd99c48573279bDake Gu
1859240e796bc63422c28f2707840bd99c48573279bDake Gu    /**
186a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     * Sets the fade out right edge to transparent.   Note turn on fading edge is very expensive
1879240e796bc63422c28f2707840bd99c48573279bDake Gu     * that you should turn off when HorizontalGridView is scrolling.
1889240e796bc63422c28f2707840bd99c48573279bDake Gu     */
1899240e796bc63422c28f2707840bd99c48573279bDake Gu    public final void setFadingRightEdge(boolean fading) {
1909240e796bc63422c28f2707840bd99c48573279bDake Gu        if (mFadingHighEdge != fading) {
1919240e796bc63422c28f2707840bd99c48573279bDake Gu            mFadingHighEdge = fading;
1929240e796bc63422c28f2707840bd99c48573279bDake Gu            if (!mFadingHighEdge) {
1939240e796bc63422c28f2707840bd99c48573279bDake Gu                mTempBitmapHigh = null;
1949240e796bc63422c28f2707840bd99c48573279bDake Gu            }
1959240e796bc63422c28f2707840bd99c48573279bDake Gu            invalidate();
196f0e485de16a48547b1c6b272cf005d0b80b92e79Craig Stout            updateLayerType();
1979240e796bc63422c28f2707840bd99c48573279bDake Gu        }
1989240e796bc63422c28f2707840bd99c48573279bDake Gu    }
1999240e796bc63422c28f2707840bd99c48573279bDake Gu
2009240e796bc63422c28f2707840bd99c48573279bDake Gu    /**
201a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     * Returns true if fading right edge is enabled.
2029240e796bc63422c28f2707840bd99c48573279bDake Gu     */
2039240e796bc63422c28f2707840bd99c48573279bDake Gu    public final boolean getFadingRightEdge() {
2049240e796bc63422c28f2707840bd99c48573279bDake Gu        return mFadingHighEdge;
2059240e796bc63422c28f2707840bd99c48573279bDake Gu    }
2069240e796bc63422c28f2707840bd99c48573279bDake Gu
2079240e796bc63422c28f2707840bd99c48573279bDake Gu    /**
208a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     * Sets the right edge fading length in pixels.
2099240e796bc63422c28f2707840bd99c48573279bDake Gu     */
2109240e796bc63422c28f2707840bd99c48573279bDake Gu    public final void setFadingRightEdgeLength(int fadeLength) {
2119240e796bc63422c28f2707840bd99c48573279bDake Gu        if (mHighFadeShaderLength != fadeLength) {
2129240e796bc63422c28f2707840bd99c48573279bDake Gu            mHighFadeShaderLength = fadeLength;
2139240e796bc63422c28f2707840bd99c48573279bDake Gu            if (mHighFadeShaderLength != 0) {
2149240e796bc63422c28f2707840bd99c48573279bDake Gu                mHighFadeShader = new LinearGradient(0, 0, mHighFadeShaderLength, 0,
2159240e796bc63422c28f2707840bd99c48573279bDake Gu                        Color.BLACK, Color.TRANSPARENT, Shader.TileMode.CLAMP);
2169240e796bc63422c28f2707840bd99c48573279bDake Gu            } else {
2179240e796bc63422c28f2707840bd99c48573279bDake Gu                mHighFadeShader = null;
2189240e796bc63422c28f2707840bd99c48573279bDake Gu            }
2199240e796bc63422c28f2707840bd99c48573279bDake Gu            invalidate();
2209240e796bc63422c28f2707840bd99c48573279bDake Gu        }
2219240e796bc63422c28f2707840bd99c48573279bDake Gu    }
2229240e796bc63422c28f2707840bd99c48573279bDake Gu
2239240e796bc63422c28f2707840bd99c48573279bDake Gu    /**
224a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     * Returns the right edge fading length in pixels.
2259240e796bc63422c28f2707840bd99c48573279bDake Gu     */
2269240e796bc63422c28f2707840bd99c48573279bDake Gu    public final int getFadingRightEdgeLength() {
2279240e796bc63422c28f2707840bd99c48573279bDake Gu        return mHighFadeShaderLength;
2289240e796bc63422c28f2707840bd99c48573279bDake Gu    }
2299240e796bc63422c28f2707840bd99c48573279bDake Gu
2309240e796bc63422c28f2707840bd99c48573279bDake Gu    /**
231a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     * Returns the distance in pixels between fading start position and right padding edge.
2329240e796bc63422c28f2707840bd99c48573279bDake Gu     * The fading start position is positive when start position is inside right padding
2339240e796bc63422c28f2707840bd99c48573279bDake Gu     * area.  Default value is 0, means that the fading starts from right padding edge.
2349240e796bc63422c28f2707840bd99c48573279bDake Gu     */
2359240e796bc63422c28f2707840bd99c48573279bDake Gu    public final void setFadingRightEdgeOffset(int fadeOffset) {
2369240e796bc63422c28f2707840bd99c48573279bDake Gu        if (mHighFadeShaderOffset != fadeOffset) {
2379240e796bc63422c28f2707840bd99c48573279bDake Gu            mHighFadeShaderOffset = fadeOffset;
2389240e796bc63422c28f2707840bd99c48573279bDake Gu            invalidate();
2399240e796bc63422c28f2707840bd99c48573279bDake Gu        }
2409240e796bc63422c28f2707840bd99c48573279bDake Gu    }
2419240e796bc63422c28f2707840bd99c48573279bDake Gu
2429240e796bc63422c28f2707840bd99c48573279bDake Gu    /**
243a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     * Sets the distance in pixels between fading start position and right padding edge.
2449240e796bc63422c28f2707840bd99c48573279bDake Gu     * The fading start position is positive when start position is inside right padding
2459240e796bc63422c28f2707840bd99c48573279bDake Gu     * area.  Default value is 0, means that the fading starts from right padding edge.
2469240e796bc63422c28f2707840bd99c48573279bDake Gu     */
2479240e796bc63422c28f2707840bd99c48573279bDake Gu    public final int getFadingRightEdgeOffset() {
2489240e796bc63422c28f2707840bd99c48573279bDake Gu        return mHighFadeShaderOffset;
2499240e796bc63422c28f2707840bd99c48573279bDake Gu    }
2509240e796bc63422c28f2707840bd99c48573279bDake Gu
2519240e796bc63422c28f2707840bd99c48573279bDake Gu    private boolean needsFadingLowEdge() {
2529240e796bc63422c28f2707840bd99c48573279bDake Gu        if (!mFadingLowEdge) {
2539240e796bc63422c28f2707840bd99c48573279bDake Gu            return false;
2549240e796bc63422c28f2707840bd99c48573279bDake Gu        }
2559240e796bc63422c28f2707840bd99c48573279bDake Gu        final int c = getChildCount();
2569240e796bc63422c28f2707840bd99c48573279bDake Gu        for (int i = 0; i < c; i++) {
2579240e796bc63422c28f2707840bd99c48573279bDake Gu            View view = getChildAt(i);
2589240e796bc63422c28f2707840bd99c48573279bDake Gu            if (mLayoutManager.getOpticalLeft(view) <
2599240e796bc63422c28f2707840bd99c48573279bDake Gu                    getPaddingLeft() - mLowFadeShaderOffset) {
2609240e796bc63422c28f2707840bd99c48573279bDake Gu                return true;
2619240e796bc63422c28f2707840bd99c48573279bDake Gu            }
2629240e796bc63422c28f2707840bd99c48573279bDake Gu        }
2639240e796bc63422c28f2707840bd99c48573279bDake Gu        return false;
2649240e796bc63422c28f2707840bd99c48573279bDake Gu    }
2659240e796bc63422c28f2707840bd99c48573279bDake Gu
2669240e796bc63422c28f2707840bd99c48573279bDake Gu    private boolean needsFadingHighEdge() {
2679240e796bc63422c28f2707840bd99c48573279bDake Gu        if (!mFadingHighEdge) {
2689240e796bc63422c28f2707840bd99c48573279bDake Gu            return false;
2699240e796bc63422c28f2707840bd99c48573279bDake Gu        }
2709240e796bc63422c28f2707840bd99c48573279bDake Gu        final int c = getChildCount();
2719240e796bc63422c28f2707840bd99c48573279bDake Gu        for (int i = c - 1; i >= 0; i--) {
2729240e796bc63422c28f2707840bd99c48573279bDake Gu            View view = getChildAt(i);
2739240e796bc63422c28f2707840bd99c48573279bDake Gu            if (mLayoutManager.getOpticalRight(view) > getWidth()
2749240e796bc63422c28f2707840bd99c48573279bDake Gu                    - getPaddingRight() + mHighFadeShaderOffset) {
2759240e796bc63422c28f2707840bd99c48573279bDake Gu                return true;
2769240e796bc63422c28f2707840bd99c48573279bDake Gu            }
2779240e796bc63422c28f2707840bd99c48573279bDake Gu        }
2789240e796bc63422c28f2707840bd99c48573279bDake Gu        return false;
2799240e796bc63422c28f2707840bd99c48573279bDake Gu    }
2809240e796bc63422c28f2707840bd99c48573279bDake Gu
2819240e796bc63422c28f2707840bd99c48573279bDake Gu    private Bitmap getTempBitmapLow() {
2829240e796bc63422c28f2707840bd99c48573279bDake Gu        if (mTempBitmapLow == null
2839240e796bc63422c28f2707840bd99c48573279bDake Gu                || mTempBitmapLow.getWidth() != mLowFadeShaderLength
2849240e796bc63422c28f2707840bd99c48573279bDake Gu                || mTempBitmapLow.getHeight() != getHeight()) {
2859240e796bc63422c28f2707840bd99c48573279bDake Gu            mTempBitmapLow = Bitmap.createBitmap(mLowFadeShaderLength, getHeight(),
2869240e796bc63422c28f2707840bd99c48573279bDake Gu                    Bitmap.Config.ARGB_8888);
2879240e796bc63422c28f2707840bd99c48573279bDake Gu        }
2889240e796bc63422c28f2707840bd99c48573279bDake Gu        return mTempBitmapLow;
2899240e796bc63422c28f2707840bd99c48573279bDake Gu    }
2909240e796bc63422c28f2707840bd99c48573279bDake Gu
2919240e796bc63422c28f2707840bd99c48573279bDake Gu    private Bitmap getTempBitmapHigh() {
2929240e796bc63422c28f2707840bd99c48573279bDake Gu        if (mTempBitmapHigh == null
2939240e796bc63422c28f2707840bd99c48573279bDake Gu                || mTempBitmapHigh.getWidth() != mHighFadeShaderLength
2949240e796bc63422c28f2707840bd99c48573279bDake Gu                || mTempBitmapHigh.getHeight() != getHeight()) {
2952e0c922430f8c285b4325da52d69c09451069c93Craig Stout            // TODO: fix logic for sharing mTempBitmapLow
2962e0c922430f8c285b4325da52d69c09451069c93Craig Stout            if (false && mTempBitmapLow != null
2979240e796bc63422c28f2707840bd99c48573279bDake Gu                    && mTempBitmapLow.getWidth() == mHighFadeShaderLength
2989240e796bc63422c28f2707840bd99c48573279bDake Gu                    && mTempBitmapLow.getHeight() == getHeight()) {
2999240e796bc63422c28f2707840bd99c48573279bDake Gu                // share same bitmap for low edge fading and high edge fading.
3009240e796bc63422c28f2707840bd99c48573279bDake Gu                mTempBitmapHigh = mTempBitmapLow;
3019240e796bc63422c28f2707840bd99c48573279bDake Gu            } else {
3022e0c922430f8c285b4325da52d69c09451069c93Craig Stout                mTempBitmapHigh = Bitmap.createBitmap(mHighFadeShaderLength, getHeight(),
3039240e796bc63422c28f2707840bd99c48573279bDake Gu                        Bitmap.Config.ARGB_8888);
3049240e796bc63422c28f2707840bd99c48573279bDake Gu            }
3059240e796bc63422c28f2707840bd99c48573279bDake Gu        }
3062e0c922430f8c285b4325da52d69c09451069c93Craig Stout        return mTempBitmapHigh;
3079240e796bc63422c28f2707840bd99c48573279bDake Gu    }
3089240e796bc63422c28f2707840bd99c48573279bDake Gu
3099240e796bc63422c28f2707840bd99c48573279bDake Gu    @Override
3109240e796bc63422c28f2707840bd99c48573279bDake Gu    public void draw(Canvas canvas) {
3119240e796bc63422c28f2707840bd99c48573279bDake Gu        final boolean needsFadingLow = needsFadingLowEdge();
3129240e796bc63422c28f2707840bd99c48573279bDake Gu        final boolean needsFadingHigh = needsFadingHighEdge();
3139240e796bc63422c28f2707840bd99c48573279bDake Gu        if (!needsFadingLow) {
3149240e796bc63422c28f2707840bd99c48573279bDake Gu            mTempBitmapLow = null;
3159240e796bc63422c28f2707840bd99c48573279bDake Gu        }
3169240e796bc63422c28f2707840bd99c48573279bDake Gu        if (!needsFadingHigh) {
3179240e796bc63422c28f2707840bd99c48573279bDake Gu            mTempBitmapHigh = null;
3189240e796bc63422c28f2707840bd99c48573279bDake Gu        }
3199240e796bc63422c28f2707840bd99c48573279bDake Gu        if (!needsFadingLow && !needsFadingHigh) {
3209240e796bc63422c28f2707840bd99c48573279bDake Gu            super.draw(canvas);
3219240e796bc63422c28f2707840bd99c48573279bDake Gu            return;
3229240e796bc63422c28f2707840bd99c48573279bDake Gu        }
3239240e796bc63422c28f2707840bd99c48573279bDake Gu
3249240e796bc63422c28f2707840bd99c48573279bDake Gu        int lowEdge = mFadingLowEdge? getPaddingLeft() - mLowFadeShaderOffset - mLowFadeShaderLength : 0;
3259240e796bc63422c28f2707840bd99c48573279bDake Gu        int highEdge = mFadingHighEdge ? getWidth() - getPaddingRight()
3269240e796bc63422c28f2707840bd99c48573279bDake Gu                + mHighFadeShaderOffset + mHighFadeShaderLength : getWidth();
3279240e796bc63422c28f2707840bd99c48573279bDake Gu
3289240e796bc63422c28f2707840bd99c48573279bDake Gu        // draw not-fade content
3299240e796bc63422c28f2707840bd99c48573279bDake Gu        int save = canvas.save();
3302e0c922430f8c285b4325da52d69c09451069c93Craig Stout        canvas.clipRect(lowEdge + (mFadingLowEdge ? mLowFadeShaderLength : 0), 0,
3312e0c922430f8c285b4325da52d69c09451069c93Craig Stout                highEdge - (mFadingHighEdge ? mHighFadeShaderLength : 0), getHeight());
3329240e796bc63422c28f2707840bd99c48573279bDake Gu        super.draw(canvas);
3339240e796bc63422c28f2707840bd99c48573279bDake Gu        canvas.restoreToCount(save);
3349240e796bc63422c28f2707840bd99c48573279bDake Gu
3359240e796bc63422c28f2707840bd99c48573279bDake Gu        Canvas tmpCanvas = new Canvas();
3369240e796bc63422c28f2707840bd99c48573279bDake Gu        mTempRect.top = 0;
3379240e796bc63422c28f2707840bd99c48573279bDake Gu        mTempRect.bottom = getHeight();
3389240e796bc63422c28f2707840bd99c48573279bDake Gu        if (needsFadingLow && mLowFadeShaderLength > 0) {
3399240e796bc63422c28f2707840bd99c48573279bDake Gu            Bitmap tempBitmap = getTempBitmapLow();
3409240e796bc63422c28f2707840bd99c48573279bDake Gu            tempBitmap.eraseColor(Color.TRANSPARENT);
3419240e796bc63422c28f2707840bd99c48573279bDake Gu            tmpCanvas.setBitmap(tempBitmap);
3429240e796bc63422c28f2707840bd99c48573279bDake Gu            // draw original content
3439240e796bc63422c28f2707840bd99c48573279bDake Gu            int tmpSave = tmpCanvas.save();
3449240e796bc63422c28f2707840bd99c48573279bDake Gu            tmpCanvas.clipRect(0, 0, mLowFadeShaderLength, getHeight());
3459240e796bc63422c28f2707840bd99c48573279bDake Gu            tmpCanvas.translate(-lowEdge, 0);
3469240e796bc63422c28f2707840bd99c48573279bDake Gu            super.draw(tmpCanvas);
3479240e796bc63422c28f2707840bd99c48573279bDake Gu            tmpCanvas.restoreToCount(tmpSave);
3489240e796bc63422c28f2707840bd99c48573279bDake Gu            // draw fading out
3499240e796bc63422c28f2707840bd99c48573279bDake Gu            mTempPaint.setShader(mLowFadeShader);
3509240e796bc63422c28f2707840bd99c48573279bDake Gu            tmpCanvas.drawRect(0, 0, mLowFadeShaderLength, getHeight(), mTempPaint);
3519240e796bc63422c28f2707840bd99c48573279bDake Gu            // copy back to canvas
3529240e796bc63422c28f2707840bd99c48573279bDake Gu            mTempRect.left = 0;
3539240e796bc63422c28f2707840bd99c48573279bDake Gu            mTempRect.right = mLowFadeShaderLength;
3549240e796bc63422c28f2707840bd99c48573279bDake Gu            canvas.translate(lowEdge, 0);
3559240e796bc63422c28f2707840bd99c48573279bDake Gu            canvas.drawBitmap(tempBitmap, mTempRect, mTempRect, null);
3569240e796bc63422c28f2707840bd99c48573279bDake Gu            canvas.translate(-lowEdge, 0);
3579240e796bc63422c28f2707840bd99c48573279bDake Gu        }
3589240e796bc63422c28f2707840bd99c48573279bDake Gu        if (needsFadingHigh && mHighFadeShaderLength > 0) {
3599240e796bc63422c28f2707840bd99c48573279bDake Gu            Bitmap tempBitmap = getTempBitmapHigh();
3609240e796bc63422c28f2707840bd99c48573279bDake Gu            tempBitmap.eraseColor(Color.TRANSPARENT);
3619240e796bc63422c28f2707840bd99c48573279bDake Gu            tmpCanvas.setBitmap(tempBitmap);
3629240e796bc63422c28f2707840bd99c48573279bDake Gu            // draw original content
3639240e796bc63422c28f2707840bd99c48573279bDake Gu            int tmpSave = tmpCanvas.save();
3649240e796bc63422c28f2707840bd99c48573279bDake Gu            tmpCanvas.clipRect(0, 0, mHighFadeShaderLength, getHeight());
3659240e796bc63422c28f2707840bd99c48573279bDake Gu            tmpCanvas.translate(-(highEdge - mHighFadeShaderLength), 0);
3669240e796bc63422c28f2707840bd99c48573279bDake Gu            super.draw(tmpCanvas);
3679240e796bc63422c28f2707840bd99c48573279bDake Gu            tmpCanvas.restoreToCount(tmpSave);
3689240e796bc63422c28f2707840bd99c48573279bDake Gu            // draw fading out
3699240e796bc63422c28f2707840bd99c48573279bDake Gu            mTempPaint.setShader(mHighFadeShader);
3709240e796bc63422c28f2707840bd99c48573279bDake Gu            tmpCanvas.drawRect(0, 0, mHighFadeShaderLength, getHeight(), mTempPaint);
3719240e796bc63422c28f2707840bd99c48573279bDake Gu            // copy back to canvas
3729240e796bc63422c28f2707840bd99c48573279bDake Gu            mTempRect.left = 0;
3739240e796bc63422c28f2707840bd99c48573279bDake Gu            mTempRect.right = mHighFadeShaderLength;
3749240e796bc63422c28f2707840bd99c48573279bDake Gu            canvas.translate(highEdge - mHighFadeShaderLength, 0);
3759240e796bc63422c28f2707840bd99c48573279bDake Gu            canvas.drawBitmap(tempBitmap, mTempRect, mTempRect, null);
3769240e796bc63422c28f2707840bd99c48573279bDake Gu            canvas.translate(-(highEdge - mHighFadeShaderLength), 0);
3779240e796bc63422c28f2707840bd99c48573279bDake Gu        }
3789240e796bc63422c28f2707840bd99c48573279bDake Gu    }
379f0e485de16a48547b1c6b272cf005d0b80b92e79Craig Stout
380f0e485de16a48547b1c6b272cf005d0b80b92e79Craig Stout    /**
381f0e485de16a48547b1c6b272cf005d0b80b92e79Craig Stout     * Updates the layer type for this view.
382f0e485de16a48547b1c6b272cf005d0b80b92e79Craig Stout     * If fading edges are needed, use a hardware layer.  This works around the problem
383f0e485de16a48547b1c6b272cf005d0b80b92e79Craig Stout     * that when a child invalidates itself (for example has an animated background),
384f0e485de16a48547b1c6b272cf005d0b80b92e79Craig Stout     * the parent view must also be invalidated to refresh the display list which
385f0e485de16a48547b1c6b272cf005d0b80b92e79Craig Stout     * updates the the caching bitmaps used to draw the fading edges.
386f0e485de16a48547b1c6b272cf005d0b80b92e79Craig Stout     */
387f0e485de16a48547b1c6b272cf005d0b80b92e79Craig Stout    private void updateLayerType() {
388f0e485de16a48547b1c6b272cf005d0b80b92e79Craig Stout        if (mFadingLowEdge || mFadingHighEdge) {
389f0e485de16a48547b1c6b272cf005d0b80b92e79Craig Stout            setLayerType(View.LAYER_TYPE_HARDWARE, null);
3903e91a20766ee7aea2bd2a8282425b0d61cd44376Craig Stout            setWillNotDraw(false);
391f0e485de16a48547b1c6b272cf005d0b80b92e79Craig Stout        } else {
392f0e485de16a48547b1c6b272cf005d0b80b92e79Craig Stout            setLayerType(View.LAYER_TYPE_NONE, null);
3933e91a20766ee7aea2bd2a8282425b0d61cd44376Craig Stout            setWillNotDraw(true);
394f0e485de16a48547b1c6b272cf005d0b80b92e79Craig Stout        }
395f0e485de16a48547b1c6b272cf005d0b80b92e79Craig Stout    }
3969106804a84e5e8733e0b9313f749fa1f726e5d11Tim Kilbourn}
397