165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane/* 265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Copyright (C) 2014 The Android Open Source Project 365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * 465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Licensed under the Apache License, Version 2.0 (the "License"); 565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * you may not use this file except in compliance with the License. 665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * You may obtain a copy of the License at 765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * 865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * http://www.apache.org/licenses/LICENSE-2.0 965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * 1065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Unless required by applicable law or agreed to in writing, software 1165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * distributed under the License is distributed on an "AS IS" BASIS, 1265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * See the License for the specific language governing permissions and 1465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * limitations under the License. 1565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 1665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 1765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lanepackage com.android.tv.settings.widget; 1865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 1965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.content.Context; 2065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.content.res.Configuration; 2165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.view.View; 2265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.view.animation.DecelerateInterpolator; 2365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.view.animation.LinearInterpolator; 2465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.widget.Scroller; 2565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 2665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane/** 2765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Maintains a Scroller object and two axis scrolling information 2865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 2965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lanepublic class ScrollController { 3065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 3165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * try to keep focused view kept in middle of viewport, focus move to the side of viewport when 3265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * scroll to the beginning or end, this will make sure you won't see blank space in viewport 3365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * {@link Axis.ItemWindow#setCount(int)} defines the size of window (how many items) we are 3465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * trying to keep in the middle. <p> 3565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * The middle point is calculated by "scrollCenterOffset" or "scrollCenterOffsetPercent"; 3665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * if none of these two are defined, default value is 1/2 of the size. 3765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * 3865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * @see Axis#setScrollCenterStrategy(int) 3965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * @see Axis#getSystemScrollPos(int) 4065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 4165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public final static int SCROLL_CENTER_IN_MIDDLE = 0; 4265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 4365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 4465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * focus view kept at a fixed location, might see blank space. The distance of fixed location 4565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * to left/top is given by {@link Axis#setScrollCenterOffset(int)} 4665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * 4765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * @see Axis#setScrollCenterStrategy(int) 4865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * @see Axis#getSystemScrollPos(int) 4965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 5065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public final static int SCROLL_CENTER_FIXED = 1; 5165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 5265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 5365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * focus view kept at a fixed percentage distance from the left/top of the view, 5465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * might see blank space. The offset percent is set by 5565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * {@link Axis#setScrollCenterOffsetPercent(int)}. A fixed offset from this 5665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * position may also be set with {@link Axis#setScrollCenterOffset(int)}. 5765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * 5865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * @see Axis#setScrollCenterStrategy(int) 5965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * @see Axis#getSystemScrollPos(int) 6065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 6165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public final static int SCROLL_CENTER_FIXED_PERCENT = 2; 6265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 6365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 6465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * focus view kept at a fixed location, might see blank space. The distance of fixed location 6565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * to right/bottom is given by {@link Axis#setScrollCenterOffset(int)} 6665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * 6765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * @see Axis#setScrollCenterStrategy(int) 6865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * @see Axis#getSystemScrollPos(int) 6965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 7065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public final static int SCROLL_CENTER_FIXED_TO_END = 3; 7165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 7265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 7365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Align center of the item 7465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 7565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public final static int SCROLL_ITEM_ALIGN_CENTER = 0; 7665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 7765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 7865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Align left/top of the item 7965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 8065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public final static int SCROLL_ITEM_ALIGN_LOW = 1; 8165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 8265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 8365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Align right/bottom of the item 8465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 8565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public final static int SCROLL_ITEM_ALIGN_HIGH = 2; 8665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 8765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** operation not allowed */ 8865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public final static int OPERATION_DISABLE = 0; 8965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 9065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 9165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * operation is using {@link Axis#mScrollMin} {@link Axis#mScrollMax}, see description in 9265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * {@link Axis#mScrollCenter} 9365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 9465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public final static int OPERATION_NOTOUCH = 1; 9565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 9665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 9765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * operation is using {@link Axis#mTouchScrollMax} and {@link Axis#mTouchScrollMin}, see 9865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * description in {@link Axis#mScrollCenter} 9965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 10065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public final static int OPERATION_TOUCH = 2; 10165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 10265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 10365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * maps to OPERATION_TOUCH for touchscreen, OPERATION_NORMAL for non-touchscreen 10465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 10565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public final static int OPERATION_AUTO = 3; 10665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 10765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private static final int SCROLL_DURATION_MIN = 250; 10865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private static final int SCROLL_DURATION_MAX = 1500; 10965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private static final int SCROLL_DURATION_PAGE_MIN = 250; 11065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // millisecond per pixel 11165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private static final float SCROLL_DURATION_MS_PER_PIX = 0.25f; 11265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 11365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 11465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Maintains scroll information in one direction 11565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 11665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public static class Axis { 11765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int mOperationMode = OPERATION_NOTOUCH; 11865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 11965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * In {@link ScrollController#OPERATION_TOUCH} mode:<br> 12065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * {@link #mScrollCenter} changes from {@link #mTouchScrollMin} and 12165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * {@link #mTouchScrollMax}; focus won't moved to two sides when scroll to edge of view 12265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * port. 12365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * <p> 12465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * In {@link ScrollController#OPERATION_NOTOUCH} mode:<br> 12565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * mScrollCenter changes from {@link #mScrollMin} and {@link #mScrollMax}. It is different 12665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * than {@link View#getScrollX()} which starts from left edge of first child; mScrollCenter 12765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * starts from center of first child, ends at center of last child; expanded views are 12865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * excluded from calculating the mScrollCenter. We convert the mScrollCenter to system 12965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * scroll position (see {@link ScrollAdapterView#adjustSystemScrollPos}), note it's not 13065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * necessarily a linear transformation between system scrollX and mScrollCenter. <br> 13165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * For {@link #SCROLL_CENTER_IN_MIDDLE}: <br> 13265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * When mScrollCenter is close to {@link #mScrollMin}, {@link View#getScrollX()} will be 13365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * fixed 0, but mScrollCenter is still decreasing, so we can move focus from the item which 13465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * is at center of screen to the first child. <br> 13565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * For {@link #SCROLL_CENTER_FIXED} and 13665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * {@link #SCROLL_CENTER_FIXED_PERCENT}: It's a easy linear conversion 13765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * applied 13865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * <p> 13965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * mScrollCenter is also used to calculate dynamic transformation based on how far a view 14065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * is from the mScrollCenter. For example, the views with center close to mScrollCenter 14165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * will be scaled up in {@link ScrollAdapterView#applyTransformations} 14265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 14365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private float mScrollCenter; 14465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 14565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Maximum scroll value, initially unlimited until we will get the value when scroll to the 14665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * last item of ListAdapter and set the value to center of last child 14765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 14865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int mScrollMax; 14965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 15065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * scroll max for standard touch friendly operation, i.e. focus will not move to side when 15165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * scroll to two edges. 15265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 15365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int mTouchScrollMax; 15465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** right/bottom edge of last child */ 15565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int mMaxEdge; 15665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** left/top edge of first child, typically should be zero*/ 15765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int mMinEdge; 15865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** Minimum scroll value, point to center of first child, typically half of child size */ 15965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int mScrollMin; 16065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 16165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * scroll min for standard touch friendly operation, i.e. focus will not move to side when 16265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * scroll to two edges. 16365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 16465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int mTouchScrollMin; 16565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 16665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int mScrollItemAlign = SCROLL_ITEM_ALIGN_CENTER; 16765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 16865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private boolean mSelectedTakesMoreSpace = false; 16965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 17065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** the offset set by a mouse dragging event */ 17165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private float mDragOffset; 17265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 17365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 17465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Total extra spaces. Divided into four parts:<p> 17565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * 1. extraSpace before scrollPosition, given by {@link #mExtraSpaceLow} 17665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * This value is animating from the extra space of "transition from" to the value 17765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * of "transition to"<p> 17865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * 2. extraSpace after scrollPosition<p> 17965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * 3. size of expanded view of "transition from"<p> 18065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * 4. size of expanded view of "transition to"<p> 18165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Among the four parts: 2,3,4 are after scroll position.<p> 18265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * 3,4 are included in mExpandedSize when {@link #mSelectedTakesMoreSpace} is true<p> 18365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * */ 18465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int mExpandedSize; 18565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** extra space used before the scroll position */ 18665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int mExtraSpaceLow; 18765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int mExtraSpaceHigh; 18865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 18965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int mAlignExtraOffset; 19065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 19165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 19265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Describes how to put the mScrollCenter in the view port different types affects how to 19365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * translate mScrollCenter to system scroll position, see details in getSystemScrollPos(). 19465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 19565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int mScrollCenterStrategy; 19665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 19765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 19865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * used when {@link #mScrollCenterStrategy} is 19965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * {@link #SCROLL_CENTER_FIXED}, {@link #SCROLL_CENTER_FIXED_PERCENT} or 20065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * {@link #SCROLL_CENTER_FIXED_TO_END}, the offset for the fixed location of center 20165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * scroll position relative to left/top, percentage or right/bottom 20265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 20365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int mScrollCenterOffset = -1; 20465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 20565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 20665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * used when {@link #mScrollCenterStrategy} is 20765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * {@link #SCROLL_CENTER_FIXED_PERCENT}. The ratio of the view's height 20865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * at which to place the scroll center from the top. 20965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 21065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private float mScrollCenterOffsetPercent = -1; 21165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 21265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** represents position information of child views, see {@link ItemWindow} */ 21365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public static class Item { 21465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 21565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int mIndex; 21665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int mLow; 21765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int mHigh; 21865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int mCenter; 21965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 22065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public Item() { 22165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mIndex = -1; 22265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 22365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 22465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public int getLow() { 22565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mLow; 22665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 22765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 22865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public int getHigh() { 22965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mHigh; 23065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 23165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 23265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public int getCenter() { 23365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mCenter; 23465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 23565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 23665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public int getIndex() { 23765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mIndex; 23865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 23965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 24065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** set low bound, high bound and index for the item */ 24165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public void setValue(int index, int low, int high) { 24265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mIndex = index; 24365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mLow = low; 24465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mHigh = high; 24565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mCenter = (low + high) / 2; 24665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 24765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 24865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public boolean isValid() { 24965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mIndex >= 0; 25065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 25165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 25265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane @Override 25365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public String toString() { 25465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mIndex + "[" + mLow + "," + mHigh + "]"; 25565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 25665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 25765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 25865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int mSize; 25965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 26065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int mPaddingLow; 26165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 26265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int mPaddingHigh; 26365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 26465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private Lerper mLerper; 26565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 26665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private String mName; // for debugging 26765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 26865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public Axis(Lerper lerper, String name) { 26965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mScrollCenterStrategy = SCROLL_CENTER_IN_MIDDLE; 27065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mLerper = lerper; 27165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane reset(); 27265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mName = name; 27365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 27465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 27565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public int getScrollCenterStrategy() { 27665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mScrollCenterStrategy; 27765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 27865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 27965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public void setScrollCenterStrategy(int scrollCenterStrategy) { 28065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mScrollCenterStrategy = scrollCenterStrategy; 28165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 28265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 28365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public int getScrollCenterOffset() { 28465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mScrollCenterOffset; 28565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 28665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 28765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public void setScrollCenterOffset(int scrollCenterOffset) { 28865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mScrollCenterOffset = scrollCenterOffset; 28965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 29065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 29165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public void setScrollCenterOffsetPercent(int scrollCenterOffsetPercent) { 29265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (scrollCenterOffsetPercent < 0) { 29365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane scrollCenterOffsetPercent = 0; 29465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else if (scrollCenterOffsetPercent > 100) { 29565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane scrollCenterOffsetPercent = 100; 29665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 29765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mScrollCenterOffsetPercent = ( scrollCenterOffsetPercent / 100.0f); 29865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 29965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 30065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public void setSelectedTakesMoreSpace(boolean selectedTakesMoreSpace) { 30165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mSelectedTakesMoreSpace = selectedTakesMoreSpace; 30265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 30365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 30465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public boolean getSelectedTakesMoreSpace() { 30565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mSelectedTakesMoreSpace; 30665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 30765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 30865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public void setScrollItemAlign(int align) { 30965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mScrollItemAlign = align; 31065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 31165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 31265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public int getScrollItemAlign() { 31365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mScrollItemAlign; 31465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 31565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 31665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public int getScrollCenter() { 31765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return (int) mScrollCenter; 31865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 31965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 32065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public void setOperationMode(int mode) { 32165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mOperationMode = mode; 32265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 32365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 32465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int scrollMin() { 32565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mOperationMode == OPERATION_TOUCH ? mTouchScrollMin : mScrollMin; 32665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 32765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 32865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int scrollMax() { 32965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mOperationMode == OPERATION_TOUCH ? mTouchScrollMax : mScrollMax; 33065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 33165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 33265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** update scroll min and minEdge, Integer.MIN_VALUE means unknown*/ 33365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public void updateScrollMin(int scrollMin, int minEdge) { 33465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mScrollMin = scrollMin; 33565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mScrollCenter < mScrollMin) { 33665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mScrollCenter = mScrollMin; 33765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 33865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mMinEdge = minEdge; 33965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mScrollCenterStrategy != SCROLL_CENTER_IN_MIDDLE 34065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane || mScrollMin == Integer.MIN_VALUE) { 34165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mTouchScrollMin = mScrollMin; 34265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else { 34365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mTouchScrollMin = Math.max(mScrollMin, mMinEdge + mSize / 2); 34465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 34565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 34665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 34765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public void invalidateScrollMin() { 34865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mScrollMin = Integer.MIN_VALUE; 34965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mMinEdge = Integer.MIN_VALUE; 35065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mTouchScrollMin = Integer.MIN_VALUE; 35165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 35265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 35365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** update scroll max and maxEdge, Integer.MAX_VALUE means unknown*/ 35465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public void updateScrollMax(int scrollMax, int maxEdge) { 35565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mScrollMax = scrollMax; 35665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mScrollCenter > mScrollMax) { 35765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mScrollCenter = mScrollMax; 35865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 35965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mMaxEdge = maxEdge; 36065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mScrollCenterStrategy != SCROLL_CENTER_IN_MIDDLE 36165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane || mScrollMax == Integer.MAX_VALUE) { 36265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mTouchScrollMax = mScrollMax; 36365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else { 36465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mTouchScrollMax = Math.min(mScrollMax, mMaxEdge - mSize / 2); 36565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 36665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 36765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 36865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public void invalidateScrollMax() { 36965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mScrollMax = Integer.MAX_VALUE; 37065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mMaxEdge = Integer.MAX_VALUE; 37165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mTouchScrollMax = Integer.MAX_VALUE; 37265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 37365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 37465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public boolean canScroll(boolean forward) { 37565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (forward) { 37665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mScrollCenter >= mScrollMax) { 37765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return false; 37865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 37965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else { 38065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mScrollCenter <= mScrollMin) { 38165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return false; 38265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 38365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 38465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return true; 38565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 38665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 38765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private boolean updateScrollCenter(float scrollTarget, boolean lerper) { 38865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mDragOffset = 0; 38965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int scrollMin = scrollMin(); 39065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int scrollMax = scrollMax(); 39165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane boolean overScroll = false; 39265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (scrollMin >= scrollMax) { 39365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane scrollTarget = mScrollCenter; 39465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane overScroll = true; 39565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else if (scrollTarget < scrollMin) { 39665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane scrollTarget = scrollMin; 39765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane overScroll = true; 39865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else if (scrollTarget > scrollMax) { 39965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane scrollTarget = scrollMax; 40065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane overScroll = true; 40165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 40265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (lerper) { 40365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mScrollCenter = mLerper.getValue(mScrollCenter, scrollTarget); 40465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else { 40565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mScrollCenter = scrollTarget; 40665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 40765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return overScroll; 40865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 40965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 41065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private void updateFromDrag() { 41165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane updateScrollCenter(mScrollCenter + mDragOffset, false); 41265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 41365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 41465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private void dragBy(float distanceX) { 41565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mDragOffset += distanceX; 41665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 41765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 41865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private void reset() { 41965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mScrollCenter = Integer.MIN_VALUE; 42065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mScrollMin = Integer.MIN_VALUE; 42165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mMinEdge = Integer.MIN_VALUE; 42265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mTouchScrollMin = Integer.MIN_VALUE; 42365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mScrollMax = Integer.MAX_VALUE; 42465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mMaxEdge = Integer.MAX_VALUE; 42565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mTouchScrollMax = Integer.MAX_VALUE; 42665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mExpandedSize = 0; 42765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mDragOffset = 0; 42865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 42965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 43065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public boolean isMinUnknown() { 43165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mScrollMin == Integer.MIN_VALUE; 43265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 43365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 43465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public boolean isMaxUnknown() { 43565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mScrollMax == Integer.MAX_VALUE; 43665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 43765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 43865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public int getSizeForExpandableItem() { 43965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mSize - mPaddingLow - mPaddingHigh - mExpandedSize; 44065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 44165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 44265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public void setSize(int size) { 44365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mSize = size; 44465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 44565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 44665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public void setExpandedSize(int expandedSize) { 44765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mExpandedSize = expandedSize; 44865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 44965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 45065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public void setExtraSpaceLow(int extraSpaceLow) { 45165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mExtraSpaceLow = extraSpaceLow; 45265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 45365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 45465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public void setExtraSpaceHigh(int extraSpaceHigh) { 45565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mExtraSpaceHigh = extraSpaceHigh; 45665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 45765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 45865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public void setAlignExtraOffset(int extraOffset) { 45965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mAlignExtraOffset = extraOffset; 46065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 46165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 46265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public void setPadding(int paddingLow, int paddingHigh) { 46365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mPaddingLow = paddingLow; 46465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mPaddingHigh = paddingHigh; 46565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 46665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 46765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public int getPaddingLow() { 46865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mPaddingLow; 46965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 47065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 47165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public int getPaddingHigh() { 47265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mPaddingHigh; 47365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 47465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 47565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public int getSystemScrollPos() { 47665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return getSystemScrollPos((int) mScrollCenter); 47765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 47865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 47965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public int getSystemScrollPos(int scrollCenter) { 48065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane scrollCenter += mAlignExtraOffset; 48165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 48265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // For the "FIXED" strategy family: 48365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int compensate = mSelectedTakesMoreSpace ? mExtraSpaceLow : -mExtraSpaceLow; 48465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mScrollCenterStrategy == SCROLL_CENTER_FIXED) { 48565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return scrollCenter - mScrollCenterOffset + compensate; 48665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else if (mScrollCenterStrategy == SCROLL_CENTER_FIXED_TO_END) { 48765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return scrollCenter - (mSize - mScrollCenterOffset) + compensate; 48865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else if (mScrollCenterStrategy == SCROLL_CENTER_FIXED_PERCENT) { 48965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return (int) (scrollCenter - mScrollCenterOffset - mSize 49065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * mScrollCenterOffsetPercent) + compensate; 49165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 49265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int clientSize = mSize - mPaddingLow - mPaddingHigh; 49365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // For SCROLL_CENTER_IN_MIDDLE, first calculate the middle point: 49465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // if the scrollCenterOffset or scrollCenterOffsetPercent is specified, 49565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // use it for middle point, otherwise, use 1/2 of the size 49665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int middlePosition; 49765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mScrollCenterOffset >= 0) { 49865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane middlePosition = mScrollCenterOffset - mPaddingLow; 49965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else if (mScrollCenterOffsetPercent >= 0) { 50065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane middlePosition = (int) (mSize * mScrollCenterOffsetPercent) - mPaddingLow; 50165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else { 50265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane middlePosition = clientSize / 2; 50365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 50465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int afterMiddlePosition = clientSize - middlePosition; 50565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // Following code for mSelectedTakesMoreSpace = true/false is quite similar, 50665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // but it's still more clear and easier to debug when separating them. 50765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane boolean isMinUnknown = isMinUnknown(); 50865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane boolean isMaxUnknown = isMaxUnknown(); 50965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mSelectedTakesMoreSpace) { 51065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int extraSpaceLow; 51165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane switch (getScrollItemAlign()) { 51265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane case SCROLL_ITEM_ALIGN_LOW: 51365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane extraSpaceLow = 0; 51465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane break; 51565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane case SCROLL_ITEM_ALIGN_HIGH: 51665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane extraSpaceLow = mExtraSpaceLow + mExtraSpaceHigh; 51765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane break; 51865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane case SCROLL_ITEM_ALIGN_CENTER: 51965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane default: 52065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane extraSpaceLow = mExtraSpaceLow; 52165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane break; 52265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 52365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (!isMinUnknown && !isMaxUnknown) { 52465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mMaxEdge - mMinEdge + mExpandedSize <= clientSize) { 52565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // total children size is less than view port: align the left edge 52665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // of first child to view port's left edge 52765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mMinEdge - mPaddingLow; 52865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 52965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 53065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (!isMinUnknown) { 53165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (scrollCenter - mMinEdge + extraSpaceLow <= middlePosition) { 53265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // scroll center is within half of view port size: align the left edge 53365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // of first child to the left edge of view port 53465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mMinEdge - mPaddingLow; 53565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 53665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 53765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (!isMaxUnknown) { 53865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int spaceAfterScrollCenter = mExpandedSize - extraSpaceLow; 53965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mMaxEdge - scrollCenter + spaceAfterScrollCenter <= afterMiddlePosition) { 54065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // scroll center is very close to the right edge of view port : align the 54165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // right edge of last children (plus expanded size) to view port's right 54265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mMaxEdge -mPaddingLow - (clientSize - mExpandedSize ); 54365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 54465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 54565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // else put scroll center in middle of view port 54665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return scrollCenter - middlePosition - mPaddingLow + extraSpaceLow; 54765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else { 54865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int shift; 54965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane switch (getScrollItemAlign()) { 55065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane case SCROLL_ITEM_ALIGN_LOW: 55165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane shift = - mExtraSpaceLow; 55265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane break; 55365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane case SCROLL_ITEM_ALIGN_HIGH: 55465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane shift = + mExtraSpaceHigh; 55565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane break; 55665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane case SCROLL_ITEM_ALIGN_CENTER: 55765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane default: 55865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane shift = 0; 55965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane break; 56065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 56165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (!isMinUnknown && !isMaxUnknown) { 56265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mMaxEdge - mMinEdge + mExpandedSize <= clientSize) { 56365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // total children size is less than view port: align the left edge 56465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // of first child to view port's left edge 56565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mMinEdge - mPaddingLow; 56665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 56765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 56865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (!isMinUnknown) { 56965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (scrollCenter + shift - mMinEdge <= middlePosition) { 57065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // scroll center is within half of view port size: align the left edge 57165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // of first child to the left edge of view port 57265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mMinEdge - mPaddingLow; 57365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 57465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 57565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (!isMaxUnknown) { 57665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mMaxEdge - scrollCenter - shift + mExpandedSize <= afterMiddlePosition) { 57765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // scroll center is very close to the right edge of view port : align the 57865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // right edge of last children (plus expanded size) to view port's right 57965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mMaxEdge -mPaddingLow - (clientSize - mExpandedSize ); 58065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 58165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 58265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // else put scroll center in middle of view port 58365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return scrollCenter - middlePosition - mPaddingLow + shift; 58465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 58565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 58665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 58765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane @Override 58865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public String toString() { 58965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return "center: " + mScrollCenter + " min:" + mMinEdge + "," + mScrollMin + 59065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane " max:" + mScrollMax + "," + mMaxEdge; 59165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 59265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 59365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 59465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 59565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private Context mContext; 59665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 59765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // we separate Scrollers for scroll animation and fling animation; this is because we want a 59865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // flywheel feature for fling animation, ScrollAdapterView inserts scroll animation between 59965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // fling animations, the fling animation will mistakenly continue the old velocity of scroll 60065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // animation: that's wrong, we want fling animation pickup the old velocity of last fling. 60165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private Scroller mScrollScroller; 60265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private Scroller mFlingScroller; 60365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 60465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private final static int STATE_NONE = 0; 60565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 60665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** using fling scroller */ 60765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private final static int STATE_FLING = 1; 60865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 60965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** using scroll scroller */ 61065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private final static int STATE_SCROLL = 2; 61165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 61265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** using drag */ 61365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private final static int STATE_DRAG = 3; 61465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 61565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int mState = STATE_NONE; 61665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 61765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int mOrientation = ScrollAdapterView.HORIZONTAL; 61865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 61965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private Lerper mLerper = new Lerper(); 62065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 62165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public Axis vertical = new Axis(mLerper, "vertical"); 62265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 62365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public Axis horizontal = new Axis(mLerper, "horizontal"); 62465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 62565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private Axis mMainAxis = horizontal; 62665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 62765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private Axis mSecondAxis = vertical; 62865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 62965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** fling operation mode */ 63065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int mFlingMode = OPERATION_AUTO; 63165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 63265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** drag operation mode */ 63365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int mDragMode = OPERATION_AUTO; 63465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 63565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** scroll operation mode (for DPAD) */ 63665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int mScrollMode = OPERATION_NOTOUCH; 63765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 63865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** the major movement is in horizontal or vertical */ 63965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private boolean mMainHorizontal; 64065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private boolean mHorizontalForward = true; 64165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private boolean mVerticalForward = true; 64265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 64365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public Lerper lerper() { 64465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mLerper; 64565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 64665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 64765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public Axis mainAxis() { 64865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mMainAxis; 64965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 65065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 65165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public Axis secondAxis() { 65265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mSecondAxis; 65365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 65465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 65565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public void setLerperDivisor(float divisor) { 65665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mLerper.setDivisor(divisor); 65765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 65865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 65965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public ScrollController(Context context) { 66065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mContext = context; 66165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // Quint easeOut 66265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mScrollScroller = new Scroller(mContext, new DecelerateInterpolator(2)); 66365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mFlingScroller = new Scroller(mContext, new LinearInterpolator()); 66465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 66565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 66665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public void setOrientation(int orientation) { 66765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int align = mainAxis().getScrollItemAlign(); 66865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane boolean selectedTakesMoreSpace = mainAxis().getSelectedTakesMoreSpace(); 66965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mOrientation = orientation; 67065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mOrientation == ScrollAdapterView.HORIZONTAL) { 67165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mMainAxis = horizontal; 67265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mSecondAxis = vertical; 67365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else { 67465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mMainAxis = vertical; 67565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mSecondAxis = horizontal; 67665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 67765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mMainAxis.setScrollItemAlign(align); 67865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mSecondAxis.setScrollItemAlign(SCROLL_ITEM_ALIGN_CENTER); 67965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mMainAxis.setSelectedTakesMoreSpace(selectedTakesMoreSpace); 68065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mSecondAxis.setSelectedTakesMoreSpace(false); 68165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 68265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 68365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public void setScrollItemAlign(int align) { 68465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mainAxis().setScrollItemAlign(align); 68565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 68665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 68765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public int getScrollItemAlign() { 68865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mainAxis().getScrollItemAlign(); 68965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 69065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 69165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public int getOrientation() { 69265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mOrientation; 69365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 69465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 69565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public int getFlingMode() { 69665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mFlingMode; 69765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 69865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 69965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public void setFlingMode(int mode) { 70065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane this.mFlingMode = mode; 70165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 70265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 70365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public int getDragMode() { 70465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mDragMode; 70565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 70665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 70765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public void setDragMode(int mode) { 70865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane this.mDragMode = mode; 70965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 71065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 71165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public int getScrollMode() { 71265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mScrollMode; 71365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 71465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 71565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public void setScrollMode(int mode) { 71665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane this.mScrollMode = mode; 71765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 71865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 71965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public float getCurrVelocity() { 72065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mState == STATE_FLING) { 72165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mFlingScroller.getCurrVelocity(); 72265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else if (mState == STATE_SCROLL) { 72365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mScrollScroller.getCurrVelocity(); 72465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 72565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return 0; 72665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 72765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 72865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public boolean canScroll(int dx, int dy) { 72965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (dx == 0 && dy == 0) { 73065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return false; 73165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 73265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return (dx == 0 || horizontal.canScroll(dx < 0)) && 73365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane (dy == 0 || vertical.canScroll(dy < 0)); 73465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 73565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 73665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int getMode(int mode) { 73765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mode == OPERATION_AUTO) { 73865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mContext.getResources().getConfiguration().touchscreen 73965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane == Configuration.TOUCHSCREEN_NOTOUCH) { 74065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mode = OPERATION_NOTOUCH; 74165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else { 74265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mode = OPERATION_TOUCH; 74365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 74465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 74565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mode; 74665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 74765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 74865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private void updateDirection(float dx, float dy) { 74965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mMainHorizontal = Math.abs(dx) >= Math.abs(dy); 75065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (dx > 0) { 75165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mHorizontalForward = true; 75265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else if (dx < 0) { 75365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mHorizontalForward = false; 75465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 75565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (dy > 0) { 75665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mVerticalForward = true; 75765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else if (dy < 0) { 75865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mVerticalForward = false; 75965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 76065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 76165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 76265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public boolean fling(int velocity_x, int velocity_y){ 76365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mFlingMode == OPERATION_DISABLE) { 76465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return false; 76565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 76665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final int operationMode = getMode(mFlingMode); 76765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane horizontal.setOperationMode(operationMode); 76865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane vertical.setOperationMode(operationMode); 76965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mState = STATE_FLING; 77065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mFlingScroller.fling((int)(horizontal.mScrollCenter), 77165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane (int)(vertical.mScrollCenter), 77265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane velocity_x, 77365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane velocity_y, 77465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane Integer.MIN_VALUE, 77565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane Integer.MAX_VALUE, 77665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane Integer.MIN_VALUE, 77765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane Integer.MAX_VALUE); 77865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane updateDirection(velocity_x, velocity_y); 77965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return true; 78065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 78165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 78265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public void startScroll(int dx, int dy, boolean easeFling, int duration, boolean page) { 78365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mScrollMode == OPERATION_DISABLE) { 78465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return; 78565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 78665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final int operationMode = getMode(mScrollMode); 78765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane horizontal.setOperationMode(operationMode); 78865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane vertical.setOperationMode(operationMode); 78965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane Scroller scroller; 79065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (easeFling) { 79165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mState = STATE_FLING; 79265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane scroller = mFlingScroller; 79365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else { 79465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mState = STATE_SCROLL; 79565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane scroller = mScrollScroller; 79665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 79765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int basex = horizontal.getScrollCenter(); 79865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int basey = vertical.getScrollCenter(); 79965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (!scroller.isFinished()) { 80065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // during scrolling, we should continue from getCurrX/getCurrY() (might be different 80165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // than current Scroll Center due to Lerper) 80265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane dx = basex + dx - scroller.getCurrX(); 80365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane dy = basey + dy - scroller.getCurrY(); 80465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane basex = scroller.getCurrX(); 80565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane basey = scroller.getCurrY(); 80665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 80765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane updateDirection(dx, dy); 80865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (easeFling) { 80965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane float curDx = Math.abs(mFlingScroller.getFinalX() - mFlingScroller.getStartX()); 81065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane float curDy = Math.abs(mFlingScroller.getFinalY() - mFlingScroller.getStartY()); 81165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane float hyp = (float) Math.sqrt(curDx * curDx + curDy * curDy); 81265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane float velocity = mFlingScroller.getCurrVelocity(); 81365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane float velocityX = velocity * curDx / hyp; 81465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane float velocityY = velocity * curDy / hyp; 81565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int durationX = velocityX ==0 ? 0 : (int)((Math.abs(dx) * 1000) / velocityX); 81665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int durationY = velocityY ==0 ? 0 : (int)((Math.abs(dy) * 1000) / velocityY); 81765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (duration == 0) duration = Math.max(durationX, durationY); 81865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else { 81965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (duration == 0) { 82065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane duration = getScrollDuration((int) Math.sqrt(dx * dx + dy * dy), page); 82165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 82265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 82365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane scroller.startScroll(basex, basey, dx, dy, duration); 82465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 82565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 82665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public int getCurrentAnimationDuration() { 82765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane Scroller scroller; 82865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mState == STATE_FLING) { 82965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane scroller = mFlingScroller; 83065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else if (mState == STATE_SCROLL) { 83165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane scroller = mScrollScroller; 83265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else { 83365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return 0; 83465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 83565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return scroller.getDuration(); 83665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 83765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 83865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public void startScrollByMain(int deltaMain, int deltaSecond, boolean easeFling, 83965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int duration, boolean page) { 84065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int dx, dy; 84165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mOrientation == ScrollAdapterView.HORIZONTAL) { 84265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane dx = deltaMain; 84365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane dy = deltaSecond; 84465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else { 84565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane dx = deltaSecond; 84665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane dy = deltaMain; 84765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 84865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane startScroll(dx, dy, easeFling, duration, page); 84965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 85065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 85165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public boolean dragBy(float distanceX, float distanceY) { 85265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mDragMode == OPERATION_DISABLE) { 85365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return false; 85465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 85565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final int operationMode = getMode(mDragMode); 85665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane horizontal.setOperationMode(operationMode); 85765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane vertical.setOperationMode(operationMode); 85865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane horizontal.dragBy(distanceX); 85965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane vertical.dragBy(distanceY); 86065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mState = STATE_DRAG; 86165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return true; 86265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 86365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 86465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public void stopDrag() { 86565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mState = STATE_NONE; 86665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane vertical.mDragOffset = 0; 86765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane horizontal.mDragOffset = 0; 86865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 86965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 87065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public void setScrollCenterByMain(int centerMain, int centerSecond) { 87165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mOrientation == ScrollAdapterView.HORIZONTAL) { 87265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane setScrollCenter(centerMain, centerSecond); 87365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else { 87465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane setScrollCenter(centerSecond, centerMain); 87565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 87665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 87765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 87865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public void setScrollCenter(int centerX, int centerY) { 87965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane horizontal.updateScrollCenter(centerX, false); 88065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane vertical.updateScrollCenter(centerY, false); 88165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // centerX, centerY might be clipped by min/max 88265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane centerX = horizontal.getScrollCenter(); 88365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane centerY = vertical.getScrollCenter(); 88465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mFlingScroller.setFinalX(centerX); 88565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mFlingScroller.setFinalY(centerY); 88665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mFlingScroller.abortAnimation(); 88765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mScrollScroller.setFinalX(centerX); 88865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mScrollScroller.setFinalY(centerY); 88965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mScrollScroller.abortAnimation(); 89065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 89165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 89265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public int getFinalX() { 89365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mState == STATE_FLING) { 89465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mFlingScroller.getFinalX(); 89565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else if (mState == STATE_SCROLL) { 89665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mScrollScroller.getFinalX(); 89765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 89865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return horizontal.getScrollCenter(); 89965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 90065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 90165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public int getFinalY() { 90265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mState == STATE_FLING) { 90365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mFlingScroller.getFinalY(); 90465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else if (mState == STATE_SCROLL) { 90565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mScrollScroller.getFinalY(); 90665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 90765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return vertical.getScrollCenter(); 90865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 90965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 91065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public void setFinalX(int finalX) { 91165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mState == STATE_FLING) { 91265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mFlingScroller.setFinalX(finalX); 91365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else if (mState == STATE_SCROLL) { 91465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mScrollScroller.setFinalX(finalX); 91565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 91665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 91765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 91865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public void setFinalY(int finalY) { 91965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mState == STATE_FLING) { 92065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mFlingScroller.setFinalY(finalY); 92165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else if (mState == STATE_SCROLL) { 92265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mScrollScroller.setFinalY(finalY); 92365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 92465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 92565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 92665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** return true if scroll/fling animation or lerper is not stopped */ 92765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public boolean isFinished() { 92865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane Scroller scroller; 92965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mState == STATE_FLING) { 93065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane scroller = mFlingScroller; 93165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else if (mState == STATE_SCROLL) { 93265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane scroller = mScrollScroller; 93365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else if (mState == STATE_DRAG){ 93465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return false; 93565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else { 93665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return true; 93765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 93865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (scroller.isFinished()) { 93965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return (horizontal.getScrollCenter() == scroller.getCurrX() && 94065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane vertical.getScrollCenter() == scroller.getCurrY()); 94165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 94265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return false; 94365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 94465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 94565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public boolean isMainAxisMovingForward() { 94665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mOrientation == ScrollAdapterView.HORIZONTAL ? 94765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mHorizontalForward : mVerticalForward; 94865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 94965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 95065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public boolean isSecondAxisMovingForward() { 95165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mOrientation == ScrollAdapterView.HORIZONTAL ? 95265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mVerticalForward : mHorizontalForward; 95365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 95465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 95565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public int getLastDirection() { 95665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mMainHorizontal) { 95765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mHorizontalForward ? View.FOCUS_RIGHT : View.FOCUS_LEFT; 95865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else { 95965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mVerticalForward ? View.FOCUS_DOWN : View.FOCUS_UP; 96065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 96165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 96265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 96365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 96465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * update scroller position, this is either trigger by fling()/startScroll() on the 96565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * scroller object, or lerper, or can be caused by a dragBy() 96665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 96765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public void computeAndSetScrollPosition() { 96865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane Scroller scroller; 96965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mState == STATE_FLING) { 97065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane scroller = mFlingScroller; 97165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else if (mState == STATE_SCROLL) { 97265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane scroller = mScrollScroller; 97365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else if (mState == STATE_DRAG) { 97465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (horizontal.mDragOffset != 0 || vertical.mDragOffset !=0 ) { 97565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane horizontal.updateFromDrag(); 97665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane vertical.updateFromDrag(); 97765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 97865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return; 97965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else { 98065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return; 98165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 98265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (!isFinished()) { 98365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane scroller.computeScrollOffset(); 98465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane horizontal.updateScrollCenter(scroller.getCurrX(), true); 98565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane vertical.updateScrollCenter(scroller.getCurrY(), true); 98665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 98765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 98865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 98965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** get Scroll animation duration in ms for given pixels */ 99065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public int getScrollDuration(int distance, boolean isPage) { 99165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int ms = (int)(distance * SCROLL_DURATION_MS_PER_PIX); 99265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int minValue = isPage ? SCROLL_DURATION_PAGE_MIN : SCROLL_DURATION_MIN; 99365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (ms < minValue) { 99465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane ms = minValue; 99565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else if (ms > SCROLL_DURATION_MAX) { 99665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane ms = SCROLL_DURATION_MAX; 99765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 99865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return ms; 99965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 100065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 100165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final public void reset() { 100265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mainAxis().reset(); 100365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 100465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 100565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane @Override 100665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public String toString() { 100765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return new StringBuffer().append("horizontal=") 100865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane .append(horizontal.toString()) 100965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane .append("vertical=") 101065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane .append(vertical.toString()) 101165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane .toString(); 101265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 101365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 101465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane} 1015