NestedScrollView.java revision bd9c35d6db58b5c9b075d751a2c64203eccc6cd7
11fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell/* 21fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Copyright (C) 2015 The Android Open Source Project 31fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * 41fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Licensed under the Apache License, Version 2.0 (the "License"); 51fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * you may not use this file except in compliance with the License. 61fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * You may obtain a copy of the License at 71fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * 81fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * http://www.apache.org/licenses/LICENSE-2.0 91fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * 101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Unless required by applicable law or agreed to in writing, software 111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * distributed under the License is distributed on an "AS IS" BASIS, 121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * See the License for the specific language governing permissions and 141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * limitations under the License. 151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 18ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikaspackage androidx.core.widget; 191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 20ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP; 218e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas 221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.content.Context; 231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.content.res.TypedArray; 241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.graphics.Canvas; 251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.graphics.Rect; 26bcb5323316d898cc22873efc2a145a5912e8a75cshepshapardimport android.os.Build; 271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.os.Bundle; 281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.os.Parcel; 291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.os.Parcelable; 301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.util.AttributeSet; 311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.util.Log; 321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.util.TypedValue; 331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.FocusFinder; 341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.KeyEvent; 351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.MotionEvent; 361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.VelocityTracker; 371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.View; 381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.ViewConfiguration; 391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.ViewGroup; 401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.ViewParent; 411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.accessibility.AccessibilityEvent; 421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.animation.AnimationUtils; 43506695b4cf6b22d70d2e3647d94551e721ee0430Aurimas Liutikasimport android.widget.EdgeEffect; 441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.widget.FrameLayout; 453035be16658d7652fdc472f971c81d8f7ffb60fdAurimas Liutikasimport android.widget.OverScroller; 461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.widget.ScrollView; 471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 489dede51868bbbe16aadcd65e04860bea8ea50e05Aurimas Liutikasimport androidx.annotation.NonNull; 499dede51868bbbe16aadcd65e04860bea8ea50e05Aurimas Liutikasimport androidx.annotation.Nullable; 509dede51868bbbe16aadcd65e04860bea8ea50e05Aurimas Liutikasimport androidx.annotation.RestrictTo; 519dede51868bbbe16aadcd65e04860bea8ea50e05Aurimas Liutikasimport androidx.core.view.AccessibilityDelegateCompat; 529dede51868bbbe16aadcd65e04860bea8ea50e05Aurimas Liutikasimport androidx.core.view.InputDeviceCompat; 539dede51868bbbe16aadcd65e04860bea8ea50e05Aurimas Liutikasimport androidx.core.view.NestedScrollingChild2; 549dede51868bbbe16aadcd65e04860bea8ea50e05Aurimas Liutikasimport androidx.core.view.NestedScrollingChildHelper; 55bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapardimport androidx.core.view.NestedScrollingParent2; 569dede51868bbbe16aadcd65e04860bea8ea50e05Aurimas Liutikasimport androidx.core.view.NestedScrollingParentHelper; 579dede51868bbbe16aadcd65e04860bea8ea50e05Aurimas Liutikasimport androidx.core.view.ScrollingView; 589dede51868bbbe16aadcd65e04860bea8ea50e05Aurimas Liutikasimport androidx.core.view.ViewCompat; 599dede51868bbbe16aadcd65e04860bea8ea50e05Aurimas Liutikasimport androidx.core.view.accessibility.AccessibilityNodeInfoCompat; 609dede51868bbbe16aadcd65e04860bea8ea50e05Aurimas Liutikasimport androidx.core.view.accessibility.AccessibilityRecordCompat; 619dede51868bbbe16aadcd65e04860bea8ea50e05Aurimas Liutikas 621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport java.util.List; 631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell/** 651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * NestedScrollView is just like {@link android.widget.ScrollView}, but it supports acting 661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * as both a nested scrolling parent and child on both new and old versions of Android. 671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Nested scrolling is enabled by default. 681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 69bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapardpublic class NestedScrollView extends FrameLayout implements NestedScrollingParent2, 7076daed103193a1756535d1f59b165e98e1d17445Chris Banes NestedScrollingChild2, ScrollingView { 711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell static final int ANIMATED_SCROLL_GAP = 250; 721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell static final float MAX_SCROLL_FACTOR = 0.5f; 741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private static final String TAG = "NestedScrollView"; 761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 77cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes /** 78cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes * Interface definition for a callback to be invoked when the scroll 79cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes * X or Y positions of a view change. 80cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes * 81cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes * <p>This version of the interface works on all versions of Android, back to API v4.</p> 82cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes * 83cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes * @see #setOnScrollChangeListener(OnScrollChangeListener) 84cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes */ 85cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes public interface OnScrollChangeListener { 86cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes /** 87cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes * Called when the scroll position of a view changes. 88cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes * 89cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes * @param v The view whose scroll position has changed. 90cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes * @param scrollX Current horizontal scroll origin. 91cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes * @param scrollY Current vertical scroll origin. 92cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes * @param oldScrollX Previous horizontal scroll origin. 93cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes * @param oldScrollY Previous vertical scroll origin. 94cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes */ 95cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes void onScrollChange(NestedScrollView v, int scrollX, int scrollY, 96cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes int oldScrollX, int oldScrollY); 97cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes } 98cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes 991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private long mLastScroll; 1001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private final Rect mTempRect = new Rect(); 1023035be16658d7652fdc472f971c81d8f7ffb60fdAurimas Liutikas private OverScroller mScroller; 103506695b4cf6b22d70d2e3647d94551e721ee0430Aurimas Liutikas private EdgeEffect mEdgeGlowTop; 104506695b4cf6b22d70d2e3647d94551e721ee0430Aurimas Liutikas private EdgeEffect mEdgeGlowBottom; 1051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 1071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Position of the last motion event. 1081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 1091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private int mLastMotionY; 1101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 1121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * True when the layout has changed but the traversal has not come through yet. 1131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Ideally the view hierarchy would keep track of this for us. 1141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 1151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private boolean mIsLayoutDirty = true; 1161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private boolean mIsLaidOut = false; 1171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 1191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * The child to give focus to in the event that a child has requested focus while the 1201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * layout is dirty. This prevents the scroll from being wrong if the child has not been 1211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * laid out before requesting focus. 1221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 1231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private View mChildToScrollTo = null; 1241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 1261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * True if the user is currently dragging this ScrollView around. This is 1271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * not the same as 'is being flinged', which can be checked by 1281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * mScroller.isFinished() (flinging begins when the user lifts his finger). 1291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 1301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private boolean mIsBeingDragged = false; 1311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 1331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Determines speed during touch scrolling 1341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 1351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private VelocityTracker mVelocityTracker; 1361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 1381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * When set to true, the scroll view measure its child to make it fill the currently 1391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * visible area. 1401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 1411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private boolean mFillViewport; 1421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 1441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Whether arrow scrolling is animated. 1451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 1461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private boolean mSmoothScrollingEnabled = true; 1471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private int mTouchSlop; 1491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private int mMinimumVelocity; 1501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private int mMaximumVelocity; 1511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 1531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * ID of the active pointer. This is used to retain consistency during 1541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * drags/flings if multiple pointers are used. 1551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 1561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private int mActivePointerId = INVALID_POINTER; 1571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 1591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Used during scrolling to retrieve the new offset within the window. 1601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 1611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private final int[] mScrollOffset = new int[2]; 1621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private final int[] mScrollConsumed = new int[2]; 1631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private int mNestedYOffset; 1641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 16576daed103193a1756535d1f59b165e98e1d17445Chris Banes private int mLastScrollerY; 16676daed103193a1756535d1f59b165e98e1d17445Chris Banes 1671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 1681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Sentinel value for no current active pointer. 1691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Used by {@link #mActivePointerId}. 1701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 1711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private static final int INVALID_POINTER = -1; 1721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private SavedState mSavedState; 1741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private static final AccessibilityDelegate ACCESSIBILITY_DELEGATE = new AccessibilityDelegate(); 1761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private static final int[] SCROLLVIEW_STYLEABLE = new int[] { 1781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell android.R.attr.fillViewport 1791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell }; 1801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private final NestedScrollingParentHelper mParentHelper; 1821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private final NestedScrollingChildHelper mChildHelper; 1831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private float mVerticalScrollFactor; 1851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 186cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes private OnScrollChangeListener mOnScrollChangeListener; 187cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes 188852749684a10e7d104eed8fb2dcc68b64d079340Jake Wharton public NestedScrollView(@NonNull Context context) { 1891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell this(context, null); 1901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 1911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 192852749684a10e7d104eed8fb2dcc68b64d079340Jake Wharton public NestedScrollView(@NonNull Context context, @Nullable AttributeSet attrs) { 1931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell this(context, attrs, 0); 1941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 1951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 196852749684a10e7d104eed8fb2dcc68b64d079340Jake Wharton public NestedScrollView(@NonNull Context context, @Nullable AttributeSet attrs, 197852749684a10e7d104eed8fb2dcc68b64d079340Jake Wharton int defStyleAttr) { 1981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell super(context, attrs, defStyleAttr); 1991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell initScrollView(); 2001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 2011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final TypedArray a = context.obtainStyledAttributes( 2021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell attrs, SCROLLVIEW_STYLEABLE, defStyleAttr, 0); 2031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 2041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell setFillViewport(a.getBoolean(0, false)); 2051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 2061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell a.recycle(); 2071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 2081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mParentHelper = new NestedScrollingParentHelper(this); 2091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mChildHelper = new NestedScrollingChildHelper(this); 2101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 2111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // ...because why else would you be using this widget? 2121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell setNestedScrollingEnabled(true); 2131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 2141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell ViewCompat.setAccessibilityDelegate(this, ACCESSIBILITY_DELEGATE); 2151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 2161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 217bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard // NestedScrollingChild2 218bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard 219bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard @Override 220bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard public boolean startNestedScroll(int axes, int type) { 221bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard return mChildHelper.startNestedScroll(axes, type); 222bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard } 223bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard 224bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard @Override 225bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard public void stopNestedScroll(int type) { 226bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard mChildHelper.stopNestedScroll(type); 227bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard } 228bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard 229bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard @Override 230bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard public boolean hasNestedScrollingParent(int type) { 231bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard return mChildHelper.hasNestedScrollingParent(type); 232bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard } 233bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard 234bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard @Override 235bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, 236bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard int dyUnconsumed, int[] offsetInWindow, int type) { 237bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard return mChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, 238bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard offsetInWindow, type); 239bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard } 240bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard 241bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard @Override 242bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow, 243bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard int type) { 244bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard return mChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow, type); 245bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard } 246bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard 2471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // NestedScrollingChild 2481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 2491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 2501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void setNestedScrollingEnabled(boolean enabled) { 2511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mChildHelper.setNestedScrollingEnabled(enabled); 2521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 2531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 2541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 2551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public boolean isNestedScrollingEnabled() { 2561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return mChildHelper.isNestedScrollingEnabled(); 2571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 2581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 2591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 2601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public boolean startNestedScroll(int axes) { 261bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard return startNestedScroll(axes, ViewCompat.TYPE_TOUCH); 2621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 2631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 2641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 265bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard public void stopNestedScroll() { 266bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard stopNestedScroll(ViewCompat.TYPE_TOUCH); 26776daed103193a1756535d1f59b165e98e1d17445Chris Banes } 26876daed103193a1756535d1f59b165e98e1d17445Chris Banes 26976daed103193a1756535d1f59b165e98e1d17445Chris Banes @Override 270bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard public boolean hasNestedScrollingParent() { 271bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard return hasNestedScrollingParent(ViewCompat.TYPE_TOUCH); 2721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 2731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 2741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 275bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, 276bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard int dyUnconsumed, int[] offsetInWindow) { 277bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard return dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, 278bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard offsetInWindow, ViewCompat.TYPE_TOUCH); 27976daed103193a1756535d1f59b165e98e1d17445Chris Banes } 28076daed103193a1756535d1f59b165e98e1d17445Chris Banes 28176daed103193a1756535d1f59b165e98e1d17445Chris Banes @Override 282bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) { 283bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard return dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow, ViewCompat.TYPE_TOUCH); 2841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 2851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 2861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 287bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) { 288bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard return mChildHelper.dispatchNestedFling(velocityX, velocityY, consumed); 28976daed103193a1756535d1f59b165e98e1d17445Chris Banes } 29076daed103193a1756535d1f59b165e98e1d17445Chris Banes 29176daed103193a1756535d1f59b165e98e1d17445Chris Banes @Override 292bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard public boolean dispatchNestedPreFling(float velocityX, float velocityY) { 293bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard return mChildHelper.dispatchNestedPreFling(velocityX, velocityY); 2941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 2951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 296bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard // NestedScrollingParent2 297bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard 2981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 299bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard public boolean onStartNestedScroll(@NonNull View child, @NonNull View target, int axes, 300bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard int type) { 301bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard return (axes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0; 30276daed103193a1756535d1f59b165e98e1d17445Chris Banes } 30376daed103193a1756535d1f59b165e98e1d17445Chris Banes 30476daed103193a1756535d1f59b165e98e1d17445Chris Banes @Override 305bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard public void onNestedScrollAccepted(@NonNull View child, @NonNull View target, int axes, 306bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard int type) { 307bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard mParentHelper.onNestedScrollAccepted(child, target, axes, type); 308bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL, type); 3091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 3101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 3111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 312bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard public void onStopNestedScroll(@NonNull View target, int type) { 313bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard mParentHelper.onStopNestedScroll(target, type); 314bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard stopNestedScroll(type); 31576daed103193a1756535d1f59b165e98e1d17445Chris Banes } 31676daed103193a1756535d1f59b165e98e1d17445Chris Banes 31776daed103193a1756535d1f59b165e98e1d17445Chris Banes @Override 318bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, 319bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard int dyUnconsumed, int type) { 320bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard final int oldScrollY = getScrollY(); 321bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard scrollBy(0, dyUnconsumed); 322bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard final int myConsumed = getScrollY() - oldScrollY; 323bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard final int myUnconsumed = dyUnconsumed - myConsumed; 324bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard dispatchNestedScroll(0, myConsumed, 0, myUnconsumed, null, 325bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard type); 3261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 3271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 3281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 329bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard public void onNestedPreScroll(@NonNull View target, int dx, int dy, @NonNull int[] consumed, 330bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard int type) { 331bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard dispatchNestedPreScroll(dx, dy, consumed, null, type); 3321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 3331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 3341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // NestedScrollingParent 3351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 3361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 3371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) { 338bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard return onStartNestedScroll(child, target, nestedScrollAxes, ViewCompat.TYPE_TOUCH); 3391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 3401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 3411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 3421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) { 343bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard onNestedScrollAccepted(child, target, nestedScrollAxes, ViewCompat.TYPE_TOUCH); 3441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 3451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 3461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 3471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void onStopNestedScroll(View target) { 348bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard onStopNestedScroll(target, ViewCompat.TYPE_TOUCH); 3491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 3501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 3511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 3521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, 3531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int dyUnconsumed) { 354bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, 355bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard ViewCompat.TYPE_TOUCH); 3561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 3571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 3581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 3591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) { 360bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard onNestedPreScroll(target, dx, dy, consumed, ViewCompat.TYPE_TOUCH); 3611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 3621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 3631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 3641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) { 3651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (!consumed) { 3661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell flingWithNestedDispatch((int) velocityY); 3671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return true; 3681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 3691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return false; 3701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 3711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 3721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 3731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public boolean onNestedPreFling(View target, float velocityX, float velocityY) { 374d370f75e50bf31d348d4e424ba8477ac77635f70Chris Banes return dispatchNestedPreFling(velocityX, velocityY); 3751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 3761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 3771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 3781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public int getNestedScrollAxes() { 3791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return mParentHelper.getNestedScrollAxes(); 3801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 3811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 3821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // ScrollView import 3831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 384e2104f4b5c8e3ad63570306a25e61502dfe4c418Aurimas Liutikas @Override 3851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public boolean shouldDelayChildPressedState() { 3861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return true; 3871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 3881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 3891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 3901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell protected float getTopFadingEdgeStrength() { 3911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (getChildCount() == 0) { 3921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return 0.0f; 3931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 3941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 3951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int length = getVerticalFadingEdgeLength(); 3961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int scrollY = getScrollY(); 3971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (scrollY < length) { 3981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return scrollY / (float) length; 3991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 4001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 4011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return 1.0f; 4021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 4031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 4041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 4051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell protected float getBottomFadingEdgeStrength() { 4061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (getChildCount() == 0) { 4071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return 0.0f; 4081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 4091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 4101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int length = getVerticalFadingEdgeLength(); 4111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int bottomEdge = getHeight() - getPaddingBottom(); 4121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int span = getChildAt(0).getBottom() - getScrollY() - bottomEdge; 4131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (span < length) { 4141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return span / (float) length; 4151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 4161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 4171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return 1.0f; 4181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 4191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 4201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 4211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @return The maximum amount this scroll view will scroll in response to 4221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * an arrow event. 4231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 4241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public int getMaxScrollAmount() { 4251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return (int) (MAX_SCROLL_FACTOR * getHeight()); 4261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 4271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 4281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private void initScrollView() { 4293035be16658d7652fdc472f971c81d8f7ffb60fdAurimas Liutikas mScroller = new OverScroller(getContext()); 4301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell setFocusable(true); 4311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); 4321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell setWillNotDraw(false); 4331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final ViewConfiguration configuration = ViewConfiguration.get(getContext()); 4341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mTouchSlop = configuration.getScaledTouchSlop(); 4351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mMinimumVelocity = configuration.getScaledMinimumFlingVelocity(); 4361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); 4371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 4381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 4391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 4401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void addView(View child) { 4411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (getChildCount() > 0) { 4421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell throw new IllegalStateException("ScrollView can host only one direct child"); 4431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 4441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 4451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell super.addView(child); 4461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 4471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 4481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 4491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void addView(View child, int index) { 4501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (getChildCount() > 0) { 4511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell throw new IllegalStateException("ScrollView can host only one direct child"); 4521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 4531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 4541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell super.addView(child, index); 4551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 4561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 4571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 4581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void addView(View child, ViewGroup.LayoutParams params) { 4591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (getChildCount() > 0) { 4601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell throw new IllegalStateException("ScrollView can host only one direct child"); 4611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 4621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 4631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell super.addView(child, params); 4641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 4651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 4661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 4671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void addView(View child, int index, ViewGroup.LayoutParams params) { 4681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (getChildCount() > 0) { 4691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell throw new IllegalStateException("ScrollView can host only one direct child"); 4701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 4711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 4721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell super.addView(child, index, params); 4731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 4741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 4751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 476cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes * Register a callback to be invoked when the scroll X or Y positions of 477cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes * this view change. 478cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes * <p>This version of the method works on all versions of Android, back to API v4.</p> 479cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes * 480cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes * @param l The listener to notify when the scroll X or Y position changes. 481cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes * @see android.view.View#getScrollX() 482cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes * @see android.view.View#getScrollY() 483cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes */ 484852749684a10e7d104eed8fb2dcc68b64d079340Jake Wharton public void setOnScrollChangeListener(@Nullable OnScrollChangeListener l) { 485cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes mOnScrollChangeListener = l; 486cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes } 487cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes 488cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes /** 4891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @return Returns true this ScrollView can be scrolled 4901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 4911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private boolean canScroll() { 4921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell View child = getChildAt(0); 4931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (child != null) { 4941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int childHeight = child.getHeight(); 4951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return getHeight() < childHeight + getPaddingTop() + getPaddingBottom(); 4961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 4971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return false; 4981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 4991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 5001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 5011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Indicates whether this ScrollView's content is stretched to fill the viewport. 5021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * 5031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @return True if the content fills the viewport, false otherwise. 5041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * 505929f27aab7ac7231f3734c988d5ee7201627d535Alan Viverette * @attr name android:fillViewport 5061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 5071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public boolean isFillViewport() { 5081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return mFillViewport; 5091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 5101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 5111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 512236bf5bc5041e4bf356eac3855fd4fcf72cd6994Aurimas Liutikas * Set whether this ScrollView should stretch its content height to fill the viewport or not. 5131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * 5141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param fillViewport True to stretch the content's height to the viewport's 5151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * boundaries, false otherwise. 5161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * 517929f27aab7ac7231f3734c988d5ee7201627d535Alan Viverette * @attr name android:fillViewport 5181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 5191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void setFillViewport(boolean fillViewport) { 5201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (fillViewport != mFillViewport) { 5211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mFillViewport = fillViewport; 5221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell requestLayout(); 5231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 5241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 5251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 5261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 5271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @return Whether arrow scrolling will animate its transition. 5281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 5291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public boolean isSmoothScrollingEnabled() { 5301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return mSmoothScrollingEnabled; 5311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 5321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 5331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 5341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Set whether arrow scrolling will animate its transition. 5351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param smoothScrollingEnabled whether arrow scrolling will animate its transition 5361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 5371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void setSmoothScrollingEnabled(boolean smoothScrollingEnabled) { 5381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mSmoothScrollingEnabled = smoothScrollingEnabled; 5391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 5401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 5411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 542cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes protected void onScrollChanged(int l, int t, int oldl, int oldt) { 543cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes super.onScrollChanged(l, t, oldl, oldt); 544cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes 545cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes if (mOnScrollChangeListener != null) { 546cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes mOnScrollChangeListener.onScrollChange(this, l, t, oldl, oldt); 547cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes } 548cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes } 549cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes 550cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes @Override 5511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 5521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell super.onMeasure(widthMeasureSpec, heightMeasureSpec); 5531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 5541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (!mFillViewport) { 5551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return; 5561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 5571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 5581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int heightMode = MeasureSpec.getMode(heightMeasureSpec); 5591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (heightMode == MeasureSpec.UNSPECIFIED) { 5601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return; 5611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 5621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 5631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (getChildCount() > 0) { 5641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final View child = getChildAt(0); 5651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int height = getMeasuredHeight(); 5661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (child.getMeasuredHeight() < height) { 5671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final FrameLayout.LayoutParams lp = (LayoutParams) child.getLayoutParams(); 5681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 5691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 5701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell getPaddingLeft() + getPaddingRight(), lp.width); 5711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell height -= getPaddingTop(); 5721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell height -= getPaddingBottom(); 5731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int childHeightMeasureSpec = 5741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); 5751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 5761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 5771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 5781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 5791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 5801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 5811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 5821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public boolean dispatchKeyEvent(KeyEvent event) { 5831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // Let the focused view and/or our descendants get the key first 5841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return super.dispatchKeyEvent(event) || executeKeyEvent(event); 5851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 5861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 5871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 5881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * You can call this function yourself to have the scroll view perform 5891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * scrolling from a key event, just as if the event had been dispatched to 5901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * it by the view hierarchy. 5911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * 5921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param event The key event to execute. 5931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @return Return true if the event was handled, else false. 5941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 595852749684a10e7d104eed8fb2dcc68b64d079340Jake Wharton public boolean executeKeyEvent(@NonNull KeyEvent event) { 5961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mTempRect.setEmpty(); 5971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 5981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (!canScroll()) { 5991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (isFocused() && event.getKeyCode() != KeyEvent.KEYCODE_BACK) { 6001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell View currentFocused = findFocus(); 6011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (currentFocused == this) currentFocused = null; 6021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell View nextFocused = FocusFinder.getInstance().findNextFocus(this, 6031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell currentFocused, View.FOCUS_DOWN); 6041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return nextFocused != null 6051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell && nextFocused != this 6061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell && nextFocused.requestFocus(View.FOCUS_DOWN); 6071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 6081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return false; 6091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 6101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 6111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell boolean handled = false; 6121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (event.getAction() == KeyEvent.ACTION_DOWN) { 6131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell switch (event.getKeyCode()) { 6141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell case KeyEvent.KEYCODE_DPAD_UP: 6151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (!event.isAltPressed()) { 6161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell handled = arrowScroll(View.FOCUS_UP); 6171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else { 6181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell handled = fullScroll(View.FOCUS_UP); 6191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 6201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell break; 6211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell case KeyEvent.KEYCODE_DPAD_DOWN: 6221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (!event.isAltPressed()) { 6231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell handled = arrowScroll(View.FOCUS_DOWN); 6241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else { 6251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell handled = fullScroll(View.FOCUS_DOWN); 6261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 6271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell break; 6281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell case KeyEvent.KEYCODE_SPACE: 6291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell pageScroll(event.isShiftPressed() ? View.FOCUS_UP : View.FOCUS_DOWN); 6301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell break; 6311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 6321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 6331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 6341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return handled; 6351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 6361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 6371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private boolean inChild(int x, int y) { 6381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (getChildCount() > 0) { 6391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int scrollY = getScrollY(); 6401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final View child = getChildAt(0); 6411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return !(y < child.getTop() - scrollY 6421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell || y >= child.getBottom() - scrollY 6431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell || x < child.getLeft() 6441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell || x >= child.getRight()); 6451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 6461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return false; 6471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 6481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 6491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private void initOrResetVelocityTracker() { 6501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (mVelocityTracker == null) { 6511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mVelocityTracker = VelocityTracker.obtain(); 6521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else { 6531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mVelocityTracker.clear(); 6541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 6551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 6561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 6571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private void initVelocityTrackerIfNotExists() { 6581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (mVelocityTracker == null) { 6591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mVelocityTracker = VelocityTracker.obtain(); 6601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 6611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 6621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 6631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private void recycleVelocityTracker() { 6641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (mVelocityTracker != null) { 6651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mVelocityTracker.recycle(); 6661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mVelocityTracker = null; 6671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 6681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 6691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 6701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 6711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { 6721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (disallowIntercept) { 6731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell recycleVelocityTracker(); 6741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 6751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell super.requestDisallowInterceptTouchEvent(disallowIntercept); 6761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 6771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 6781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 6791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public boolean onInterceptTouchEvent(MotionEvent ev) { 6801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /* 6811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * This method JUST determines whether we want to intercept the motion. 6821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * If we return true, onMotionEvent will be called and we do the actual 6831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * scrolling there. 6841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 6851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 6861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /* 6871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Shortcut the most recurring case: the user is in the dragging 6881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * state and he is moving his finger. We want to intercept this 6891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * motion. 6901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 6911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int action = ev.getAction(); 6921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) { 6931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return true; 6941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 6951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 6966ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas switch (action & MotionEvent.ACTION_MASK) { 6971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell case MotionEvent.ACTION_MOVE: { 6981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /* 6991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check 7001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * whether the user has moved far enough from his original down touch. 7011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 7021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 7031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /* 7041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Locally do absolute value. mLastMotionY is set to the y value 7051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * of the down event. 7061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 7071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int activePointerId = mActivePointerId; 7081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (activePointerId == INVALID_POINTER) { 7091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // If we don't have a valid id, the touch down wasn't on content. 7101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell break; 7111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 7121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 7131b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov final int pointerIndex = ev.findPointerIndex(activePointerId); 7141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (pointerIndex == -1) { 7151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell Log.e(TAG, "Invalid pointerId=" + activePointerId 7161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell + " in onInterceptTouchEvent"); 7171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell break; 7181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 7191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 7201b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov final int y = (int) ev.getY(pointerIndex); 7211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int yDiff = Math.abs(y - mLastMotionY); 7221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (yDiff > mTouchSlop 7231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell && (getNestedScrollAxes() & ViewCompat.SCROLL_AXIS_VERTICAL) == 0) { 7241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mIsBeingDragged = true; 7251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mLastMotionY = y; 7261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell initVelocityTrackerIfNotExists(); 7271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mVelocityTracker.addMovement(ev); 7281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mNestedYOffset = 0; 7291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final ViewParent parent = getParent(); 7301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (parent != null) { 7311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell parent.requestDisallowInterceptTouchEvent(true); 7321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 7331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 7341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell break; 7351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 7361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 7371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell case MotionEvent.ACTION_DOWN: { 7381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int y = (int) ev.getY(); 73976daed103193a1756535d1f59b165e98e1d17445Chris Banes if (!inChild((int) ev.getX(), y)) { 7401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mIsBeingDragged = false; 7411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell recycleVelocityTracker(); 7421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell break; 7431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 7441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 7451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /* 7461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Remember location of down touch. 7471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * ACTION_DOWN always refers to pointer index 0. 7481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 7491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mLastMotionY = y; 7501b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov mActivePointerId = ev.getPointerId(0); 7511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 7521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell initOrResetVelocityTracker(); 7531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mVelocityTracker.addMovement(ev); 7541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /* 755661d25de05417a357540283ee0cd93b1326ac3a4Chris Banes * If being flinged and user touches the screen, initiate drag; 756661d25de05417a357540283ee0cd93b1326ac3a4Chris Banes * otherwise don't. mScroller.isFinished should be false when 757661d25de05417a357540283ee0cd93b1326ac3a4Chris Banes * being flinged. We need to call computeScrollOffset() first so that 758661d25de05417a357540283ee0cd93b1326ac3a4Chris Banes * isFinished() is correct. 7591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 760661d25de05417a357540283ee0cd93b1326ac3a4Chris Banes mScroller.computeScrollOffset(); 7611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mIsBeingDragged = !mScroller.isFinished(); 76276daed103193a1756535d1f59b165e98e1d17445Chris Banes startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_TOUCH); 7631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell break; 7641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 7651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 7661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell case MotionEvent.ACTION_CANCEL: 7671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell case MotionEvent.ACTION_UP: 7681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /* Release the drag */ 7691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mIsBeingDragged = false; 7701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mActivePointerId = INVALID_POINTER; 7711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell recycleVelocityTracker(); 77200db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes if (mScroller.springBack(getScrollX(), getScrollY(), 0, 0, 0, getScrollRange())) { 77300db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes ViewCompat.postInvalidateOnAnimation(this); 77400db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes } 77576daed103193a1756535d1f59b165e98e1d17445Chris Banes stopNestedScroll(ViewCompat.TYPE_TOUCH); 7761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell break; 7776ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas case MotionEvent.ACTION_POINTER_UP: 7781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell onSecondaryPointerUp(ev); 7791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell break; 7801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 7811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 7821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /* 7831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * The only time we want to intercept motion events is if we are in the 7841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * drag mode. 7851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 7861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return mIsBeingDragged; 7871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 7881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 7891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 7901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public boolean onTouchEvent(MotionEvent ev) { 7911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell initVelocityTrackerIfNotExists(); 7921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 7931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell MotionEvent vtev = MotionEvent.obtain(ev); 7941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 7956ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas final int actionMasked = ev.getActionMasked(); 7961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 7971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (actionMasked == MotionEvent.ACTION_DOWN) { 7981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mNestedYOffset = 0; 7991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 8001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell vtev.offsetLocation(0, mNestedYOffset); 8011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 8021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell switch (actionMasked) { 8031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell case MotionEvent.ACTION_DOWN: { 8041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (getChildCount() == 0) { 8051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return false; 8061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 8071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if ((mIsBeingDragged = !mScroller.isFinished())) { 8081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final ViewParent parent = getParent(); 8091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (parent != null) { 8101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell parent.requestDisallowInterceptTouchEvent(true); 8111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 8121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 8131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 8141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /* 8151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * If being flinged and user touches, stop the fling. isFinished 8161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * will be false if being flinged. 8171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 8181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (!mScroller.isFinished()) { 8191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mScroller.abortAnimation(); 8201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 8211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 8221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // Remember where the motion event started 8231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mLastMotionY = (int) ev.getY(); 8241b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov mActivePointerId = ev.getPointerId(0); 82576daed103193a1756535d1f59b165e98e1d17445Chris Banes startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_TOUCH); 8261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell break; 8271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 8281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell case MotionEvent.ACTION_MOVE: 8291b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov final int activePointerIndex = ev.findPointerIndex(mActivePointerId); 8301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (activePointerIndex == -1) { 8311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent"); 8321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell break; 8331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 8341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 8351b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov final int y = (int) ev.getY(activePointerIndex); 8361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int deltaY = mLastMotionY - y; 83776daed103193a1756535d1f59b165e98e1d17445Chris Banes if (dispatchNestedPreScroll(0, deltaY, mScrollConsumed, mScrollOffset, 83876daed103193a1756535d1f59b165e98e1d17445Chris Banes ViewCompat.TYPE_TOUCH)) { 8391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell deltaY -= mScrollConsumed[1]; 8401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell vtev.offsetLocation(0, mScrollOffset[1]); 8411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mNestedYOffset += mScrollOffset[1]; 8421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 8431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (!mIsBeingDragged && Math.abs(deltaY) > mTouchSlop) { 8441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final ViewParent parent = getParent(); 8451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (parent != null) { 8461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell parent.requestDisallowInterceptTouchEvent(true); 8471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 8481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mIsBeingDragged = true; 8491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (deltaY > 0) { 8501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell deltaY -= mTouchSlop; 8511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else { 8521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell deltaY += mTouchSlop; 8531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 8541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 8551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (mIsBeingDragged) { 8561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // Scroll to follow the motion event 8571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mLastMotionY = y - mScrollOffset[1]; 8581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 8591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int oldY = getScrollY(); 8601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int range = getScrollRange(); 861e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas final int overscrollMode = getOverScrollMode(); 862e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas boolean canOverscroll = overscrollMode == View.OVER_SCROLL_ALWAYS 863e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas || (overscrollMode == View.OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0); 8641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 8651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // Calling overScrollByCompat will call onOverScrolled, which 8661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // calls onScrollChanged if applicable. 8671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (overScrollByCompat(0, deltaY, 0, getScrollY(), 0, range, 0, 86876daed103193a1756535d1f59b165e98e1d17445Chris Banes 0, true) && !hasNestedScrollingParent(ViewCompat.TYPE_TOUCH)) { 8691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // Break our velocity if we hit a scroll barrier. 8701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mVelocityTracker.clear(); 8711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 8721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 8731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int scrolledDeltaY = getScrollY() - oldY; 8741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int unconsumedY = deltaY - scrolledDeltaY; 87576daed103193a1756535d1f59b165e98e1d17445Chris Banes if (dispatchNestedScroll(0, scrolledDeltaY, 0, unconsumedY, mScrollOffset, 87676daed103193a1756535d1f59b165e98e1d17445Chris Banes ViewCompat.TYPE_TOUCH)) { 8771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mLastMotionY -= mScrollOffset[1]; 8781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell vtev.offsetLocation(0, mScrollOffset[1]); 8791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mNestedYOffset += mScrollOffset[1]; 8801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else if (canOverscroll) { 8811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell ensureGlows(); 8821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int pulledToY = oldY + deltaY; 8831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (pulledToY < 0) { 884506695b4cf6b22d70d2e3647d94551e721ee0430Aurimas Liutikas EdgeEffectCompat.onPull(mEdgeGlowTop, (float) deltaY / getHeight(), 8851b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov ev.getX(activePointerIndex) / getWidth()); 8861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (!mEdgeGlowBottom.isFinished()) { 8871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mEdgeGlowBottom.onRelease(); 8881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 8891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else if (pulledToY > range) { 890506695b4cf6b22d70d2e3647d94551e721ee0430Aurimas Liutikas EdgeEffectCompat.onPull(mEdgeGlowBottom, (float) deltaY / getHeight(), 8911b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov 1.f - ev.getX(activePointerIndex) 8921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell / getWidth()); 8931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (!mEdgeGlowTop.isFinished()) { 8941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mEdgeGlowTop.onRelease(); 8951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 8961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 8971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (mEdgeGlowTop != null 8981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell && (!mEdgeGlowTop.isFinished() || !mEdgeGlowBottom.isFinished())) { 8991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell ViewCompat.postInvalidateOnAnimation(this); 9001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 9011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 9021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 9031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell break; 9041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell case MotionEvent.ACTION_UP: 90576daed103193a1756535d1f59b165e98e1d17445Chris Banes final VelocityTracker velocityTracker = mVelocityTracker; 90676daed103193a1756535d1f59b165e98e1d17445Chris Banes velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); 90776daed103193a1756535d1f59b165e98e1d17445Chris Banes int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId); 90876daed103193a1756535d1f59b165e98e1d17445Chris Banes if ((Math.abs(initialVelocity) > mMinimumVelocity)) { 90976daed103193a1756535d1f59b165e98e1d17445Chris Banes flingWithNestedDispatch(-initialVelocity); 91076daed103193a1756535d1f59b165e98e1d17445Chris Banes } else if (mScroller.springBack(getScrollX(), getScrollY(), 0, 0, 0, 91176daed103193a1756535d1f59b165e98e1d17445Chris Banes getScrollRange())) { 91276daed103193a1756535d1f59b165e98e1d17445Chris Banes ViewCompat.postInvalidateOnAnimation(this); 9131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 914ba3bd1923ffd1ef5597deb0a9df8c1444fbf4433Chris Banes mActivePointerId = INVALID_POINTER; 915ba3bd1923ffd1ef5597deb0a9df8c1444fbf4433Chris Banes endDrag(); 9161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell break; 9171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell case MotionEvent.ACTION_CANCEL: 9181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (mIsBeingDragged && getChildCount() > 0) { 91900db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes if (mScroller.springBack(getScrollX(), getScrollY(), 0, 0, 0, 92000db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes getScrollRange())) { 92100db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes ViewCompat.postInvalidateOnAnimation(this); 92200db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes } 9231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 924ba3bd1923ffd1ef5597deb0a9df8c1444fbf4433Chris Banes mActivePointerId = INVALID_POINTER; 925ba3bd1923ffd1ef5597deb0a9df8c1444fbf4433Chris Banes endDrag(); 9261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell break; 9276ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas case MotionEvent.ACTION_POINTER_DOWN: { 9286ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas final int index = ev.getActionIndex(); 9291b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov mLastMotionY = (int) ev.getY(index); 9301b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov mActivePointerId = ev.getPointerId(index); 9311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell break; 9321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 9336ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas case MotionEvent.ACTION_POINTER_UP: 9341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell onSecondaryPointerUp(ev); 9351b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId)); 9361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell break; 9371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 9381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 9391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (mVelocityTracker != null) { 9401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mVelocityTracker.addMovement(vtev); 9411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 9421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell vtev.recycle(); 9431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return true; 9441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 9451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 9461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private void onSecondaryPointerUp(MotionEvent ev) { 9476ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas final int pointerIndex = ev.getActionIndex(); 9481b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov final int pointerId = ev.getPointerId(pointerIndex); 9491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (pointerId == mActivePointerId) { 9501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // This was our active pointer going up. Choose a new 9511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // active pointer and adjust accordingly. 9521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // TODO: Make this decision more intelligent. 9531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int newPointerIndex = pointerIndex == 0 ? 1 : 0; 9541b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov mLastMotionY = (int) ev.getY(newPointerIndex); 9551b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov mActivePointerId = ev.getPointerId(newPointerIndex); 9561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (mVelocityTracker != null) { 9571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mVelocityTracker.clear(); 9581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 9591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 9601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 9611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 962e2104f4b5c8e3ad63570306a25e61502dfe4c418Aurimas Liutikas @Override 9631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public boolean onGenericMotionEvent(MotionEvent event) { 9641b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov if ((event.getSource() & InputDeviceCompat.SOURCE_CLASS_POINTER) != 0) { 9651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell switch (event.getAction()) { 9666ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas case MotionEvent.ACTION_SCROLL: { 9671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (!mIsBeingDragged) { 9686ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas final float vscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL); 9691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (vscroll != 0) { 9701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int delta = (int) (vscroll * getVerticalScrollFactorCompat()); 9711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int range = getScrollRange(); 9721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int oldScrollY = getScrollY(); 9731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int newScrollY = oldScrollY - delta; 9741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (newScrollY < 0) { 9751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell newScrollY = 0; 9761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else if (newScrollY > range) { 9771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell newScrollY = range; 9781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 9791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (newScrollY != oldScrollY) { 9801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell super.scrollTo(getScrollX(), newScrollY); 9811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return true; 9821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 9831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 9841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 9851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 9861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 9871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 9881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return false; 9891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 9901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 9911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private float getVerticalScrollFactorCompat() { 9921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (mVerticalScrollFactor == 0) { 9931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell TypedValue outValue = new TypedValue(); 9941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final Context context = getContext(); 9951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (!context.getTheme().resolveAttribute( 9961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell android.R.attr.listPreferredItemHeight, outValue, true)) { 9971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell throw new IllegalStateException( 9981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell "Expected theme to define listPreferredItemHeight."); 9991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 10001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mVerticalScrollFactor = outValue.getDimension( 10011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell context.getResources().getDisplayMetrics()); 10021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 10031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return mVerticalScrollFactor; 10041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 10051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 100615375aa6fd54b036f97f99229aefab2822c8a1c9Aurimas Liutikas @Override 10071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell protected void onOverScrolled(int scrollX, int scrollY, 10081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell boolean clampedX, boolean clampedY) { 10091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell super.scrollTo(scrollX, scrollY); 10101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 10111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 10121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell boolean overScrollByCompat(int deltaX, int deltaY, 10131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int scrollX, int scrollY, 10141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int scrollRangeX, int scrollRangeY, 10151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int maxOverScrollX, int maxOverScrollY, 10161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell boolean isTouchEvent) { 1017e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas final int overScrollMode = getOverScrollMode(); 10181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final boolean canScrollHorizontal = 10191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell computeHorizontalScrollRange() > computeHorizontalScrollExtent(); 10201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final boolean canScrollVertical = 10211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell computeVerticalScrollRange() > computeVerticalScrollExtent(); 1022e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas final boolean overScrollHorizontal = overScrollMode == View.OVER_SCROLL_ALWAYS 1023e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas || (overScrollMode == View.OVER_SCROLL_IF_CONTENT_SCROLLS && canScrollHorizontal); 1024e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas final boolean overScrollVertical = overScrollMode == View.OVER_SCROLL_ALWAYS 1025e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas || (overScrollMode == View.OVER_SCROLL_IF_CONTENT_SCROLLS && canScrollVertical); 10261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 10271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int newScrollX = scrollX + deltaX; 10281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (!overScrollHorizontal) { 10291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell maxOverScrollX = 0; 10301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 10311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 10321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int newScrollY = scrollY + deltaY; 10331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (!overScrollVertical) { 10341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell maxOverScrollY = 0; 10351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 10361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 10371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // Clamp values if at the limits and record 10381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int left = -maxOverScrollX; 10391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int right = maxOverScrollX + scrollRangeX; 10401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int top = -maxOverScrollY; 10411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int bottom = maxOverScrollY + scrollRangeY; 10421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 10431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell boolean clampedX = false; 10441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (newScrollX > right) { 10451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell newScrollX = right; 10461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell clampedX = true; 10471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else if (newScrollX < left) { 10481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell newScrollX = left; 10491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell clampedX = true; 10501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 10511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 10521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell boolean clampedY = false; 10531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (newScrollY > bottom) { 10541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell newScrollY = bottom; 10551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell clampedY = true; 10561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else if (newScrollY < top) { 10571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell newScrollY = top; 10581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell clampedY = true; 10591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 10601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 106176daed103193a1756535d1f59b165e98e1d17445Chris Banes if (clampedY && !hasNestedScrollingParent(ViewCompat.TYPE_NON_TOUCH)) { 106200db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes mScroller.springBack(newScrollX, newScrollY, 0, 0, 0, getScrollRange()); 106300db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes } 106400db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes 10651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell onOverScrolled(newScrollX, newScrollY, clampedX, clampedY); 10661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 10671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return clampedX || clampedY; 10681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 10691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1070540222c3675801eaa141ace1c164c4d3499b4721Aurimas Liutikas int getScrollRange() { 10711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int scrollRange = 0; 10721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (getChildCount() > 0) { 10731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell View child = getChildAt(0); 10741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollRange = Math.max(0, 10751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell child.getHeight() - (getHeight() - getPaddingBottom() - getPaddingTop())); 10761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 10771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return scrollRange; 10781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 10791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 10801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 10811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * <p> 10821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Finds the next focusable component that fits in the specified bounds. 10831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * </p> 10841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * 10851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param topFocus look for a candidate is the one at the top of the bounds 10861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * if topFocus is true, or at the bottom of the bounds if topFocus is 10871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * false 10881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param top the top offset of the bounds in which a focusable must be 10891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * found 10901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param bottom the bottom offset of the bounds in which a focusable must 10911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * be found 10921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @return the next focusable component in the bounds or null if none can 10931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * be found 10941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 10951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private View findFocusableViewInBounds(boolean topFocus, int top, int bottom) { 10961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 10971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell List<View> focusables = getFocusables(View.FOCUS_FORWARD); 10981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell View focusCandidate = null; 10991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 11001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /* 11011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * A fully contained focusable is one where its top is below the bound's 11021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * top, and its bottom is above the bound's bottom. A partially 11031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * contained focusable is one where some part of it is within the 11041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * bounds, but it also has some part that is not within bounds. A fully contained 11051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * focusable is preferred to a partially contained focusable. 11061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 11071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell boolean foundFullyContainedFocusable = false; 11081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 11091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int count = focusables.size(); 11101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell for (int i = 0; i < count; i++) { 11111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell View view = focusables.get(i); 11121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int viewTop = view.getTop(); 11131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int viewBottom = view.getBottom(); 11141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 11151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (top < viewBottom && viewTop < bottom) { 11161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /* 11171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * the focusable is in the target area, it is a candidate for 11181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * focusing 11191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 11201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 11214d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas final boolean viewIsFullyContained = (top < viewTop) && (viewBottom < bottom); 11221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 11231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (focusCandidate == null) { 11241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /* No candidate, take this one */ 11251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell focusCandidate = view; 11261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell foundFullyContainedFocusable = viewIsFullyContained; 11271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else { 11281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final boolean viewIsCloserToBoundary = 11294d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas (topFocus && viewTop < focusCandidate.getTop()) 11304d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas || (!topFocus && viewBottom > focusCandidate.getBottom()); 11311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 11321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (foundFullyContainedFocusable) { 11331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (viewIsFullyContained && viewIsCloserToBoundary) { 11341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /* 11351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * We're dealing with only fully contained views, so 11361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * it has to be closer to the boundary to beat our 11371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * candidate 11381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 11391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell focusCandidate = view; 11401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 11411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else { 11421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (viewIsFullyContained) { 11431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /* Any fully contained view beats a partially contained view */ 11441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell focusCandidate = view; 11451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell foundFullyContainedFocusable = true; 11461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else if (viewIsCloserToBoundary) { 11471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /* 11481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Partially contained view beats another partially 11491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * contained view if it's closer 11501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 11511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell focusCandidate = view; 11521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 11531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 11541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 11551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 11561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 11571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 11581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return focusCandidate; 11591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 11601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 11611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 11621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * <p>Handles scrolling in response to a "page up/down" shortcut press. This 11631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * method will scroll the view by one page up or down and give the focus 11641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * to the topmost/bottommost component in the new visible area. If no 11651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * component is a good candidate for focus, this scrollview reclaims the 11661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * focus.</p> 11671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * 11681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param direction the scroll direction: {@link android.view.View#FOCUS_UP} 11691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * to go one page up or 11701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * {@link android.view.View#FOCUS_DOWN} to go one page down 11711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @return true if the key event is consumed by this method, false otherwise 11721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 11731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public boolean pageScroll(int direction) { 11741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell boolean down = direction == View.FOCUS_DOWN; 11751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int height = getHeight(); 11761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 11771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (down) { 11781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mTempRect.top = getScrollY() + height; 11791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int count = getChildCount(); 11801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (count > 0) { 11811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell View view = getChildAt(count - 1); 11821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (mTempRect.top + height > view.getBottom()) { 11831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mTempRect.top = view.getBottom() - height; 11841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 11851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 11861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else { 11871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mTempRect.top = getScrollY() - height; 11881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (mTempRect.top < 0) { 11891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mTempRect.top = 0; 11901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 11911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 11921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mTempRect.bottom = mTempRect.top + height; 11931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 11941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return scrollAndFocus(direction, mTempRect.top, mTempRect.bottom); 11951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 11961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 11971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 11981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * <p>Handles scrolling in response to a "home/end" shortcut press. This 11991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * method will scroll the view to the top or bottom and give the focus 12001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * to the topmost/bottommost component in the new visible area. If no 12011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * component is a good candidate for focus, this scrollview reclaims the 12021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * focus.</p> 12031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * 12041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param direction the scroll direction: {@link android.view.View#FOCUS_UP} 12051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * to go the top of the view or 12061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * {@link android.view.View#FOCUS_DOWN} to go the bottom 12071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @return true if the key event is consumed by this method, false otherwise 12081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 12091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public boolean fullScroll(int direction) { 12101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell boolean down = direction == View.FOCUS_DOWN; 12111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int height = getHeight(); 12121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 12131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mTempRect.top = 0; 12141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mTempRect.bottom = height; 12151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 12161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (down) { 12171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int count = getChildCount(); 12181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (count > 0) { 12191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell View view = getChildAt(count - 1); 12201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mTempRect.bottom = view.getBottom() + getPaddingBottom(); 12211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mTempRect.top = mTempRect.bottom - height; 12221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 12231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 12241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 12251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return scrollAndFocus(direction, mTempRect.top, mTempRect.bottom); 12261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 12271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 12281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 12291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * <p>Scrolls the view to make the area defined by <code>top</code> and 12301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * <code>bottom</code> visible. This method attempts to give the focus 12311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * to a component visible in this area. If no component can be focused in 12321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * the new visible area, the focus is reclaimed by this ScrollView.</p> 12331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * 12341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param direction the scroll direction: {@link android.view.View#FOCUS_UP} 12351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * to go upward, {@link android.view.View#FOCUS_DOWN} to downward 12361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param top the top offset of the new area to be made visible 12371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param bottom the bottom offset of the new area to be made visible 12381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @return true if the key event is consumed by this method, false otherwise 12391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 12401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private boolean scrollAndFocus(int direction, int top, int bottom) { 12411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell boolean handled = true; 12421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 12431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int height = getHeight(); 12441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int containerTop = getScrollY(); 12451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int containerBottom = containerTop + height; 12461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell boolean up = direction == View.FOCUS_UP; 12471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 12481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell View newFocused = findFocusableViewInBounds(up, top, bottom); 12491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (newFocused == null) { 12501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell newFocused = this; 12511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 12521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 12531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (top >= containerTop && bottom <= containerBottom) { 12541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell handled = false; 12551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else { 12561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int delta = up ? (top - containerTop) : (bottom - containerBottom); 12571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell doScrollY(delta); 12581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 12591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 12601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (newFocused != findFocus()) newFocused.requestFocus(direction); 12611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 12621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return handled; 12631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 12641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 12651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 12661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Handle scrolling in response to an up or down arrow click. 12671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * 12681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param direction The direction corresponding to the arrow key that was 12691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * pressed 12701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @return True if we consumed the event, false otherwise 12711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 12721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public boolean arrowScroll(int direction) { 12731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 12741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell View currentFocused = findFocus(); 12751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (currentFocused == this) currentFocused = null; 12761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 12771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused, direction); 12781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 12791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int maxJump = getMaxScrollAmount(); 12801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 12811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (nextFocused != null && isWithinDeltaOfScreen(nextFocused, maxJump, getHeight())) { 12821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell nextFocused.getDrawingRect(mTempRect); 12831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell offsetDescendantRectToMyCoords(nextFocused, mTempRect); 12841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect); 12851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell doScrollY(scrollDelta); 12861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell nextFocused.requestFocus(direction); 12871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else { 12881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // no new focus 12891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int scrollDelta = maxJump; 12901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 12911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (direction == View.FOCUS_UP && getScrollY() < scrollDelta) { 12921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollDelta = getScrollY(); 12931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else if (direction == View.FOCUS_DOWN) { 12941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (getChildCount() > 0) { 12951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int daBottom = getChildAt(0).getBottom(); 12961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int screenBottom = getScrollY() + getHeight() - getPaddingBottom(); 12971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (daBottom - screenBottom < maxJump) { 12981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollDelta = daBottom - screenBottom; 12991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 13001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 13011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 13021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (scrollDelta == 0) { 13031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return false; 13041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 13051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell doScrollY(direction == View.FOCUS_DOWN ? scrollDelta : -scrollDelta); 13061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 13071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 13081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (currentFocused != null && currentFocused.isFocused() 13091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell && isOffScreen(currentFocused)) { 13101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // previously focused item still has focus and is off screen, give 13111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // it up (take it back to ourselves) 13121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // (also, need to temporarily force FOCUS_BEFORE_DESCENDANTS so we are 13131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // sure to 13141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // get it) 13151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int descendantFocusability = getDescendantFocusability(); // save 13161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS); 13171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell requestFocus(); 13181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell setDescendantFocusability(descendantFocusability); // restore 13191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 13201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return true; 13211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 13221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 13231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 13241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @return whether the descendant of this scroll view is scrolled off 13251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * screen. 13261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 13271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private boolean isOffScreen(View descendant) { 13281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return !isWithinDeltaOfScreen(descendant, 0, getHeight()); 13291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 13301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 13311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 13321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @return whether the descendant of this scroll view is within delta 13331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * pixels of being on the screen. 13341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 13351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private boolean isWithinDeltaOfScreen(View descendant, int delta, int height) { 13361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell descendant.getDrawingRect(mTempRect); 13371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell offsetDescendantRectToMyCoords(descendant, mTempRect); 13381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 13391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return (mTempRect.bottom + delta) >= getScrollY() 13401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell && (mTempRect.top - delta) <= (getScrollY() + height); 13411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 13421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 13431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 13441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Smooth scroll by a Y delta 13451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * 13461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param delta the number of pixels to scroll by on the Y axis 13471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 13481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private void doScrollY(int delta) { 13491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (delta != 0) { 13501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (mSmoothScrollingEnabled) { 13511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell smoothScrollBy(0, delta); 13521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else { 13531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollBy(0, delta); 13541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 13551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 13561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 13571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 13581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 13591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Like {@link View#scrollBy}, but scroll smoothly instead of immediately. 13601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * 13611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param dx the number of pixels to scroll by on the X axis 13621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param dy the number of pixels to scroll by on the Y axis 13631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 13641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public final void smoothScrollBy(int dx, int dy) { 13651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (getChildCount() == 0) { 13661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // Nothing to do. 13671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return; 13681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 13691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell long duration = AnimationUtils.currentAnimationTimeMillis() - mLastScroll; 13701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (duration > ANIMATED_SCROLL_GAP) { 13711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int height = getHeight() - getPaddingBottom() - getPaddingTop(); 13721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int bottom = getChildAt(0).getHeight(); 13731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int maxY = Math.max(0, bottom - height); 13741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int scrollY = getScrollY(); 13751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell dy = Math.max(0, Math.min(scrollY + dy, maxY)) - scrollY; 13761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 13771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mScroller.startScroll(getScrollX(), scrollY, 0, dy); 13781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell ViewCompat.postInvalidateOnAnimation(this); 13791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else { 13801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (!mScroller.isFinished()) { 13811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mScroller.abortAnimation(); 13821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 13831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollBy(dx, dy); 13841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 13851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mLastScroll = AnimationUtils.currentAnimationTimeMillis(); 13861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 13871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 13881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 13891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Like {@link #scrollTo}, but scroll smoothly instead of immediately. 13901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * 13911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param x the position where to scroll on the X axis 13921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param y the position where to scroll on the Y axis 13931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 13941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public final void smoothScrollTo(int x, int y) { 13951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell smoothScrollBy(x - getScrollX(), y - getScrollY()); 13961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 13971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 13981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 13991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * <p>The scroll range of a scroll view is the overall height of all of its 14001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * children.</p> 1401c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki * @hide 14021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 14038e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas @RestrictTo(LIBRARY_GROUP) 14041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 1405c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki public int computeVerticalScrollRange() { 14061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int count = getChildCount(); 14071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int contentHeight = getHeight() - getPaddingBottom() - getPaddingTop(); 14081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (count == 0) { 14091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return contentHeight; 14101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 14111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 14121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int scrollRange = getChildAt(0).getBottom(); 14131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int scrollY = getScrollY(); 14141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int overscrollBottom = Math.max(0, scrollRange - contentHeight); 14151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (scrollY < 0) { 14161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollRange -= scrollY; 14171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else if (scrollY > overscrollBottom) { 14181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollRange += scrollY - overscrollBottom; 14191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 14201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 14211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return scrollRange; 14221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 14231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1424c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki /** @hide */ 14258e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas @RestrictTo(LIBRARY_GROUP) 14261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 1427c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki public int computeVerticalScrollOffset() { 14281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return Math.max(0, super.computeVerticalScrollOffset()); 14291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 14301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1431c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki /** @hide */ 14328e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas @RestrictTo(LIBRARY_GROUP) 1433c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki @Override 1434c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki public int computeVerticalScrollExtent() { 1435c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki return super.computeVerticalScrollExtent(); 1436c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki } 1437c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki 1438c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki /** @hide */ 14398e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas @RestrictTo(LIBRARY_GROUP) 1440c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki @Override 1441c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki public int computeHorizontalScrollRange() { 1442c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki return super.computeHorizontalScrollRange(); 1443c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki } 1444c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki 1445c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki /** @hide */ 14468e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas @RestrictTo(LIBRARY_GROUP) 1447c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki @Override 1448c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki public int computeHorizontalScrollOffset() { 1449c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki return super.computeHorizontalScrollOffset(); 1450c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki } 1451c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki 1452c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki /** @hide */ 14538e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas @RestrictTo(LIBRARY_GROUP) 1454c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki @Override 1455c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki public int computeHorizontalScrollExtent() { 1456c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki return super.computeHorizontalScrollExtent(); 1457c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki } 1458c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki 14591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 14604d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas protected void measureChild(View child, int parentWidthMeasureSpec, 14614d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas int parentHeightMeasureSpec) { 14621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell ViewGroup.LayoutParams lp = child.getLayoutParams(); 14631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 14641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int childWidthMeasureSpec; 14651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int childHeightMeasureSpec; 14661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 14671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, getPaddingLeft() 14681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell + getPaddingRight(), lp.width); 14691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 14701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); 14711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 14721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 14731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 14741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 14751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 14761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, 14771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int parentHeightMeasureSpec, int heightUsed) { 14781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); 14791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 14801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, 14811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell getPaddingLeft() + getPaddingRight() + lp.leftMargin + lp.rightMargin 14821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell + widthUsed, lp.width); 14831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec( 14841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell lp.topMargin + lp.bottomMargin, MeasureSpec.UNSPECIFIED); 14851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 14861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 14871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 14881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 14891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 14901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void computeScroll() { 14911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (mScroller.computeScrollOffset()) { 149276daed103193a1756535d1f59b165e98e1d17445Chris Banes final int x = mScroller.getCurrX(); 149376daed103193a1756535d1f59b165e98e1d17445Chris Banes final int y = mScroller.getCurrY(); 149476daed103193a1756535d1f59b165e98e1d17445Chris Banes 149576daed103193a1756535d1f59b165e98e1d17445Chris Banes int dy = y - mLastScrollerY; 14961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 149776daed103193a1756535d1f59b165e98e1d17445Chris Banes // Dispatch up to parent 149876daed103193a1756535d1f59b165e98e1d17445Chris Banes if (dispatchNestedPreScroll(0, dy, mScrollConsumed, null, ViewCompat.TYPE_NON_TOUCH)) { 149976daed103193a1756535d1f59b165e98e1d17445Chris Banes dy -= mScrollConsumed[1]; 150076daed103193a1756535d1f59b165e98e1d17445Chris Banes } 150176daed103193a1756535d1f59b165e98e1d17445Chris Banes 150276daed103193a1756535d1f59b165e98e1d17445Chris Banes if (dy != 0) { 15031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int range = getScrollRange(); 150476daed103193a1756535d1f59b165e98e1d17445Chris Banes final int oldScrollY = getScrollY(); 150576daed103193a1756535d1f59b165e98e1d17445Chris Banes 150676daed103193a1756535d1f59b165e98e1d17445Chris Banes overScrollByCompat(0, dy, getScrollX(), oldScrollY, 0, range, 0, 0, false); 150776daed103193a1756535d1f59b165e98e1d17445Chris Banes 150876daed103193a1756535d1f59b165e98e1d17445Chris Banes final int scrolledDeltaY = getScrollY() - oldScrollY; 150976daed103193a1756535d1f59b165e98e1d17445Chris Banes final int unconsumedY = dy - scrolledDeltaY; 151076daed103193a1756535d1f59b165e98e1d17445Chris Banes 151176daed103193a1756535d1f59b165e98e1d17445Chris Banes if (!dispatchNestedScroll(0, scrolledDeltaY, 0, unconsumedY, null, 151276daed103193a1756535d1f59b165e98e1d17445Chris Banes ViewCompat.TYPE_NON_TOUCH)) { 151376daed103193a1756535d1f59b165e98e1d17445Chris Banes final int mode = getOverScrollMode(); 151476daed103193a1756535d1f59b165e98e1d17445Chris Banes final boolean canOverscroll = mode == OVER_SCROLL_ALWAYS 151576daed103193a1756535d1f59b165e98e1d17445Chris Banes || (mode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0); 151676daed103193a1756535d1f59b165e98e1d17445Chris Banes if (canOverscroll) { 151776daed103193a1756535d1f59b165e98e1d17445Chris Banes ensureGlows(); 151876daed103193a1756535d1f59b165e98e1d17445Chris Banes if (y <= 0 && oldScrollY > 0) { 151976daed103193a1756535d1f59b165e98e1d17445Chris Banes mEdgeGlowTop.onAbsorb((int) mScroller.getCurrVelocity()); 152076daed103193a1756535d1f59b165e98e1d17445Chris Banes } else if (y >= range && oldScrollY < range) { 152176daed103193a1756535d1f59b165e98e1d17445Chris Banes mEdgeGlowBottom.onAbsorb((int) mScroller.getCurrVelocity()); 152276daed103193a1756535d1f59b165e98e1d17445Chris Banes } 15231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 15241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 15251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 152676daed103193a1756535d1f59b165e98e1d17445Chris Banes 152776daed103193a1756535d1f59b165e98e1d17445Chris Banes // Finally update the scroll positions and post an invalidation 152876daed103193a1756535d1f59b165e98e1d17445Chris Banes mLastScrollerY = y; 152976daed103193a1756535d1f59b165e98e1d17445Chris Banes ViewCompat.postInvalidateOnAnimation(this); 153076daed103193a1756535d1f59b165e98e1d17445Chris Banes } else { 153176daed103193a1756535d1f59b165e98e1d17445Chris Banes // We can't scroll any more, so stop any indirect scrolling 153276daed103193a1756535d1f59b165e98e1d17445Chris Banes if (hasNestedScrollingParent(ViewCompat.TYPE_NON_TOUCH)) { 153376daed103193a1756535d1f59b165e98e1d17445Chris Banes stopNestedScroll(ViewCompat.TYPE_NON_TOUCH); 153476daed103193a1756535d1f59b165e98e1d17445Chris Banes } 153576daed103193a1756535d1f59b165e98e1d17445Chris Banes // and reset the scroller y 153676daed103193a1756535d1f59b165e98e1d17445Chris Banes mLastScrollerY = 0; 15371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 15381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 15391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 15401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 15411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Scrolls the view to the given child. 15421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * 15431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param child the View to scroll to 15441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 15451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private void scrollToChild(View child) { 15461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell child.getDrawingRect(mTempRect); 15471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 15481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /* Offset from child's local coordinates to ScrollView coordinates */ 15491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell offsetDescendantRectToMyCoords(child, mTempRect); 15501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 15511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect); 15521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 15531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (scrollDelta != 0) { 15541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollBy(0, scrollDelta); 15551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 15561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 15571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 15581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 15591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * If rect is off screen, scroll just enough to get it (or at least the 15601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * first screen size chunk of it) on screen. 15611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * 15621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param rect The rectangle. 15631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param immediate True to scroll immediately without animation 15641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @return true if scrolling was performed 15651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 15661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private boolean scrollToChildRect(Rect rect, boolean immediate) { 15671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int delta = computeScrollDeltaToGetChildRectOnScreen(rect); 15681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final boolean scroll = delta != 0; 15691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (scroll) { 15701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (immediate) { 15711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollBy(0, delta); 15721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else { 15731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell smoothScrollBy(0, delta); 15741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 15751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 15761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return scroll; 15771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 15781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 15791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 15801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Compute the amount to scroll in the Y direction in order to get 15811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * a rectangle completely on the screen (or, if taller than the screen, 15821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * at least the first screen size chunk of it). 15831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * 15841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param rect The rect. 15851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @return The scroll delta. 15861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 15871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell protected int computeScrollDeltaToGetChildRectOnScreen(Rect rect) { 15881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (getChildCount() == 0) return 0; 15891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 15901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int height = getHeight(); 15911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int screenTop = getScrollY(); 15921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int screenBottom = screenTop + height; 15931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 15941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int fadingEdge = getVerticalFadingEdgeLength(); 15951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 15961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // leave room for top fading edge as long as rect isn't at very top 15971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (rect.top > 0) { 15981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell screenTop += fadingEdge; 15991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 16001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 16011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // leave room for bottom fading edge as long as rect isn't at very bottom 16021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (rect.bottom < getChildAt(0).getHeight()) { 16031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell screenBottom -= fadingEdge; 16041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 16051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 16061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int scrollYDelta = 0; 16071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 16081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (rect.bottom > screenBottom && rect.top > screenTop) { 16091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // need to move down to get it in view: move down just enough so 16101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // that the entire rectangle is in view (or at least the first 16111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // screen size chunk). 16121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 16131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (rect.height() > height) { 16141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // just enough to get screen size chunk on 16151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollYDelta += (rect.top - screenTop); 16161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else { 16171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // get entire rect at bottom of screen 16181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollYDelta += (rect.bottom - screenBottom); 16191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 16201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 16211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // make sure we aren't scrolling beyond the end of our content 16221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int bottom = getChildAt(0).getBottom(); 16231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int distanceToBottom = bottom - screenBottom; 16241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollYDelta = Math.min(scrollYDelta, distanceToBottom); 16251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 16261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else if (rect.top < screenTop && rect.bottom < screenBottom) { 16271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // need to move up to get it in view: move up just enough so that 16281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // entire rectangle is in view (or at least the first screen 16291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // size chunk of it). 16301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 16311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (rect.height() > height) { 16321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // screen size chunk 16331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollYDelta -= (screenBottom - rect.bottom); 16341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else { 16351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // entire rect at top 16361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollYDelta -= (screenTop - rect.top); 16371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 16381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 16391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // make sure we aren't scrolling any further than the top our content 16401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollYDelta = Math.max(scrollYDelta, -getScrollY()); 16411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 16421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return scrollYDelta; 16431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 16441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 16451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 16461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void requestChildFocus(View child, View focused) { 16471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (!mIsLayoutDirty) { 16481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollToChild(focused); 16491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else { 16501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // The child may not be laid out yet, we can't compute the scroll yet 16511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mChildToScrollTo = focused; 16521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 16531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell super.requestChildFocus(child, focused); 16541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 16551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 16561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 16571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 16581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * When looking for focus in children of a scroll view, need to be a little 16591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * more careful not to give focus to something that is scrolled off screen. 16601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * 16611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * This is more expensive than the default {@link android.view.ViewGroup} 16621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * implementation, otherwise this behavior might have been made the default. 16631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 16641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 16651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell protected boolean onRequestFocusInDescendants(int direction, 16661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell Rect previouslyFocusedRect) { 16671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 16681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // convert from forward / backward notation to up / down / left / right 16691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // (ugh). 16701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (direction == View.FOCUS_FORWARD) { 16711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell direction = View.FOCUS_DOWN; 16721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else if (direction == View.FOCUS_BACKWARD) { 16731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell direction = View.FOCUS_UP; 16741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 16751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 16764d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas final View nextFocus = previouslyFocusedRect == null 16774d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas ? FocusFinder.getInstance().findNextFocus(this, null, direction) 16784d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas : FocusFinder.getInstance().findNextFocusFromRect( 16794d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas this, previouslyFocusedRect, direction); 16801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 16811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (nextFocus == null) { 16821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return false; 16831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 16841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 16851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (isOffScreen(nextFocus)) { 16861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return false; 16871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 16881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 16891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return nextFocus.requestFocus(direction, previouslyFocusedRect); 16901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 16911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 16921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 16931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public boolean requestChildRectangleOnScreen(View child, Rect rectangle, 16941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell boolean immediate) { 16951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // offset into coordinate space of this scroll view 16961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell rectangle.offset(child.getLeft() - child.getScrollX(), 16971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell child.getTop() - child.getScrollY()); 16981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 16991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return scrollToChildRect(rectangle, immediate); 17001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 17011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 17021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 17031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void requestLayout() { 17041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mIsLayoutDirty = true; 17051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell super.requestLayout(); 17061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 17071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 17081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 17091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell protected void onLayout(boolean changed, int l, int t, int r, int b) { 17101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell super.onLayout(changed, l, t, r, b); 17111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mIsLayoutDirty = false; 17121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // Give a child focus if it needs it 17131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (mChildToScrollTo != null && isViewDescendantOf(mChildToScrollTo, this)) { 17141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollToChild(mChildToScrollTo); 17151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 17161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mChildToScrollTo = null; 17171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 17181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (!mIsLaidOut) { 17191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (mSavedState != null) { 17201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollTo(getScrollX(), mSavedState.scrollPosition); 17211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mSavedState = null; 17221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } // mScrollY default value is "0" 17231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 17241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int childHeight = (getChildCount() > 0) ? getChildAt(0).getMeasuredHeight() : 0; 17251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int scrollRange = Math.max(0, 17261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell childHeight - (b - t - getPaddingBottom() - getPaddingTop())); 17271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 17281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // Don't forget to clamp 17291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (getScrollY() > scrollRange) { 17301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollTo(getScrollX(), scrollRange); 17311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else if (getScrollY() < 0) { 17321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollTo(getScrollX(), 0); 17331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 17341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 17351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 17361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // Calling this with the present values causes it to re-claim them 17371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollTo(getScrollX(), getScrollY()); 17381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mIsLaidOut = true; 17391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 17401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 17411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 17421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void onAttachedToWindow() { 1743d344e81ad956ec445038f5ba2ca2ebd1831b1f8aAlan Viverette super.onAttachedToWindow(); 1744d344e81ad956ec445038f5ba2ca2ebd1831b1f8aAlan Viverette 17451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mIsLaidOut = false; 17461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 17471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 17481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 17491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell protected void onSizeChanged(int w, int h, int oldw, int oldh) { 17501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell super.onSizeChanged(w, h, oldw, oldh); 17511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 17521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell View currentFocused = findFocus(); 17534d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas if (null == currentFocused || this == currentFocused) { 17541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return; 17554d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas } 17561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 17571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // If the currently-focused view was visible on the screen when the 17581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // screen was at the old height, then scroll the screen to make that 17591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // view visible with the new screen height. 17601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (isWithinDeltaOfScreen(currentFocused, 0, oldh)) { 17611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell currentFocused.getDrawingRect(mTempRect); 17621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell offsetDescendantRectToMyCoords(currentFocused, mTempRect); 17631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect); 17641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell doScrollY(scrollDelta); 17651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 17661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 17671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 17681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 17691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Return true if child is a descendant of parent, (or equal to the parent). 17701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 17711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private static boolean isViewDescendantOf(View child, View parent) { 17721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (child == parent) { 17731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return true; 17741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 17751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 17761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final ViewParent theParent = child.getParent(); 17771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return (theParent instanceof ViewGroup) && isViewDescendantOf((View) theParent, parent); 17781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 17791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 17801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 17811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Fling the scroll view 17821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * 17831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param velocityY The initial velocity in the Y direction. Positive 17841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * numbers mean that the finger/cursor is moving down the screen, 17851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * which means we want to scroll towards the top. 17861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 17871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void fling(int velocityY) { 17881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (getChildCount() > 0) { 178976daed103193a1756535d1f59b165e98e1d17445Chris Banes startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_NON_TOUCH); 179076daed103193a1756535d1f59b165e98e1d17445Chris Banes mScroller.fling(getScrollX(), getScrollY(), // start 179176daed103193a1756535d1f59b165e98e1d17445Chris Banes 0, velocityY, // velocities 179276daed103193a1756535d1f59b165e98e1d17445Chris Banes 0, 0, // x 179376daed103193a1756535d1f59b165e98e1d17445Chris Banes Integer.MIN_VALUE, Integer.MAX_VALUE, // y 179476daed103193a1756535d1f59b165e98e1d17445Chris Banes 0, 0); // overscroll 179576daed103193a1756535d1f59b165e98e1d17445Chris Banes mLastScrollerY = getScrollY(); 17961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell ViewCompat.postInvalidateOnAnimation(this); 17971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 17981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 17991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 18001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private void flingWithNestedDispatch(int velocityY) { 18011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int scrollY = getScrollY(); 18024d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas final boolean canFling = (scrollY > 0 || velocityY > 0) 18034d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas && (scrollY < getScrollRange() || velocityY < 0); 18041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (!dispatchNestedPreFling(0, velocityY)) { 18051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell dispatchNestedFling(0, velocityY, canFling); 180676daed103193a1756535d1f59b165e98e1d17445Chris Banes fling(velocityY); 18071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 18081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 18091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 18101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private void endDrag() { 18111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mIsBeingDragged = false; 18121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 18131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell recycleVelocityTracker(); 181476daed103193a1756535d1f59b165e98e1d17445Chris Banes stopNestedScroll(ViewCompat.TYPE_TOUCH); 18151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 18161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (mEdgeGlowTop != null) { 18171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mEdgeGlowTop.onRelease(); 18181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mEdgeGlowBottom.onRelease(); 18191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 18201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 18211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 18221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 18231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * {@inheritDoc} 18241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * 18251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * <p>This version also clamps the scrolling to the bounds of our child. 18261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 18271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 18281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void scrollTo(int x, int y) { 18291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // we rely on the fact the View.scrollBy calls scrollTo. 18301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (getChildCount() > 0) { 18311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell View child = getChildAt(0); 18321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell x = clamp(x, getWidth() - getPaddingRight() - getPaddingLeft(), child.getWidth()); 18331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell y = clamp(y, getHeight() - getPaddingBottom() - getPaddingTop(), child.getHeight()); 18341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (x != getScrollX() || y != getScrollY()) { 18351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell super.scrollTo(x, y); 18361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 18371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 18381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 18391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 18401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private void ensureGlows() { 1841e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas if (getOverScrollMode() != View.OVER_SCROLL_NEVER) { 18421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (mEdgeGlowTop == null) { 18431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell Context context = getContext(); 1844506695b4cf6b22d70d2e3647d94551e721ee0430Aurimas Liutikas mEdgeGlowTop = new EdgeEffect(context); 1845506695b4cf6b22d70d2e3647d94551e721ee0430Aurimas Liutikas mEdgeGlowBottom = new EdgeEffect(context); 18461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 18471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else { 18481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mEdgeGlowTop = null; 18491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mEdgeGlowBottom = null; 18501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 18511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 18521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 18531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 18541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void draw(Canvas canvas) { 18551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell super.draw(canvas); 18561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (mEdgeGlowTop != null) { 18571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int scrollY = getScrollY(); 18581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (!mEdgeGlowTop.isFinished()) { 18591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int restoreCount = canvas.save(); 1860bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard int width = getWidth(); 1861bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard int height = getHeight(); 1862bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard int xTranslation = 0; 1863bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard int yTranslation = Math.min(0, scrollY); 1864bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP || getClipToPadding()) { 1865bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard width -= getPaddingLeft() + getPaddingRight(); 1866bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard xTranslation += getPaddingLeft(); 1867bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard } 1868bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && getClipToPadding()) { 1869bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard height -= getPaddingTop() + getPaddingBottom(); 1870bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard yTranslation += getPaddingTop(); 1871bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard } 1872bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard canvas.translate(xTranslation, yTranslation); 1873bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard mEdgeGlowTop.setSize(width, height); 18741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (mEdgeGlowTop.draw(canvas)) { 18751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell ViewCompat.postInvalidateOnAnimation(this); 18761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 18771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell canvas.restoreToCount(restoreCount); 18781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 18791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (!mEdgeGlowBottom.isFinished()) { 18801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int restoreCount = canvas.save(); 1881bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard int width = getWidth(); 1882bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard int height = getHeight(); 1883bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard int xTranslation = 0; 1884bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard int yTranslation = Math.max(getScrollRange(), scrollY) + height; 1885bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP || getClipToPadding()) { 1886bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard width -= getPaddingLeft() + getPaddingRight(); 1887bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard xTranslation += getPaddingLeft(); 1888bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard } 1889bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && getClipToPadding()) { 1890bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard height -= getPaddingTop() + getPaddingBottom(); 1891bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard yTranslation -= getPaddingBottom(); 1892bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard } 1893bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard canvas.translate(xTranslation - width, yTranslation); 18941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell canvas.rotate(180, width, 0); 18951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mEdgeGlowBottom.setSize(width, height); 18961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (mEdgeGlowBottom.draw(canvas)) { 18971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell ViewCompat.postInvalidateOnAnimation(this); 18981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 18991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell canvas.restoreToCount(restoreCount); 19001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 19011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 19021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 19031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 19041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private static int clamp(int n, int my, int child) { 19051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (my >= child || n < 0) { 19061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /* my >= child is this case: 19071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * |--------------- me ---------------| 19081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * |------ child ------| 19091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * or 19101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * |--------------- me ---------------| 19111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * |------ child ------| 19121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * or 19131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * |--------------- me ---------------| 19141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * |------ child ------| 19151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * 19161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * n < 0 is this case: 19171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * |------ me ------| 19181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * |-------- child --------| 19191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * |-- mScrollX --| 19201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 19211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return 0; 19221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 19234d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas if ((my + n) > child) { 19241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /* this case: 19251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * |------ me ------| 19261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * |------ child ------| 19271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * |-- mScrollX --| 19281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 19294d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas return child - my; 19301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 19311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return n; 19321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 19331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 19341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 19351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell protected void onRestoreInstanceState(Parcelable state) { 1936bb58a82daf11bf3c056d1cd5887aa26435d37b69Aurimas Liutikas if (!(state instanceof SavedState)) { 1937bb58a82daf11bf3c056d1cd5887aa26435d37b69Aurimas Liutikas super.onRestoreInstanceState(state); 1938bb58a82daf11bf3c056d1cd5887aa26435d37b69Aurimas Liutikas return; 1939bb58a82daf11bf3c056d1cd5887aa26435d37b69Aurimas Liutikas } 1940bb58a82daf11bf3c056d1cd5887aa26435d37b69Aurimas Liutikas 19411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell SavedState ss = (SavedState) state; 19421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell super.onRestoreInstanceState(ss.getSuperState()); 19431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mSavedState = ss; 19441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell requestLayout(); 19451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 19461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 19471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 19481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell protected Parcelable onSaveInstanceState() { 19491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell Parcelable superState = super.onSaveInstanceState(); 19501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell SavedState ss = new SavedState(superState); 19511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell ss.scrollPosition = getScrollY(); 19521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return ss; 19531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 19541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 19551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell static class SavedState extends BaseSavedState { 19561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public int scrollPosition; 19571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 19581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell SavedState(Parcelable superState) { 19591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell super(superState); 19601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 19611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 19624d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas SavedState(Parcel source) { 19631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell super(source); 19641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollPosition = source.readInt(); 19651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 19661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 19671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 19681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void writeToParcel(Parcel dest, int flags) { 19691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell super.writeToParcel(dest, flags); 19701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell dest.writeInt(scrollPosition); 19711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 19721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 19731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 19741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public String toString() { 19751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return "HorizontalScrollView.SavedState{" 19761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell + Integer.toHexString(System.identityHashCode(this)) 19771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell + " scrollPosition=" + scrollPosition + "}"; 19781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 19791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 19804d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas public static final Parcelable.Creator<SavedState> CREATOR = 19814d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas new Parcelable.Creator<SavedState>() { 198215375aa6fd54b036f97f99229aefab2822c8a1c9Aurimas Liutikas @Override 19831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public SavedState createFromParcel(Parcel in) { 19841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return new SavedState(in); 19851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 19861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 198715375aa6fd54b036f97f99229aefab2822c8a1c9Aurimas Liutikas @Override 19881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public SavedState[] newArray(int size) { 19891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return new SavedState[size]; 19901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 19911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell }; 19921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 19931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 19941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell static class AccessibilityDelegate extends AccessibilityDelegateCompat { 19951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 19961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public boolean performAccessibilityAction(View host, int action, Bundle arguments) { 19971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (super.performAccessibilityAction(host, action, arguments)) { 19981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return true; 19991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 20001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final NestedScrollView nsvHost = (NestedScrollView) host; 20011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (!nsvHost.isEnabled()) { 20021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return false; 20031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 20041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell switch (action) { 20051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD: { 20061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int viewportHeight = nsvHost.getHeight() - nsvHost.getPaddingBottom() 20071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell - nsvHost.getPaddingTop(); 20081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int targetScrollY = Math.min(nsvHost.getScrollY() + viewportHeight, 20091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell nsvHost.getScrollRange()); 20101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (targetScrollY != nsvHost.getScrollY()) { 20111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell nsvHost.smoothScrollTo(0, targetScrollY); 20121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return true; 20131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 20141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 20151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return false; 20161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD: { 20171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int viewportHeight = nsvHost.getHeight() - nsvHost.getPaddingBottom() 20181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell - nsvHost.getPaddingTop(); 20191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int targetScrollY = Math.max(nsvHost.getScrollY() - viewportHeight, 0); 20201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (targetScrollY != nsvHost.getScrollY()) { 20211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell nsvHost.smoothScrollTo(0, targetScrollY); 20221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return true; 20231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 20241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 20251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return false; 20261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 20271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return false; 20281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 20291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 20301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 20311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) { 20321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell super.onInitializeAccessibilityNodeInfo(host, info); 20331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final NestedScrollView nsvHost = (NestedScrollView) host; 20341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell info.setClassName(ScrollView.class.getName()); 20351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (nsvHost.isEnabled()) { 20361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int scrollRange = nsvHost.getScrollRange(); 20371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (scrollRange > 0) { 20381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell info.setScrollable(true); 20391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (nsvHost.getScrollY() > 0) { 20401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD); 20411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 20421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (nsvHost.getScrollY() < scrollRange) { 20431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD); 20441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 20451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 20461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 20471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 20481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 20491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 20501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) { 20511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell super.onInitializeAccessibilityEvent(host, event); 20521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final NestedScrollView nsvHost = (NestedScrollView) host; 20531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell event.setClassName(ScrollView.class.getName()); 20541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final boolean scrollable = nsvHost.getScrollRange() > 0; 205514d02ef06479168249fdfeea47bc105d05e88749Aurimas Liutikas event.setScrollable(scrollable); 205614d02ef06479168249fdfeea47bc105d05e88749Aurimas Liutikas event.setScrollX(nsvHost.getScrollX()); 205714d02ef06479168249fdfeea47bc105d05e88749Aurimas Liutikas event.setScrollY(nsvHost.getScrollY()); 205814d02ef06479168249fdfeea47bc105d05e88749Aurimas Liutikas AccessibilityRecordCompat.setMaxScrollX(event, nsvHost.getScrollX()); 205914d02ef06479168249fdfeea47bc105d05e88749Aurimas Liutikas AccessibilityRecordCompat.setMaxScrollY(event, nsvHost.getScrollRange()); 20601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 20611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 20621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell} 2063