Skip to content

Commit 14719d3

Browse files
author
Jeremy Walker
committed
Adds support for page up, page down, move home, and move end key events.
Bug: 205800548 Test: Added additional unit tests. Change-Id: I72cbba7bf0a0a3763755e3c23f4121ec9577e6ab
1 parent d70fbfd commit 14719d3

File tree

3 files changed

+396
-60
lines changed

3 files changed

+396
-60
lines changed

coordinatorlayout/coordinatorlayout/src/main/java/androidx/coordinatorlayout/widget/CoordinatorLayout.java

+77-35
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ public class CoordinatorLayout extends ViewGroup implements NestedScrollingParen
115115
NestedScrollingParent3 {
116116
static final String TAG = "CoordinatorLayout";
117117
static final String WIDGET_PACKAGE_NAME;
118+
// For the UP/DOWN keys, we scroll 1/10th of the screen.
119+
private static final float KEY_SCROLL_FRACTION_AMOUNT = 0.1f;
118120

119121
static {
120122
final Package pkg = CoordinatorLayout.class.getPackage();
@@ -181,6 +183,13 @@ private static void releaseTempRect(@NonNull Rect rect) {
181183
// This only exist to prevent GC and object instantiation costs that are present before API 21.
182184
private final int[] mNestedScrollingV2ConsumedCompat = new int[2];
183185

186+
// Array to be mutated by calls to nested scrolling related methods triggered by key events.
187+
// Because these scrolling events rely on lower level methods using mBehaviorConsumed, we need
188+
// a separate variable to save memory. As with the above, this only exist to prevent GC and
189+
// object instantiation costs that are
190+
// present before API 21.
191+
private final int[] mKeyTriggeredScrollConsumed = new int[2];
192+
184193
private boolean mDisallowInterceptReset;
185194

186195
private boolean mIsAttachedToWindow;
@@ -1945,47 +1954,46 @@ public boolean dispatchKeyEvent(
19451954
if (event.getAction() == KeyEvent.ACTION_DOWN) {
19461955
switch (event.getKeyCode()) {
19471956
case KeyEvent.KEYCODE_DPAD_UP:
1948-
case KeyEvent.KEYCODE_DPAD_DOWN:
1949-
case KeyEvent.KEYCODE_SPACE:
1950-
1951-
int yScrollDelta;
1952-
1953-
if (event.getKeyCode() == KeyEvent.KEYCODE_SPACE) {
1954-
if (event.isShiftPressed()) {
1955-
// Places the CoordinatorLayout at the top of the available
1956-
// content.
1957-
// Note: The delta may represent a value that would overshoot the
1958-
// top of the screen, but the children only use as much of the
1959-
// delta as they can support, so it will always go exactly to the
1960-
// top.
1961-
yScrollDelta = -getFullContentHeight();
1962-
} else {
1963-
// Places the CoordinatorLayout at the bottom of the available
1964-
// content.
1965-
yScrollDelta = getFullContentHeight() - getHeight();
1966-
}
1967-
1968-
} else if (event.isAltPressed()) { // For UP and DOWN KeyEvents
1969-
// Full page scroll
1970-
yScrollDelta = getHeight();
1957+
if (event.isAltPressed()) {
1958+
// Inverse to move up the screen
1959+
handled = moveVertically(-pageDelta());
1960+
} else {
1961+
// Inverse to move up the screen
1962+
handled = moveVertically(-lineDelta());
1963+
}
1964+
break;
19711965

1966+
case KeyEvent.KEYCODE_DPAD_DOWN:
1967+
if (event.isAltPressed()) {
1968+
handled = moveVertically(pageDelta());
19721969
} else {
1973-
// Regular arrow scroll
1974-
yScrollDelta = (int) (getHeight() * 0.1f);
1970+
handled = moveVertically(lineDelta());
19751971
}
1972+
break;
1973+
1974+
case KeyEvent.KEYCODE_PAGE_UP:
1975+
// Inverse to move up the screen
1976+
handled = moveVertically(-pageDelta());
1977+
break;
19761978

1977-
View focusedView = findDeepestFocusedChild(this);
1979+
case KeyEvent.KEYCODE_PAGE_DOWN:
1980+
handled = moveVertically(pageDelta());
1981+
break;
19781982

1979-
// Convert delta to negative if the key event is UP.
1980-
if (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_UP) {
1981-
yScrollDelta = -yScrollDelta;
1983+
case KeyEvent.KEYCODE_SPACE:
1984+
if (event.isShiftPressed()) {
1985+
handled = moveVertically(distanceToTop());
1986+
} else {
1987+
handled = moveVertically(distanceToBottom());
19821988
}
1989+
break;
19831990

1984-
handled = manuallyTriggersNestedScrollFromKeyEvent(
1985-
focusedView,
1986-
yScrollDelta
1987-
);
1991+
case KeyEvent.KEYCODE_MOVE_HOME:
1992+
handled = moveVertically(distanceToTop());
1993+
break;
19881994

1995+
case KeyEvent.KEYCODE_MOVE_END:
1996+
handled = moveVertically(distanceToBottom());
19891997
break;
19901998
}
19911999
}
@@ -1994,6 +2002,36 @@ public boolean dispatchKeyEvent(
19942002
return handled;
19952003
}
19962004

2005+
// Distance for moving one arrow key tap.
2006+
private int lineDelta() {
2007+
return (int) (getHeight() * KEY_SCROLL_FRACTION_AMOUNT);
2008+
}
2009+
2010+
private int pageDelta() {
2011+
return getHeight();
2012+
}
2013+
2014+
private int distanceToTop() {
2015+
// Note: The delta may represent a value that would overshoot the
2016+
// top of the screen, but the children only use as much of the
2017+
// delta as they can support, so it will always go exactly to the
2018+
// top.
2019+
return -getFullContentHeight();
2020+
}
2021+
2022+
private int distanceToBottom() {
2023+
return getFullContentHeight() - getHeight();
2024+
}
2025+
2026+
private boolean moveVertically(int yScrollDelta) {
2027+
View focusedView = findDeepestFocusedChild(this);
2028+
2029+
return manuallyTriggersNestedScrollFromKeyEvent(
2030+
focusedView,
2031+
yScrollDelta
2032+
);
2033+
}
2034+
19972035
private View findDeepestFocusedChild(View startingParentView) {
19982036
View focusedView = startingParentView;
19992037
while (focusedView != null) {
@@ -2050,19 +2088,23 @@ private boolean manuallyTriggersNestedScrollFromKeyEvent(View focusedView, int y
20502088
ViewCompat.TYPE_NON_TOUCH
20512089
);
20522090

2091+
// Reset consumed values to zero.
2092+
mKeyTriggeredScrollConsumed[0] = 0;
2093+
mKeyTriggeredScrollConsumed[1] = 0;
2094+
20532095
onNestedScroll(
20542096
focusedView,
20552097
0,
20562098
0,
20572099
0,
20582100
yScrollDelta,
20592101
ViewCompat.TYPE_NON_TOUCH,
2060-
mBehaviorConsumed
2102+
mKeyTriggeredScrollConsumed
20612103
);
20622104

20632105
onStopNestedScroll(focusedView, ViewCompat.TYPE_NON_TOUCH);
20642106

2065-
if (mBehaviorConsumed[1] > 0) {
2107+
if (mKeyTriggeredScrollConsumed[1] > 0) {
20662108
handled = true;
20672109
}
20682110

0 commit comments

Comments
 (0)