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