NestedScrollView.java revision 1fcce4485ef99aca928ebfb877859c5ecd47716c
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 181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellpackage android.support.v4.widget; 191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.content.Context; 211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.content.res.TypedArray; 221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.graphics.Canvas; 231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.graphics.Rect; 241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.os.Bundle; 251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.os.Parcel; 261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.os.Parcelable; 271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.support.v4.view.AccessibilityDelegateCompat; 281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.support.v4.view.InputDeviceCompat; 291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.support.v4.view.MotionEventCompat; 301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.support.v4.view.NestedScrollingChild; 311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.support.v4.view.NestedScrollingChildHelper; 321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.support.v4.view.NestedScrollingParent; 331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.support.v4.view.NestedScrollingParentHelper; 341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.support.v4.view.VelocityTrackerCompat; 351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.support.v4.view.ViewCompat; 361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.support.v4.view.accessibility.AccessibilityEventCompat; 371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; 381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.support.v4.view.accessibility.AccessibilityRecordCompat; 391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.util.AttributeSet; 401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.util.Log; 411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.util.TypedValue; 421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.FocusFinder; 431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.KeyEvent; 441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.MotionEvent; 451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.VelocityTracker; 461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.View; 471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.ViewConfiguration; 481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.ViewDebug; 491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.ViewGroup; 501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.ViewParent; 511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.accessibility.AccessibilityEvent; 521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.animation.AnimationUtils; 531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.widget.FrameLayout; 541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.widget.ScrollView; 551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport java.util.List; 571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell/** 591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * NestedScrollView is just like {@link android.widget.ScrollView}, but it supports acting 601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * as both a nested scrolling parent and child on both new and old versions of Android. 611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Nested scrolling is enabled by default. 621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellpublic class NestedScrollView extends FrameLayout implements NestedScrollingParent, 641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell NestedScrollingChild { 651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell static final int ANIMATED_SCROLL_GAP = 250; 661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell static final float MAX_SCROLL_FACTOR = 0.5f; 681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private static final String TAG = "NestedScrollView"; 701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private long mLastScroll; 721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private final Rect mTempRect = new Rect(); 741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private ScrollerCompat mScroller; 751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private EdgeEffectCompat mEdgeGlowTop; 761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private EdgeEffectCompat mEdgeGlowBottom; 771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Position of the last motion event. 801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private int mLastMotionY; 821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * True when the layout has changed but the traversal has not come through yet. 851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Ideally the view hierarchy would keep track of this for us. 861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private boolean mIsLayoutDirty = true; 881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private boolean mIsLaidOut = false; 891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * The child to give focus to in the event that a child has requested focus while the 921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * layout is dirty. This prevents the scroll from being wrong if the child has not been 931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * laid out before requesting focus. 941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private View mChildToScrollTo = null; 961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * True if the user is currently dragging this ScrollView around. This is 991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * not the same as 'is being flinged', which can be checked by 1001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * mScroller.isFinished() (flinging begins when the user lifts his finger). 1011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 1021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private boolean mIsBeingDragged = false; 1031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 1051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Determines speed during touch scrolling 1061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 1071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private VelocityTracker mVelocityTracker; 1081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 1101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * When set to true, the scroll view measure its child to make it fill the currently 1111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * visible area. 1121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 1131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private boolean mFillViewport; 1141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 1161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Whether arrow scrolling is animated. 1171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 1181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private boolean mSmoothScrollingEnabled = true; 1191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private int mTouchSlop; 1211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private int mMinimumVelocity; 1221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private int mMaximumVelocity; 1231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 1251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * ID of the active pointer. This is used to retain consistency during 1261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * drags/flings if multiple pointers are used. 1271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 1281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private int mActivePointerId = INVALID_POINTER; 1291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 1311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Used during scrolling to retrieve the new offset within the window. 1321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 1331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private final int[] mScrollOffset = new int[2]; 1341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private final int[] mScrollConsumed = new int[2]; 1351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private int mNestedYOffset; 1361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 1381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Sentinel value for no current active pointer. 1391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Used by {@link #mActivePointerId}. 1401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 1411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private static final int INVALID_POINTER = -1; 1421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private SavedState mSavedState; 1441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private static final AccessibilityDelegate ACCESSIBILITY_DELEGATE = new AccessibilityDelegate(); 1461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private static final int[] SCROLLVIEW_STYLEABLE = new int[] { 1481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell android.R.attr.fillViewport 1491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell }; 1501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private final NestedScrollingParentHelper mParentHelper; 1521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private final NestedScrollingChildHelper mChildHelper; 1531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private float mVerticalScrollFactor; 1551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public NestedScrollView(Context context) { 1571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell this(context, null); 1581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 1591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public NestedScrollView(Context context, AttributeSet attrs) { 1611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell this(context, attrs, 0); 1621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 1631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public NestedScrollView(Context context, AttributeSet attrs, int defStyleAttr) { 1651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell super(context, attrs, defStyleAttr); 1661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell initScrollView(); 1671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final TypedArray a = context.obtainStyledAttributes( 1691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell attrs, SCROLLVIEW_STYLEABLE, defStyleAttr, 0); 1701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell setFillViewport(a.getBoolean(0, false)); 1721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell a.recycle(); 1741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mParentHelper = new NestedScrollingParentHelper(this); 1761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mChildHelper = new NestedScrollingChildHelper(this); 1771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // ...because why else would you be using this widget? 1791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell setNestedScrollingEnabled(true); 1801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell ViewCompat.setAccessibilityDelegate(this, ACCESSIBILITY_DELEGATE); 1821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 1831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // NestedScrollingChild 1851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 1871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void setNestedScrollingEnabled(boolean enabled) { 1881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mChildHelper.setNestedScrollingEnabled(enabled); 1891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 1901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 1921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public boolean isNestedScrollingEnabled() { 1931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return mChildHelper.isNestedScrollingEnabled(); 1941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 1951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 1971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public boolean startNestedScroll(int axes) { 1981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return mChildHelper.startNestedScroll(axes); 1991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 2001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 2011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 2021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void stopNestedScroll() { 2031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mChildHelper.stopNestedScroll(); 2041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 2051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 2061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 2071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public boolean hasNestedScrollingParent() { 2081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return mChildHelper.hasNestedScrollingParent(); 2091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 2101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 2111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 2121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, 2131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int dyUnconsumed, int[] offsetInWindow) { 2141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return mChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, 2151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell offsetInWindow); 2161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 2171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 2181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 2191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) { 2201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return mChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow); 2211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 2221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 2231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 2241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) { 2251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return mChildHelper.dispatchNestedFling(velocityX, velocityY, consumed); 2261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 2271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 2281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 2291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public boolean dispatchNestedPreFling(float velocityX, float velocityY) { 2301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return mChildHelper.dispatchNestedPreFling(velocityX, velocityY); 2311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 2321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 2331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // NestedScrollingParent 2341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 2351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 2361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) { 2371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0; 2381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 2391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 2401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 2411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) { 2421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mParentHelper.onNestedScrollAccepted(child, target, nestedScrollAxes); 2431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL); 2441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 2451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 2461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 2471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void onStopNestedScroll(View target) { 2481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell stopNestedScroll(); 2491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 2501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 2511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 2521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, 2531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int dyUnconsumed) { 2541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int oldScrollY = getScrollY(); 2551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollBy(0, dyUnconsumed); 2561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int myConsumed = getScrollY() - oldScrollY; 2571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int myUnconsumed = dyUnconsumed - myConsumed; 2581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell dispatchNestedScroll(0, myConsumed, 0, myUnconsumed, null); 2591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 2601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 2611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 2621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) { 2631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // Do nothing 2641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 2651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 2661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 2671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) { 2681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (!consumed) { 2691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell flingWithNestedDispatch((int) velocityY); 2701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return true; 2711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 2721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return false; 2731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 2741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 2751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 2761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public boolean onNestedPreFling(View target, float velocityX, float velocityY) { 2771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // Do nothing 2781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return false; 2791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 2801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 2811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 2821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public int getNestedScrollAxes() { 2831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return mParentHelper.getNestedScrollAxes(); 2841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 2851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 2861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // ScrollView import 2871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 2881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public boolean shouldDelayChildPressedState() { 2891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return true; 2901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 2911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 2921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 2931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell protected float getTopFadingEdgeStrength() { 2941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (getChildCount() == 0) { 2951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return 0.0f; 2961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 2971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 2981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int length = getVerticalFadingEdgeLength(); 2991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int scrollY = getScrollY(); 3001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (scrollY < length) { 3011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return scrollY / (float) length; 3021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 3031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 3041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return 1.0f; 3051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 3061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 3071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 3081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell protected float getBottomFadingEdgeStrength() { 3091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (getChildCount() == 0) { 3101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return 0.0f; 3111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 3121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 3131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int length = getVerticalFadingEdgeLength(); 3141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int bottomEdge = getHeight() - getPaddingBottom(); 3151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int span = getChildAt(0).getBottom() - getScrollY() - bottomEdge; 3161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (span < length) { 3171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return span / (float) length; 3181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 3191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 3201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return 1.0f; 3211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 3221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 3231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 3241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @return The maximum amount this scroll view will scroll in response to 3251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * an arrow event. 3261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 3271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public int getMaxScrollAmount() { 3281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return (int) (MAX_SCROLL_FACTOR * getHeight()); 3291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 3301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 3311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private void initScrollView() { 3321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mScroller = new ScrollerCompat(getContext(), null); 3331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell setFocusable(true); 3341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); 3351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell setWillNotDraw(false); 3361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final ViewConfiguration configuration = ViewConfiguration.get(getContext()); 3371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mTouchSlop = configuration.getScaledTouchSlop(); 3381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mMinimumVelocity = configuration.getScaledMinimumFlingVelocity(); 3391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); 3401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 3411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 3421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 3431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void addView(View child) { 3441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (getChildCount() > 0) { 3451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell throw new IllegalStateException("ScrollView can host only one direct child"); 3461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 3471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 3481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell super.addView(child); 3491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 3501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 3511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 3521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void addView(View child, int index) { 3531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (getChildCount() > 0) { 3541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell throw new IllegalStateException("ScrollView can host only one direct child"); 3551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 3561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 3571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell super.addView(child, index); 3581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 3591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 3601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 3611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void addView(View child, ViewGroup.LayoutParams params) { 3621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (getChildCount() > 0) { 3631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell throw new IllegalStateException("ScrollView can host only one direct child"); 3641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 3651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 3661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell super.addView(child, params); 3671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 3681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 3691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 3701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void addView(View child, int index, ViewGroup.LayoutParams params) { 3711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (getChildCount() > 0) { 3721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell throw new IllegalStateException("ScrollView can host only one direct child"); 3731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 3741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 3751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell super.addView(child, index, params); 3761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 3771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 3781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 3791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @return Returns true this ScrollView can be scrolled 3801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 3811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private boolean canScroll() { 3821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell View child = getChildAt(0); 3831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (child != null) { 3841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int childHeight = child.getHeight(); 3851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return getHeight() < childHeight + getPaddingTop() + getPaddingBottom(); 3861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 3871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return false; 3881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 3891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 3901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 3911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Indicates whether this ScrollView's content is stretched to fill the viewport. 3921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * 3931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @return True if the content fills the viewport, false otherwise. 3941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * 3951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @attr ref android.R.styleable#ScrollView_fillViewport 3961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 3971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public boolean isFillViewport() { 3981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return mFillViewport; 3991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 4001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 4011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 4021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Indicates this ScrollView whether it should stretch its content height to fill 4031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * the viewport or not. 4041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * 4051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param fillViewport True to stretch the content's height to the viewport's 4061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * boundaries, false otherwise. 4071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * 4081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @attr ref android.R.styleable#ScrollView_fillViewport 4091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 4101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void setFillViewport(boolean fillViewport) { 4111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (fillViewport != mFillViewport) { 4121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mFillViewport = fillViewport; 4131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell requestLayout(); 4141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 4151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 4161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 4171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 4181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @return Whether arrow scrolling will animate its transition. 4191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 4201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public boolean isSmoothScrollingEnabled() { 4211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return mSmoothScrollingEnabled; 4221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 4231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 4241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 4251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Set whether arrow scrolling will animate its transition. 4261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param smoothScrollingEnabled whether arrow scrolling will animate its transition 4271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 4281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void setSmoothScrollingEnabled(boolean smoothScrollingEnabled) { 4291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mSmoothScrollingEnabled = smoothScrollingEnabled; 4301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 4311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 4321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 4331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 4341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell super.onMeasure(widthMeasureSpec, heightMeasureSpec); 4351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 4361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (!mFillViewport) { 4371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return; 4381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 4391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 4401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int heightMode = MeasureSpec.getMode(heightMeasureSpec); 4411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (heightMode == MeasureSpec.UNSPECIFIED) { 4421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return; 4431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 4441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 4451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (getChildCount() > 0) { 4461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final View child = getChildAt(0); 4471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int height = getMeasuredHeight(); 4481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (child.getMeasuredHeight() < height) { 4491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final FrameLayout.LayoutParams lp = (LayoutParams) child.getLayoutParams(); 4501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 4511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 4521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell getPaddingLeft() + getPaddingRight(), lp.width); 4531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell height -= getPaddingTop(); 4541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell height -= getPaddingBottom(); 4551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int childHeightMeasureSpec = 4561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); 4571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 4581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 4591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 4601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 4611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 4621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 4631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 4641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public boolean dispatchKeyEvent(KeyEvent event) { 4651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // Let the focused view and/or our descendants get the key first 4661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return super.dispatchKeyEvent(event) || executeKeyEvent(event); 4671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 4681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 4691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 4701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * You can call this function yourself to have the scroll view perform 4711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * scrolling from a key event, just as if the event had been dispatched to 4721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * it by the view hierarchy. 4731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * 4741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param event The key event to execute. 4751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @return Return true if the event was handled, else false. 4761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 4771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public boolean executeKeyEvent(KeyEvent event) { 4781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mTempRect.setEmpty(); 4791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 4801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (!canScroll()) { 4811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (isFocused() && event.getKeyCode() != KeyEvent.KEYCODE_BACK) { 4821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell View currentFocused = findFocus(); 4831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (currentFocused == this) currentFocused = null; 4841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell View nextFocused = FocusFinder.getInstance().findNextFocus(this, 4851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell currentFocused, View.FOCUS_DOWN); 4861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return nextFocused != null 4871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell && nextFocused != this 4881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell && nextFocused.requestFocus(View.FOCUS_DOWN); 4891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 4901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return false; 4911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 4921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 4931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell boolean handled = false; 4941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (event.getAction() == KeyEvent.ACTION_DOWN) { 4951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell switch (event.getKeyCode()) { 4961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell case KeyEvent.KEYCODE_DPAD_UP: 4971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (!event.isAltPressed()) { 4981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell handled = arrowScroll(View.FOCUS_UP); 4991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else { 5001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell handled = fullScroll(View.FOCUS_UP); 5011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 5021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell break; 5031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell case KeyEvent.KEYCODE_DPAD_DOWN: 5041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (!event.isAltPressed()) { 5051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell handled = arrowScroll(View.FOCUS_DOWN); 5061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else { 5071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell handled = fullScroll(View.FOCUS_DOWN); 5081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 5091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell break; 5101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell case KeyEvent.KEYCODE_SPACE: 5111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell pageScroll(event.isShiftPressed() ? View.FOCUS_UP : View.FOCUS_DOWN); 5121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell break; 5131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 5141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 5151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 5161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return handled; 5171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 5181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 5191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private boolean inChild(int x, int y) { 5201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (getChildCount() > 0) { 5211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int scrollY = getScrollY(); 5221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final View child = getChildAt(0); 5231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return !(y < child.getTop() - scrollY 5241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell || y >= child.getBottom() - scrollY 5251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell || x < child.getLeft() 5261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell || x >= child.getRight()); 5271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 5281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return false; 5291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 5301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 5311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private void initOrResetVelocityTracker() { 5321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (mVelocityTracker == null) { 5331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mVelocityTracker = VelocityTracker.obtain(); 5341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else { 5351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mVelocityTracker.clear(); 5361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 5371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 5381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 5391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private void initVelocityTrackerIfNotExists() { 5401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (mVelocityTracker == null) { 5411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mVelocityTracker = VelocityTracker.obtain(); 5421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 5431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 5441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 5451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private void recycleVelocityTracker() { 5461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (mVelocityTracker != null) { 5471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mVelocityTracker.recycle(); 5481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mVelocityTracker = null; 5491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 5501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 5511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 5521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 5531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { 5541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (disallowIntercept) { 5551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell recycleVelocityTracker(); 5561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 5571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell super.requestDisallowInterceptTouchEvent(disallowIntercept); 5581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 5591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 5601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 5611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 5621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public boolean onInterceptTouchEvent(MotionEvent ev) { 5631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /* 5641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * This method JUST determines whether we want to intercept the motion. 5651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * If we return true, onMotionEvent will be called and we do the actual 5661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * scrolling there. 5671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 5681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 5691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /* 5701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Shortcut the most recurring case: the user is in the dragging 5711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * state and he is moving his finger. We want to intercept this 5721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * motion. 5731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 5741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int action = ev.getAction(); 5751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) { 5761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return true; 5771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 5781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 5791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /* 5801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Don't try to intercept touch if we can't scroll anyway. 5811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 5821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (getScrollY() == 0 && !ViewCompat.canScrollVertically(this, 1)) { 5831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return false; 5841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 5851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 5861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell switch (action & MotionEventCompat.ACTION_MASK) { 5871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell case MotionEvent.ACTION_MOVE: { 5881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /* 5891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check 5901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * whether the user has moved far enough from his original down touch. 5911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 5921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 5931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /* 5941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Locally do absolute value. mLastMotionY is set to the y value 5951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * of the down event. 5961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 5971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int activePointerId = mActivePointerId; 5981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (activePointerId == INVALID_POINTER) { 5991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // If we don't have a valid id, the touch down wasn't on content. 6001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell break; 6011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 6021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 6031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int pointerIndex = MotionEventCompat.findPointerIndex(ev, activePointerId); 6041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (pointerIndex == -1) { 6051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell Log.e(TAG, "Invalid pointerId=" + activePointerId 6061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell + " in onInterceptTouchEvent"); 6071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell break; 6081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 6091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 6101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int y = (int) MotionEventCompat.getY(ev, pointerIndex); 6111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int yDiff = Math.abs(y - mLastMotionY); 6121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (yDiff > mTouchSlop 6131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell && (getNestedScrollAxes() & ViewCompat.SCROLL_AXIS_VERTICAL) == 0) { 6141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mIsBeingDragged = true; 6151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mLastMotionY = y; 6161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell initVelocityTrackerIfNotExists(); 6171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mVelocityTracker.addMovement(ev); 6181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mNestedYOffset = 0; 6191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final ViewParent parent = getParent(); 6201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (parent != null) { 6211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell parent.requestDisallowInterceptTouchEvent(true); 6221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 6231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 6241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell break; 6251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 6261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 6271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell case MotionEvent.ACTION_DOWN: { 6281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int y = (int) ev.getY(); 6291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (!inChild((int) ev.getX(), (int) y)) { 6301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mIsBeingDragged = false; 6311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell recycleVelocityTracker(); 6321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell break; 6331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 6341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 6351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /* 6361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Remember location of down touch. 6371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * ACTION_DOWN always refers to pointer index 0. 6381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 6391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mLastMotionY = y; 6401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mActivePointerId = MotionEventCompat.getPointerId(ev, 0); 6411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 6421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell initOrResetVelocityTracker(); 6431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mVelocityTracker.addMovement(ev); 6441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /* 6451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * If being flinged and user touches the screen, initiate drag; 6461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * otherwise don't. mScroller.isFinished should be false when 6471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * being flinged. 6481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 6491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mIsBeingDragged = !mScroller.isFinished(); 6501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL); 6511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell break; 6521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 6531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 6541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell case MotionEvent.ACTION_CANCEL: 6551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell case MotionEvent.ACTION_UP: 6561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /* Release the drag */ 6571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mIsBeingDragged = false; 6581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mActivePointerId = INVALID_POINTER; 6591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell recycleVelocityTracker(); 6601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell stopNestedScroll(); 6611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell break; 6621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell case MotionEventCompat.ACTION_POINTER_UP: 6631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell onSecondaryPointerUp(ev); 6641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell break; 6651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 6661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 6671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /* 6681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * The only time we want to intercept motion events is if we are in the 6691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * drag mode. 6701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 6711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return mIsBeingDragged; 6721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 6731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 6741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 6751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public boolean onTouchEvent(MotionEvent ev) { 6761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell initVelocityTrackerIfNotExists(); 6771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 6781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell MotionEvent vtev = MotionEvent.obtain(ev); 6791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 6801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int actionMasked = MotionEventCompat.getActionMasked(ev); 6811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 6821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (actionMasked == MotionEvent.ACTION_DOWN) { 6831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mNestedYOffset = 0; 6841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 6851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell vtev.offsetLocation(0, mNestedYOffset); 6861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 6871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell switch (actionMasked) { 6881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell case MotionEvent.ACTION_DOWN: { 6891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (getChildCount() == 0) { 6901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return false; 6911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 6921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if ((mIsBeingDragged = !mScroller.isFinished())) { 6931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final ViewParent parent = getParent(); 6941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (parent != null) { 6951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell parent.requestDisallowInterceptTouchEvent(true); 6961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 6971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 6981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 6991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /* 7001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * If being flinged and user touches, stop the fling. isFinished 7011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * will be false if being flinged. 7021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 7031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (!mScroller.isFinished()) { 7041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mScroller.abortAnimation(); 7051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 7061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 7071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // Remember where the motion event started 7081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mLastMotionY = (int) ev.getY(); 7091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mActivePointerId = MotionEventCompat.getPointerId(ev, 0); 7101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL); 7111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell break; 7121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 7131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell case MotionEvent.ACTION_MOVE: 7141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int activePointerIndex = MotionEventCompat.findPointerIndex(ev, 7151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mActivePointerId); 7161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (activePointerIndex == -1) { 7171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent"); 7181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell break; 7191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 7201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 7211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int y = (int) MotionEventCompat.getY(ev, activePointerIndex); 7221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int deltaY = mLastMotionY - y; 7231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (dispatchNestedPreScroll(0, deltaY, mScrollConsumed, mScrollOffset)) { 7241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell deltaY -= mScrollConsumed[1]; 7251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell vtev.offsetLocation(0, mScrollOffset[1]); 7261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mNestedYOffset += mScrollOffset[1]; 7271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 7281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (!mIsBeingDragged && Math.abs(deltaY) > mTouchSlop) { 7291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final ViewParent parent = getParent(); 7301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (parent != null) { 7311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell parent.requestDisallowInterceptTouchEvent(true); 7321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 7331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mIsBeingDragged = true; 7341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (deltaY > 0) { 7351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell deltaY -= mTouchSlop; 7361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else { 7371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell deltaY += mTouchSlop; 7381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 7391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 7401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (mIsBeingDragged) { 7411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // Scroll to follow the motion event 7421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mLastMotionY = y - mScrollOffset[1]; 7431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 7441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int oldY = getScrollY(); 7451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int range = getScrollRange(); 7461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int overscrollMode = ViewCompat.getOverScrollMode(this); 7471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell boolean canOverscroll = overscrollMode == ViewCompat.OVER_SCROLL_ALWAYS || 7481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell (overscrollMode == ViewCompat.OVER_SCROLL_IF_CONTENT_SCROLLS && 7491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell range > 0); 7501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 7511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // Calling overScrollByCompat will call onOverScrolled, which 7521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // calls onScrollChanged if applicable. 7531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (overScrollByCompat(0, deltaY, 0, getScrollY(), 0, range, 0, 7541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 0, true) && !hasNestedScrollingParent()) { 7551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // Break our velocity if we hit a scroll barrier. 7561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mVelocityTracker.clear(); 7571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 7581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 7591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int scrolledDeltaY = getScrollY() - oldY; 7601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int unconsumedY = deltaY - scrolledDeltaY; 7611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (dispatchNestedScroll(0, scrolledDeltaY, 0, unconsumedY, mScrollOffset)) { 7621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mLastMotionY -= mScrollOffset[1]; 7631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell vtev.offsetLocation(0, mScrollOffset[1]); 7641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mNestedYOffset += mScrollOffset[1]; 7651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else if (canOverscroll) { 7661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell ensureGlows(); 7671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int pulledToY = oldY + deltaY; 7681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (pulledToY < 0) { 7691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mEdgeGlowTop.onPull((float) deltaY / getHeight(), 7701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell MotionEventCompat.getX(ev, activePointerIndex) / getWidth()); 7711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (!mEdgeGlowBottom.isFinished()) { 7721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mEdgeGlowBottom.onRelease(); 7731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 7741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else if (pulledToY > range) { 7751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mEdgeGlowBottom.onPull((float) deltaY / getHeight(), 7761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 1.f - MotionEventCompat.getX(ev, activePointerIndex) 7771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell / getWidth()); 7781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (!mEdgeGlowTop.isFinished()) { 7791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mEdgeGlowTop.onRelease(); 7801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 7811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 7821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (mEdgeGlowTop != null 7831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell && (!mEdgeGlowTop.isFinished() || !mEdgeGlowBottom.isFinished())) { 7841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell ViewCompat.postInvalidateOnAnimation(this); 7851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 7861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 7871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 7881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell break; 7891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell case MotionEvent.ACTION_UP: 7901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (mIsBeingDragged) { 7911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final VelocityTracker velocityTracker = mVelocityTracker; 7921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); 7931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int initialVelocity = (int) VelocityTrackerCompat.getYVelocity(velocityTracker, 7941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mActivePointerId); 7951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 7961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if ((Math.abs(initialVelocity) > mMinimumVelocity)) { 7971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell flingWithNestedDispatch(-initialVelocity); 7981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 7991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 8001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mActivePointerId = INVALID_POINTER; 8011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell endDrag(); 8021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 8031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell break; 8041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell case MotionEvent.ACTION_CANCEL: 8051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (mIsBeingDragged && getChildCount() > 0) { 8061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mActivePointerId = INVALID_POINTER; 8071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell endDrag(); 8081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 8091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell break; 8101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell case MotionEventCompat.ACTION_POINTER_DOWN: { 8111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int index = MotionEventCompat.getActionIndex(ev); 8121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mLastMotionY = (int) MotionEventCompat.getY(ev, index); 8131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mActivePointerId = MotionEventCompat.getPointerId(ev, index); 8141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell break; 8151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 8161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell case MotionEventCompat.ACTION_POINTER_UP: 8171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell onSecondaryPointerUp(ev); 8181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mLastMotionY = (int) MotionEventCompat.getY(ev, 8191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell MotionEventCompat.findPointerIndex(ev, mActivePointerId)); 8201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell break; 8211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 8221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 8231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (mVelocityTracker != null) { 8241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mVelocityTracker.addMovement(vtev); 8251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 8261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell vtev.recycle(); 8271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return true; 8281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 8291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 8301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private void onSecondaryPointerUp(MotionEvent ev) { 8311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int pointerIndex = (ev.getAction() & MotionEventCompat.ACTION_POINTER_INDEX_MASK) >> 8321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell MotionEventCompat.ACTION_POINTER_INDEX_SHIFT; 8331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex); 8341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (pointerId == mActivePointerId) { 8351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // This was our active pointer going up. Choose a new 8361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // active pointer and adjust accordingly. 8371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // TODO: Make this decision more intelligent. 8381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int newPointerIndex = pointerIndex == 0 ? 1 : 0; 8391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mLastMotionY = (int) MotionEventCompat.getY(ev, newPointerIndex); 8401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex); 8411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (mVelocityTracker != null) { 8421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mVelocityTracker.clear(); 8431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 8441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 8451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 8461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 8471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public boolean onGenericMotionEvent(MotionEvent event) { 8481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if ((MotionEventCompat.getSource(event) & InputDeviceCompat.SOURCE_CLASS_POINTER) != 0) { 8491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell switch (event.getAction()) { 8501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell case MotionEventCompat.ACTION_SCROLL: { 8511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (!mIsBeingDragged) { 8521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final float vscroll = MotionEventCompat.getAxisValue(event, 8531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell MotionEventCompat.AXIS_VSCROLL); 8541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (vscroll != 0) { 8551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int delta = (int) (vscroll * getVerticalScrollFactorCompat()); 8561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int range = getScrollRange(); 8571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int oldScrollY = getScrollY(); 8581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int newScrollY = oldScrollY - delta; 8591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (newScrollY < 0) { 8601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell newScrollY = 0; 8611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else if (newScrollY > range) { 8621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell newScrollY = range; 8631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 8641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (newScrollY != oldScrollY) { 8651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell super.scrollTo(getScrollX(), newScrollY); 8661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return true; 8671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 8681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 8691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 8701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 8711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 8721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 8731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return false; 8741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 8751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 8761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private float getVerticalScrollFactorCompat() { 8771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (mVerticalScrollFactor == 0) { 8781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell TypedValue outValue = new TypedValue(); 8791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final Context context = getContext(); 8801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (!context.getTheme().resolveAttribute( 8811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell android.R.attr.listPreferredItemHeight, outValue, true)) { 8821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell throw new IllegalStateException( 8831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell "Expected theme to define listPreferredItemHeight."); 8841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 8851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mVerticalScrollFactor = outValue.getDimension( 8861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell context.getResources().getDisplayMetrics()); 8871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 8881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return mVerticalScrollFactor; 8891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 8901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 8911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell protected void onOverScrolled(int scrollX, int scrollY, 8921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell boolean clampedX, boolean clampedY) { 8931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell super.scrollTo(scrollX, scrollY); 8941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 8951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 8961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell boolean overScrollByCompat(int deltaX, int deltaY, 8971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int scrollX, int scrollY, 8981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int scrollRangeX, int scrollRangeY, 8991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int maxOverScrollX, int maxOverScrollY, 9001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell boolean isTouchEvent) { 9011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int overScrollMode = ViewCompat.getOverScrollMode(this); 9021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final boolean canScrollHorizontal = 9031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell computeHorizontalScrollRange() > computeHorizontalScrollExtent(); 9041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final boolean canScrollVertical = 9051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell computeVerticalScrollRange() > computeVerticalScrollExtent(); 9061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final boolean overScrollHorizontal = overScrollMode == ViewCompat.OVER_SCROLL_ALWAYS || 9071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell (overScrollMode == ViewCompat.OVER_SCROLL_IF_CONTENT_SCROLLS && canScrollHorizontal); 9081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final boolean overScrollVertical = overScrollMode == ViewCompat.OVER_SCROLL_ALWAYS || 9091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell (overScrollMode == ViewCompat.OVER_SCROLL_IF_CONTENT_SCROLLS && canScrollVertical); 9101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 9111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int newScrollX = scrollX + deltaX; 9121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (!overScrollHorizontal) { 9131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell maxOverScrollX = 0; 9141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 9151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 9161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int newScrollY = scrollY + deltaY; 9171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (!overScrollVertical) { 9181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell maxOverScrollY = 0; 9191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 9201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 9211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // Clamp values if at the limits and record 9221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int left = -maxOverScrollX; 9231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int right = maxOverScrollX + scrollRangeX; 9241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int top = -maxOverScrollY; 9251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int bottom = maxOverScrollY + scrollRangeY; 9261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 9271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell boolean clampedX = false; 9281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (newScrollX > right) { 9291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell newScrollX = right; 9301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell clampedX = true; 9311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else if (newScrollX < left) { 9321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell newScrollX = left; 9331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell clampedX = true; 9341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 9351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 9361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell boolean clampedY = false; 9371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (newScrollY > bottom) { 9381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell newScrollY = bottom; 9391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell clampedY = true; 9401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else if (newScrollY < top) { 9411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell newScrollY = top; 9421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell clampedY = true; 9431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 9441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 9451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell onOverScrolled(newScrollX, newScrollY, clampedX, clampedY); 9461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 9471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return clampedX || clampedY; 9481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 9491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 9501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private int getScrollRange() { 9511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int scrollRange = 0; 9521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (getChildCount() > 0) { 9531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell View child = getChildAt(0); 9541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollRange = Math.max(0, 9551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell child.getHeight() - (getHeight() - getPaddingBottom() - getPaddingTop())); 9561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 9571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return scrollRange; 9581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 9591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 9601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 9611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * <p> 9621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Finds the next focusable component that fits in the specified bounds. 9631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * </p> 9641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * 9651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param topFocus look for a candidate is the one at the top of the bounds 9661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * if topFocus is true, or at the bottom of the bounds if topFocus is 9671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * false 9681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param top the top offset of the bounds in which a focusable must be 9691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * found 9701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param bottom the bottom offset of the bounds in which a focusable must 9711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * be found 9721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @return the next focusable component in the bounds or null if none can 9731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * be found 9741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 9751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private View findFocusableViewInBounds(boolean topFocus, int top, int bottom) { 9761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 9771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell List<View> focusables = getFocusables(View.FOCUS_FORWARD); 9781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell View focusCandidate = null; 9791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 9801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /* 9811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * A fully contained focusable is one where its top is below the bound's 9821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * top, and its bottom is above the bound's bottom. A partially 9831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * contained focusable is one where some part of it is within the 9841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * bounds, but it also has some part that is not within bounds. A fully contained 9851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * focusable is preferred to a partially contained focusable. 9861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 9871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell boolean foundFullyContainedFocusable = false; 9881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 9891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int count = focusables.size(); 9901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell for (int i = 0; i < count; i++) { 9911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell View view = focusables.get(i); 9921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int viewTop = view.getTop(); 9931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int viewBottom = view.getBottom(); 9941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 9951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (top < viewBottom && viewTop < bottom) { 9961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /* 9971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * the focusable is in the target area, it is a candidate for 9981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * focusing 9991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 10001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 10011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final boolean viewIsFullyContained = (top < viewTop) && 10021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell (viewBottom < bottom); 10031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 10041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (focusCandidate == null) { 10051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /* No candidate, take this one */ 10061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell focusCandidate = view; 10071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell foundFullyContainedFocusable = viewIsFullyContained; 10081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else { 10091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final boolean viewIsCloserToBoundary = 10101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell (topFocus && viewTop < focusCandidate.getTop()) || 10111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell (!topFocus && viewBottom > focusCandidate 10121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell .getBottom()); 10131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 10141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (foundFullyContainedFocusable) { 10151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (viewIsFullyContained && viewIsCloserToBoundary) { 10161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /* 10171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * We're dealing with only fully contained views, so 10181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * it has to be closer to the boundary to beat our 10191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * candidate 10201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 10211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell focusCandidate = view; 10221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 10231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else { 10241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (viewIsFullyContained) { 10251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /* Any fully contained view beats a partially contained view */ 10261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell focusCandidate = view; 10271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell foundFullyContainedFocusable = true; 10281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else if (viewIsCloserToBoundary) { 10291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /* 10301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Partially contained view beats another partially 10311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * contained view if it's closer 10321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 10331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell focusCandidate = view; 10341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 10351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 10361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 10371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 10381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 10391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 10401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return focusCandidate; 10411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 10421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 10431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 10441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * <p>Handles scrolling in response to a "page up/down" shortcut press. This 10451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * method will scroll the view by one page up or down and give the focus 10461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * to the topmost/bottommost component in the new visible area. If no 10471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * component is a good candidate for focus, this scrollview reclaims the 10481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * focus.</p> 10491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * 10501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param direction the scroll direction: {@link android.view.View#FOCUS_UP} 10511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * to go one page up or 10521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * {@link android.view.View#FOCUS_DOWN} to go one page down 10531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @return true if the key event is consumed by this method, false otherwise 10541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 10551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public boolean pageScroll(int direction) { 10561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell boolean down = direction == View.FOCUS_DOWN; 10571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int height = getHeight(); 10581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 10591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (down) { 10601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mTempRect.top = getScrollY() + height; 10611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int count = getChildCount(); 10621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (count > 0) { 10631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell View view = getChildAt(count - 1); 10641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (mTempRect.top + height > view.getBottom()) { 10651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mTempRect.top = view.getBottom() - height; 10661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 10671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 10681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else { 10691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mTempRect.top = getScrollY() - height; 10701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (mTempRect.top < 0) { 10711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mTempRect.top = 0; 10721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 10731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 10741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mTempRect.bottom = mTempRect.top + height; 10751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 10761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return scrollAndFocus(direction, mTempRect.top, mTempRect.bottom); 10771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 10781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 10791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 10801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * <p>Handles scrolling in response to a "home/end" shortcut press. This 10811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * method will scroll the view to the top or bottom and give the focus 10821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * to the topmost/bottommost component in the new visible area. If no 10831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * component is a good candidate for focus, this scrollview reclaims the 10841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * focus.</p> 10851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * 10861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param direction the scroll direction: {@link android.view.View#FOCUS_UP} 10871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * to go the top of the view or 10881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * {@link android.view.View#FOCUS_DOWN} to go the bottom 10891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @return true if the key event is consumed by this method, false otherwise 10901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 10911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public boolean fullScroll(int direction) { 10921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell boolean down = direction == View.FOCUS_DOWN; 10931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int height = getHeight(); 10941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 10951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mTempRect.top = 0; 10961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mTempRect.bottom = height; 10971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 10981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (down) { 10991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int count = getChildCount(); 11001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (count > 0) { 11011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell View view = getChildAt(count - 1); 11021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mTempRect.bottom = view.getBottom() + getPaddingBottom(); 11031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mTempRect.top = mTempRect.bottom - height; 11041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 11051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 11061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 11071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return scrollAndFocus(direction, mTempRect.top, mTempRect.bottom); 11081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 11091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 11101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 11111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * <p>Scrolls the view to make the area defined by <code>top</code> and 11121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * <code>bottom</code> visible. This method attempts to give the focus 11131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * to a component visible in this area. If no component can be focused in 11141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * the new visible area, the focus is reclaimed by this ScrollView.</p> 11151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * 11161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param direction the scroll direction: {@link android.view.View#FOCUS_UP} 11171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * to go upward, {@link android.view.View#FOCUS_DOWN} to downward 11181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param top the top offset of the new area to be made visible 11191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param bottom the bottom offset of the new area to be made visible 11201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @return true if the key event is consumed by this method, false otherwise 11211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 11221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private boolean scrollAndFocus(int direction, int top, int bottom) { 11231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell boolean handled = true; 11241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 11251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int height = getHeight(); 11261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int containerTop = getScrollY(); 11271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int containerBottom = containerTop + height; 11281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell boolean up = direction == View.FOCUS_UP; 11291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 11301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell View newFocused = findFocusableViewInBounds(up, top, bottom); 11311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (newFocused == null) { 11321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell newFocused = this; 11331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 11341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 11351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (top >= containerTop && bottom <= containerBottom) { 11361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell handled = false; 11371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else { 11381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int delta = up ? (top - containerTop) : (bottom - containerBottom); 11391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell doScrollY(delta); 11401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 11411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 11421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (newFocused != findFocus()) newFocused.requestFocus(direction); 11431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 11441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return handled; 11451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 11461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 11471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 11481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Handle scrolling in response to an up or down arrow click. 11491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * 11501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param direction The direction corresponding to the arrow key that was 11511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * pressed 11521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @return True if we consumed the event, false otherwise 11531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 11541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public boolean arrowScroll(int direction) { 11551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 11561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell View currentFocused = findFocus(); 11571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (currentFocused == this) currentFocused = null; 11581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 11591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused, direction); 11601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 11611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int maxJump = getMaxScrollAmount(); 11621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 11631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (nextFocused != null && isWithinDeltaOfScreen(nextFocused, maxJump, getHeight())) { 11641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell nextFocused.getDrawingRect(mTempRect); 11651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell offsetDescendantRectToMyCoords(nextFocused, mTempRect); 11661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect); 11671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell doScrollY(scrollDelta); 11681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell nextFocused.requestFocus(direction); 11691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else { 11701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // no new focus 11711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int scrollDelta = maxJump; 11721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 11731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (direction == View.FOCUS_UP && getScrollY() < scrollDelta) { 11741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollDelta = getScrollY(); 11751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else if (direction == View.FOCUS_DOWN) { 11761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (getChildCount() > 0) { 11771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int daBottom = getChildAt(0).getBottom(); 11781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int screenBottom = getScrollY() + getHeight() - getPaddingBottom(); 11791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (daBottom - screenBottom < maxJump) { 11801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollDelta = daBottom - screenBottom; 11811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 11821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 11831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 11841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (scrollDelta == 0) { 11851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return false; 11861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 11871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell doScrollY(direction == View.FOCUS_DOWN ? scrollDelta : -scrollDelta); 11881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 11891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 11901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (currentFocused != null && currentFocused.isFocused() 11911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell && isOffScreen(currentFocused)) { 11921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // previously focused item still has focus and is off screen, give 11931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // it up (take it back to ourselves) 11941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // (also, need to temporarily force FOCUS_BEFORE_DESCENDANTS so we are 11951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // sure to 11961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // get it) 11971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int descendantFocusability = getDescendantFocusability(); // save 11981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS); 11991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell requestFocus(); 12001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell setDescendantFocusability(descendantFocusability); // restore 12011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 12021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return true; 12031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 12041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 12051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 12061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @return whether the descendant of this scroll view is scrolled off 12071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * screen. 12081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 12091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private boolean isOffScreen(View descendant) { 12101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return !isWithinDeltaOfScreen(descendant, 0, getHeight()); 12111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 12121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 12131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 12141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @return whether the descendant of this scroll view is within delta 12151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * pixels of being on the screen. 12161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 12171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private boolean isWithinDeltaOfScreen(View descendant, int delta, int height) { 12181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell descendant.getDrawingRect(mTempRect); 12191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell offsetDescendantRectToMyCoords(descendant, mTempRect); 12201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 12211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return (mTempRect.bottom + delta) >= getScrollY() 12221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell && (mTempRect.top - delta) <= (getScrollY() + height); 12231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 12241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 12251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 12261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Smooth scroll by a Y delta 12271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * 12281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param delta the number of pixels to scroll by on the Y axis 12291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 12301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private void doScrollY(int delta) { 12311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (delta != 0) { 12321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (mSmoothScrollingEnabled) { 12331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell smoothScrollBy(0, delta); 12341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else { 12351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollBy(0, delta); 12361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 12371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 12381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 12391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 12401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 12411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Like {@link View#scrollBy}, but scroll smoothly instead of immediately. 12421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * 12431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param dx the number of pixels to scroll by on the X axis 12441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param dy the number of pixels to scroll by on the Y axis 12451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 12461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public final void smoothScrollBy(int dx, int dy) { 12471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (getChildCount() == 0) { 12481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // Nothing to do. 12491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return; 12501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 12511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell long duration = AnimationUtils.currentAnimationTimeMillis() - mLastScroll; 12521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (duration > ANIMATED_SCROLL_GAP) { 12531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int height = getHeight() - getPaddingBottom() - getPaddingTop(); 12541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int bottom = getChildAt(0).getHeight(); 12551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int maxY = Math.max(0, bottom - height); 12561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int scrollY = getScrollY(); 12571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell dy = Math.max(0, Math.min(scrollY + dy, maxY)) - scrollY; 12581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 12591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mScroller.startScroll(getScrollX(), scrollY, 0, dy); 12601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell ViewCompat.postInvalidateOnAnimation(this); 12611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else { 12621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (!mScroller.isFinished()) { 12631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mScroller.abortAnimation(); 12641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 12651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollBy(dx, dy); 12661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 12671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mLastScroll = AnimationUtils.currentAnimationTimeMillis(); 12681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 12691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 12701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 12711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Like {@link #scrollTo}, but scroll smoothly instead of immediately. 12721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * 12731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param x the position where to scroll on the X axis 12741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param y the position where to scroll on the Y axis 12751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 12761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public final void smoothScrollTo(int x, int y) { 12771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell smoothScrollBy(x - getScrollX(), y - getScrollY()); 12781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 12791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 12801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 12811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * <p>The scroll range of a scroll view is the overall height of all of its 12821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * children.</p> 12831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 12841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 12851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell protected int computeVerticalScrollRange() { 12861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int count = getChildCount(); 12871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int contentHeight = getHeight() - getPaddingBottom() - getPaddingTop(); 12881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (count == 0) { 12891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return contentHeight; 12901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 12911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 12921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int scrollRange = getChildAt(0).getBottom(); 12931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int scrollY = getScrollY(); 12941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int overscrollBottom = Math.max(0, scrollRange - contentHeight); 12951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (scrollY < 0) { 12961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollRange -= scrollY; 12971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else if (scrollY > overscrollBottom) { 12981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollRange += scrollY - overscrollBottom; 12991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 13001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 13011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return scrollRange; 13021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 13031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 13041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 13051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell protected int computeVerticalScrollOffset() { 13061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return Math.max(0, super.computeVerticalScrollOffset()); 13071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 13081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 13091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 13101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) { 13111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell ViewGroup.LayoutParams lp = child.getLayoutParams(); 13121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 13131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int childWidthMeasureSpec; 13141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int childHeightMeasureSpec; 13151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 13161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, getPaddingLeft() 13171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell + getPaddingRight(), lp.width); 13181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 13191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); 13201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 13211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 13221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 13231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 13241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 13251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, 13261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int parentHeightMeasureSpec, int heightUsed) { 13271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); 13281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 13291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, 13301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell getPaddingLeft() + getPaddingRight() + lp.leftMargin + lp.rightMargin 13311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell + widthUsed, lp.width); 13321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec( 13331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell lp.topMargin + lp.bottomMargin, MeasureSpec.UNSPECIFIED); 13341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 13351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 13361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 13371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 13381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 13391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void computeScroll() { 13401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (mScroller.computeScrollOffset()) { 13411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int oldX = getScrollX(); 13421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int oldY = getScrollY(); 13431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int x = mScroller.getCurrX(); 13441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int y = mScroller.getCurrY(); 13451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 13461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (oldX != x || oldY != y) { 13471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int range = getScrollRange(); 13481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int overscrollMode = ViewCompat.getOverScrollMode(this); 13491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final boolean canOverscroll = overscrollMode == ViewCompat.OVER_SCROLL_ALWAYS || 13501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell (overscrollMode == ViewCompat.OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0); 13511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 13521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell overScrollByCompat(x - oldX, y - oldY, oldX, oldY, 0, range, 13531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 0, 0, false); 13541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 13551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (canOverscroll) { 13561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell ensureGlows(); 13571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (y <= 0 && oldY > 0) { 13581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mEdgeGlowTop.onAbsorb((int) mScroller.getCurrVelocity()); 13591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else if (y >= range && oldY < range) { 13601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mEdgeGlowBottom.onAbsorb((int) mScroller.getCurrVelocity()); 13611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 13621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 13631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 13641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 13651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 13661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 13671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 13681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Scrolls the view to the given child. 13691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * 13701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param child the View to scroll to 13711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 13721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private void scrollToChild(View child) { 13731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell child.getDrawingRect(mTempRect); 13741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 13751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /* Offset from child's local coordinates to ScrollView coordinates */ 13761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell offsetDescendantRectToMyCoords(child, mTempRect); 13771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 13781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect); 13791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 13801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (scrollDelta != 0) { 13811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollBy(0, scrollDelta); 13821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 13831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 13841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 13851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 13861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * If rect is off screen, scroll just enough to get it (or at least the 13871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * first screen size chunk of it) on screen. 13881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * 13891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param rect The rectangle. 13901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param immediate True to scroll immediately without animation 13911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @return true if scrolling was performed 13921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 13931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private boolean scrollToChildRect(Rect rect, boolean immediate) { 13941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int delta = computeScrollDeltaToGetChildRectOnScreen(rect); 13951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final boolean scroll = delta != 0; 13961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (scroll) { 13971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (immediate) { 13981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollBy(0, delta); 13991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else { 14001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell smoothScrollBy(0, delta); 14011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 14021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 14031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return scroll; 14041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 14051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 14061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 14071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Compute the amount to scroll in the Y direction in order to get 14081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * a rectangle completely on the screen (or, if taller than the screen, 14091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * at least the first screen size chunk of it). 14101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * 14111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param rect The rect. 14121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @return The scroll delta. 14131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 14141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell protected int computeScrollDeltaToGetChildRectOnScreen(Rect rect) { 14151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (getChildCount() == 0) return 0; 14161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 14171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int height = getHeight(); 14181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int screenTop = getScrollY(); 14191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int screenBottom = screenTop + height; 14201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 14211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int fadingEdge = getVerticalFadingEdgeLength(); 14221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 14231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // leave room for top fading edge as long as rect isn't at very top 14241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (rect.top > 0) { 14251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell screenTop += fadingEdge; 14261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 14271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 14281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // leave room for bottom fading edge as long as rect isn't at very bottom 14291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (rect.bottom < getChildAt(0).getHeight()) { 14301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell screenBottom -= fadingEdge; 14311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 14321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 14331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int scrollYDelta = 0; 14341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 14351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (rect.bottom > screenBottom && rect.top > screenTop) { 14361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // need to move down to get it in view: move down just enough so 14371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // that the entire rectangle is in view (or at least the first 14381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // screen size chunk). 14391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 14401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (rect.height() > height) { 14411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // just enough to get screen size chunk on 14421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollYDelta += (rect.top - screenTop); 14431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else { 14441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // get entire rect at bottom of screen 14451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollYDelta += (rect.bottom - screenBottom); 14461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 14471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 14481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // make sure we aren't scrolling beyond the end of our content 14491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int bottom = getChildAt(0).getBottom(); 14501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int distanceToBottom = bottom - screenBottom; 14511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollYDelta = Math.min(scrollYDelta, distanceToBottom); 14521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 14531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else if (rect.top < screenTop && rect.bottom < screenBottom) { 14541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // need to move up to get it in view: move up just enough so that 14551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // entire rectangle is in view (or at least the first screen 14561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // size chunk of it). 14571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 14581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (rect.height() > height) { 14591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // screen size chunk 14601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollYDelta -= (screenBottom - rect.bottom); 14611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else { 14621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // entire rect at top 14631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollYDelta -= (screenTop - rect.top); 14641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 14651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 14661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // make sure we aren't scrolling any further than the top our content 14671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollYDelta = Math.max(scrollYDelta, -getScrollY()); 14681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 14691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return scrollYDelta; 14701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 14711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 14721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 14731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void requestChildFocus(View child, View focused) { 14741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (!mIsLayoutDirty) { 14751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollToChild(focused); 14761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else { 14771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // The child may not be laid out yet, we can't compute the scroll yet 14781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mChildToScrollTo = focused; 14791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 14801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell super.requestChildFocus(child, focused); 14811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 14821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 14831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 14841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 14851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * When looking for focus in children of a scroll view, need to be a little 14861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * more careful not to give focus to something that is scrolled off screen. 14871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * 14881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * This is more expensive than the default {@link android.view.ViewGroup} 14891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * implementation, otherwise this behavior might have been made the default. 14901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 14911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 14921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell protected boolean onRequestFocusInDescendants(int direction, 14931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell Rect previouslyFocusedRect) { 14941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 14951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // convert from forward / backward notation to up / down / left / right 14961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // (ugh). 14971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (direction == View.FOCUS_FORWARD) { 14981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell direction = View.FOCUS_DOWN; 14991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else if (direction == View.FOCUS_BACKWARD) { 15001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell direction = View.FOCUS_UP; 15011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 15021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 15031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final View nextFocus = previouslyFocusedRect == null ? 15041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell FocusFinder.getInstance().findNextFocus(this, null, direction) : 15051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell FocusFinder.getInstance().findNextFocusFromRect(this, 15061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell previouslyFocusedRect, direction); 15071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 15081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (nextFocus == null) { 15091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return false; 15101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 15111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 15121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (isOffScreen(nextFocus)) { 15131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return false; 15141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 15151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 15161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return nextFocus.requestFocus(direction, previouslyFocusedRect); 15171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 15181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 15191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 15201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public boolean requestChildRectangleOnScreen(View child, Rect rectangle, 15211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell boolean immediate) { 15221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // offset into coordinate space of this scroll view 15231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell rectangle.offset(child.getLeft() - child.getScrollX(), 15241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell child.getTop() - child.getScrollY()); 15251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 15261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return scrollToChildRect(rectangle, immediate); 15271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 15281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 15291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 15301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void requestLayout() { 15311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mIsLayoutDirty = true; 15321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell super.requestLayout(); 15331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 15341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 15351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 15361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell protected void onLayout(boolean changed, int l, int t, int r, int b) { 15371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell super.onLayout(changed, l, t, r, b); 15381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mIsLayoutDirty = false; 15391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // Give a child focus if it needs it 15401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (mChildToScrollTo != null && isViewDescendantOf(mChildToScrollTo, this)) { 15411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollToChild(mChildToScrollTo); 15421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 15431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mChildToScrollTo = null; 15441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 15451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (!mIsLaidOut) { 15461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (mSavedState != null) { 15471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollTo(getScrollX(), mSavedState.scrollPosition); 15481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mSavedState = null; 15491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } // mScrollY default value is "0" 15501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 15511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int childHeight = (getChildCount() > 0) ? getChildAt(0).getMeasuredHeight() : 0; 15521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int scrollRange = Math.max(0, 15531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell childHeight - (b - t - getPaddingBottom() - getPaddingTop())); 15541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 15551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // Don't forget to clamp 15561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (getScrollY() > scrollRange) { 15571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollTo(getScrollX(), scrollRange); 15581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else if (getScrollY() < 0) { 15591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollTo(getScrollX(), 0); 15601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 15611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 15621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 15631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // Calling this with the present values causes it to re-claim them 15641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollTo(getScrollX(), getScrollY()); 15651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mIsLaidOut = true; 15661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 15671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 15681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 15691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void onAttachedToWindow() { 15701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mIsLaidOut = false; 15711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 15721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 15731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 15741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell protected void onSizeChanged(int w, int h, int oldw, int oldh) { 15751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell super.onSizeChanged(w, h, oldw, oldh); 15761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 15771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell View currentFocused = findFocus(); 15781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (null == currentFocused || this == currentFocused) 15791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return; 15801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 15811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // If the currently-focused view was visible on the screen when the 15821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // screen was at the old height, then scroll the screen to make that 15831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // view visible with the new screen height. 15841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (isWithinDeltaOfScreen(currentFocused, 0, oldh)) { 15851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell currentFocused.getDrawingRect(mTempRect); 15861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell offsetDescendantRectToMyCoords(currentFocused, mTempRect); 15871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect); 15881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell doScrollY(scrollDelta); 15891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 15901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 15911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 15921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 15931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Return true if child is a descendant of parent, (or equal to the parent). 15941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 15951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private static boolean isViewDescendantOf(View child, View parent) { 15961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (child == parent) { 15971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return true; 15981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 15991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 16001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final ViewParent theParent = child.getParent(); 16011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return (theParent instanceof ViewGroup) && isViewDescendantOf((View) theParent, parent); 16021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 16031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 16041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 16051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Fling the scroll view 16061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * 16071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * @param velocityY The initial velocity in the Y direction. Positive 16081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * numbers mean that the finger/cursor is moving down the screen, 16091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * which means we want to scroll towards the top. 16101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 16111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void fling(int velocityY) { 16121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (getChildCount() > 0) { 16131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int height = getHeight() - getPaddingBottom() - getPaddingTop(); 16141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell int bottom = getChildAt(0).getHeight(); 16151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 16161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mScroller.fling(getScrollX(), getScrollY(), 0, velocityY, 0, 0, 0, 16171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell Math.max(0, bottom - height), 0, height/2); 16181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 16191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell ViewCompat.postInvalidateOnAnimation(this); 16201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 16211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 16221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 16231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private void flingWithNestedDispatch(int velocityY) { 16241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int scrollY = getScrollY(); 16251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final boolean canFling = (scrollY > 0 || velocityY > 0) && 16261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell (scrollY < getScrollRange() || velocityY < 0); 16271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (!dispatchNestedPreFling(0, velocityY)) { 16281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell dispatchNestedFling(0, velocityY, canFling); 16291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (canFling) { 16301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell fling(velocityY); 16311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 16321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 16331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 16341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 16351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private void endDrag() { 16361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mIsBeingDragged = false; 16371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 16381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell recycleVelocityTracker(); 16391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 16401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (mEdgeGlowTop != null) { 16411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mEdgeGlowTop.onRelease(); 16421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mEdgeGlowBottom.onRelease(); 16431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 16441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 16451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 16461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /** 16471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * {@inheritDoc} 16481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * 16491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * <p>This version also clamps the scrolling to the bounds of our child. 16501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 16511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 16521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void scrollTo(int x, int y) { 16531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell // we rely on the fact the View.scrollBy calls scrollTo. 16541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (getChildCount() > 0) { 16551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell View child = getChildAt(0); 16561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell x = clamp(x, getWidth() - getPaddingRight() - getPaddingLeft(), child.getWidth()); 16571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell y = clamp(y, getHeight() - getPaddingBottom() - getPaddingTop(), child.getHeight()); 16581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (x != getScrollX() || y != getScrollY()) { 16591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell super.scrollTo(x, y); 16601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 16611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 16621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 16631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 16641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private void ensureGlows() { 16651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (ViewCompat.getOverScrollMode(this) != ViewCompat.OVER_SCROLL_NEVER) { 16661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (mEdgeGlowTop == null) { 16671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell Context context = getContext(); 16681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mEdgeGlowTop = new EdgeEffectCompat(context); 16691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mEdgeGlowBottom = new EdgeEffectCompat(context); 16701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 16711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } else { 16721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mEdgeGlowTop = null; 16731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mEdgeGlowBottom = null; 16741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 16751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 16761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 16771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 16781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void draw(Canvas canvas) { 16791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell super.draw(canvas); 16801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (mEdgeGlowTop != null) { 16811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int scrollY = getScrollY(); 16821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (!mEdgeGlowTop.isFinished()) { 16831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int restoreCount = canvas.save(); 16841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int width = getWidth() - getPaddingLeft() - getPaddingRight(); 16851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 16861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell canvas.translate(getPaddingLeft(), Math.min(0, scrollY)); 16871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mEdgeGlowTop.setSize(width, getHeight()); 16881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (mEdgeGlowTop.draw(canvas)) { 16891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell ViewCompat.postInvalidateOnAnimation(this); 16901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 16911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell canvas.restoreToCount(restoreCount); 16921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 16931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (!mEdgeGlowBottom.isFinished()) { 16941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int restoreCount = canvas.save(); 16951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int width = getWidth() - getPaddingLeft() - getPaddingRight(); 16961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int height = getHeight(); 16971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 16981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell canvas.translate(-width + getPaddingLeft(), 16991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell Math.max(getScrollRange(), scrollY) + height); 17001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell canvas.rotate(180, width, 0); 17011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mEdgeGlowBottom.setSize(width, height); 17021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (mEdgeGlowBottom.draw(canvas)) { 17031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell ViewCompat.postInvalidateOnAnimation(this); 17041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 17051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell canvas.restoreToCount(restoreCount); 17061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 17071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 17081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 17091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 17101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell private static int clamp(int n, int my, int child) { 17111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (my >= child || n < 0) { 17121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /* my >= child is this case: 17131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * |--------------- me ---------------| 17141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * |------ child ------| 17151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * or 17161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * |--------------- me ---------------| 17171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * |------ child ------| 17181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * or 17191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * |--------------- me ---------------| 17201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * |------ child ------| 17211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * 17221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * n < 0 is this case: 17231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * |------ me ------| 17241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * |-------- child --------| 17251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * |-- mScrollX --| 17261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 17271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return 0; 17281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 17291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if ((my+n) > child) { 17301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell /* this case: 17311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * |------ me ------| 17321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * |------ child ------| 17331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * |-- mScrollX --| 17341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */ 17351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return child-my; 17361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 17371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return n; 17381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 17391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 17401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 17411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell protected void onRestoreInstanceState(Parcelable state) { 17421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell SavedState ss = (SavedState) state; 17431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell super.onRestoreInstanceState(ss.getSuperState()); 17441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell mSavedState = ss; 17451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell requestLayout(); 17461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 17471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 17481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 17491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell protected Parcelable onSaveInstanceState() { 17501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell Parcelable superState = super.onSaveInstanceState(); 17511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell SavedState ss = new SavedState(superState); 17521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell ss.scrollPosition = getScrollY(); 17531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return ss; 17541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 17551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 17561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell static class SavedState extends BaseSavedState { 17571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public int scrollPosition; 17581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 17591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell SavedState(Parcelable superState) { 17601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell super(superState); 17611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 17621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 17631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public SavedState(Parcel source) { 17641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell super(source); 17651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell scrollPosition = source.readInt(); 17661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 17671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 17681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 17691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void writeToParcel(Parcel dest, int flags) { 17701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell super.writeToParcel(dest, flags); 17711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell dest.writeInt(scrollPosition); 17721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 17731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 17741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 17751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public String toString() { 17761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return "HorizontalScrollView.SavedState{" 17771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell + Integer.toHexString(System.identityHashCode(this)) 17781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell + " scrollPosition=" + scrollPosition + "}"; 17791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 17801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 17811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public static final Parcelable.Creator<SavedState> CREATOR 17821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell = new Parcelable.Creator<SavedState>() { 17831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public SavedState createFromParcel(Parcel in) { 17841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return new SavedState(in); 17851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 17861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 17871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public SavedState[] newArray(int size) { 17881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return new SavedState[size]; 17891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 17901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell }; 17911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 17921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 17931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell static class AccessibilityDelegate extends AccessibilityDelegateCompat { 17941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 17951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public boolean performAccessibilityAction(View host, int action, Bundle arguments) { 17961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (super.performAccessibilityAction(host, action, arguments)) { 17971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return true; 17981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 17991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final NestedScrollView nsvHost = (NestedScrollView) host; 18001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (!nsvHost.isEnabled()) { 18011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return false; 18021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 18031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell switch (action) { 18041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD: { 18051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int viewportHeight = nsvHost.getHeight() - nsvHost.getPaddingBottom() 18061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell - nsvHost.getPaddingTop(); 18071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int targetScrollY = Math.min(nsvHost.getScrollY() + viewportHeight, 18081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell nsvHost.getScrollRange()); 18091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (targetScrollY != nsvHost.getScrollY()) { 18101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell nsvHost.smoothScrollTo(0, targetScrollY); 18111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return true; 18121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 18131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 18141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return false; 18151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD: { 18161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int viewportHeight = nsvHost.getHeight() - nsvHost.getPaddingBottom() 18171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell - nsvHost.getPaddingTop(); 18181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int targetScrollY = Math.max(nsvHost.getScrollY() - viewportHeight, 0); 18191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (targetScrollY != nsvHost.getScrollY()) { 18201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell nsvHost.smoothScrollTo(0, targetScrollY); 18211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return true; 18221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 18231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 18241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return false; 18251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 18261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell return false; 18271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 18281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 18291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 18301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) { 18311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell super.onInitializeAccessibilityNodeInfo(host, info); 18321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final NestedScrollView nsvHost = (NestedScrollView) host; 18331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell info.setClassName(ScrollView.class.getName()); 18341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (nsvHost.isEnabled()) { 18351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final int scrollRange = nsvHost.getScrollRange(); 18361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (scrollRange > 0) { 18371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell info.setScrollable(true); 18381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (nsvHost.getScrollY() > 0) { 18391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD); 18401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 18411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell if (nsvHost.getScrollY() < scrollRange) { 18421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD); 18431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 18441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 18451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 18461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 18471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell 18481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell @Override 18491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) { 18501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell super.onInitializeAccessibilityEvent(host, event); 18511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final NestedScrollView nsvHost = (NestedScrollView) host; 18521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell event.setClassName(ScrollView.class.getName()); 18531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final AccessibilityRecordCompat record = AccessibilityEventCompat.asRecord(event); 18541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell final boolean scrollable = nsvHost.getScrollRange() > 0; 18551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell record.setScrollable(scrollable); 18561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell record.setScrollX(nsvHost.getScrollX()); 18571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell record.setScrollY(nsvHost.getScrollY()); 18581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell record.setMaxScrollX(nsvHost.getScrollX()); 18591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell record.setMaxScrollY(nsvHost.getScrollRange()); 18601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 18611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell } 18621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell} 1863