18b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen/*
28b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen * Copyright (C) 2015 The Android Open Source Project
38b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen *
48b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen * Licensed under the Apache License, Version 2.0 (the "License");
58b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen * you may not use this file except in compliance with the License.
68b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen * You may obtain a copy of the License at
78b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen *
88b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen *      http://www.apache.org/licenses/LICENSE-2.0
98b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen *
108b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen * Unless required by applicable law or agreed to in writing, software
118b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen * distributed under the License is distributed on an "AS IS" BASIS,
128b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen * See the License for the specific language governing permissions and
148b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen * limitations under the License.
158b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen */
168b7af7188228ca88838e31e070f0fca0d1f90581Yao Chenpackage android.support.car.ui;
178b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
188b7af7188228ca88838e31e070f0fca0d1f90581Yao Chenimport android.content.Context;
198b7af7188228ca88838e31e070f0fca0d1f90581Yao Chenimport android.graphics.PointF;
208b7af7188228ca88838e31e070f0fca0d1f90581Yao Chenimport android.support.annotation.IntDef;
218b7af7188228ca88838e31e070f0fca0d1f90581Yao Chenimport android.support.annotation.NonNull;
228b7af7188228ca88838e31e070f0fca0d1f90581Yao Chenimport android.support.v7.widget.LinearSmoothScroller;
238b7af7188228ca88838e31e070f0fca0d1f90581Yao Chenimport android.support.v7.widget.RecyclerView;
248b7af7188228ca88838e31e070f0fca0d1f90581Yao Chenimport android.util.DisplayMetrics;
258b7af7188228ca88838e31e070f0fca0d1f90581Yao Chenimport android.util.Log;
268b7af7188228ca88838e31e070f0fca0d1f90581Yao Chenimport android.view.View;
278b7af7188228ca88838e31e070f0fca0d1f90581Yao Chenimport android.view.ViewGroup;
288b7af7188228ca88838e31e070f0fca0d1f90581Yao Chenimport android.view.animation.AccelerateInterpolator;
298b7af7188228ca88838e31e070f0fca0d1f90581Yao Chenimport android.view.animation.DecelerateInterpolator;
308b7af7188228ca88838e31e070f0fca0d1f90581Yao Chenimport android.view.animation.Interpolator;
318b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
328b7af7188228ca88838e31e070f0fca0d1f90581Yao Chenimport java.lang.annotation.Retention;
338b7af7188228ca88838e31e070f0fca0d1f90581Yao Chenimport java.lang.annotation.RetentionPolicy;
348b7af7188228ca88838e31e070f0fca0d1f90581Yao Chenimport java.util.ArrayList;
358b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
368b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen/**
378b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen * Custom {@link RecyclerView.LayoutManager} that behaves similar to LinearLayoutManager except that
388b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen * it has a few tricks up its sleeve.
398b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen * <ol>
408b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen *    <li>In a normal ListView, when views reach the top of the list, they are clipped. In
418b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen *        CarLayoutManager, views have the option of flying off of the top of the screen as the
428b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen *        next row settles in to place. This functionality can be enabled or disabled with
438b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen *        {@link #setOffsetRows(boolean)}.
448b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen *    <li>Standard list physics is disabled. Instead, when the user scrolls, it will settle
458b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen *        on the next page. {@link #FLING_THRESHOLD_TO_PAGINATE} and
468b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen *        {@link #DRAG_DISTANCE_TO_PAGINATE} can be set to have the list settle on the next item
478b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen *        instead of the next page for small gestures.
488b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen *    <li>Items can scroll past the bottom edge of the screen. This helps with pagination so that
498b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen *        the last page can be properly aligned.
508b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen * </ol>
518b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen *
528b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen * This LayoutManger should be used with {@link CarRecyclerView}.
5357de61296cc8f29d8740fc7e6983af9553e7a410Jason Tholstrup * @hide
548b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen */
558b7af7188228ca88838e31e070f0fca0d1f90581Yao Chenpublic class CarLayoutManager extends RecyclerView.LayoutManager {
568b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private static final String TAG = "CarLayoutManager";
571b1247b5648975dd41ee73c25425825abb256234Vitalii Tomkiv    private static final boolean DEBUG = false;
588b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
598b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
608b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * Any fling below the threshold will just scroll to the top fully visible row. The units is
618b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * whatever {@link android.widget.Scroller} would return.
628b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     *
638b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * A reasonable value is ~200
648b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     *
658b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * This can be disabled by setting the threshold to -1.
668b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     */
678b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private static final int FLING_THRESHOLD_TO_PAGINATE = -1;
688b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
698b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
708b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * Any fling shorter than this threshold (in px) will just scroll to the top fully visible row.
718b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     *
728b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * A reasonable value is 15.
738b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     *
748b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * This can be disabled by setting the distance to -1.
758b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     */
768b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private static final int DRAG_DISTANCE_TO_PAGINATE = -1;
778b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
788b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
798b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * If you scroll really quickly, you can hit the end of the laid out rows before Android has a
808b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * chance to layout more. To help counter this, we can layout a number of extra rows past
818b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * wherever the focus is if necessary.
828b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     */
838b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private static final int NUM_EXTRA_ROWS_TO_LAYOUT_PAST_FOCUS = 2;
848b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
858b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
868b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * Scroll bar calculation is a bit complicated. This basically defines the granularity we want
878b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * our scroll bar to move. Set this to 1 means our scrollbar will have really jerky movement.
888b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * Setting it too big will risk an overflow (although there is no performance impact). Ideally
898b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * we want to set this higher than the height of our list view. We can't use our list view
908b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * height directly though because we might run into situations where getHeight() returns 0, for
918b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * example, when the view is not yet measured.
928b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     */
938b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private static final int SCROLL_RANGE = 1000;
948b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
958b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    @ScrollStyle private final int SCROLL_TYPE = MARIO;
968b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
978b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    @Retention(RetentionPolicy.SOURCE)
988b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    @IntDef({MARIO, SUPER_MARIO})
998b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private @interface ScrollStyle {}
1008b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private static final int MARIO = 0;
1018b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private static final int SUPER_MARIO = 1;
1028b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
1038b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    @Retention(RetentionPolicy.SOURCE)
1048b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    @IntDef({BEFORE, AFTER})
1058b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private @interface LayoutDirection {}
1068b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private static final int BEFORE = 0;
1078b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private static final int AFTER = 1;
1088b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
1098b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    @Retention(RetentionPolicy.SOURCE)
1108b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    @IntDef({ROW_OFFSET_MODE_INDIVIDUAL, ROW_OFFSET_MODE_PAGE})
1118b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public @interface RowOffsetMode {}
1128b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public static final int ROW_OFFSET_MODE_INDIVIDUAL = 0;
1138b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public static final int ROW_OFFSET_MODE_PAGE = 1;
1148b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
1158b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public interface OnItemsChangedListener {
1168b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        void onItemsChanged();
1178b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
1188b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
1198b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private final AccelerateInterpolator mDanglingRowInterpolator = new AccelerateInterpolator(2);
1208b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private final Context mContext;
1218b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
1228b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /** Determines whether or not rows will be offset as they slide off screen **/
1238b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private boolean mOffsetRows = false;
1248b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /** Determines whether rows will be offset individually or a page at a time **/
1258b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    @RowOffsetMode private int mRowOffsetMode = ROW_OFFSET_MODE_PAGE;
1268b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
1278b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
1288b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * The LayoutManager only gets {@link #onScrollStateChanged(int)} updates. This enables the
1298b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * scroll state to be used anywhere.
1308b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     */
1318b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private int mScrollState = RecyclerView.SCROLL_STATE_IDLE;
1328b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
1338b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * Used to inspect the current scroll state to help with the various calculations.
1348b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     **/
1358b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private CarSmoothScroller mSmoothScroller;
1368b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private OnItemsChangedListener mItemsChangedListener;
1378b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
1388b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /** The distance that the list has actually scrolled in the most recent drag gesture **/
1398b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private int mLastDragDistance = 0;
1408b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /** True if the current drag was limited/capped because it was at some boundary **/
1418b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private boolean mReachedLimitOfDrag;
1428b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
1438b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * The values are continuously updated to keep track of where the current page boundaries are
1448b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * on screen. The anchor page break is the page break that is currently within or at the
1458b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * top of the viewport. The Upper page break is the page break before it and the lower page
1468b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * break is the page break after it.
1478b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     *
1488b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * A page break will be set to -1 if it is unknown or n/a.
1498b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * @see #updatePageBreakPositions()
1508b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     */
1518b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private int mItemCountDuringLastPageBreakUpdate;
152a32c79c66f4a134e6650a1acf307523221110e4fVictor Chan    // The index of the first item on the current page
1538b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private int mAnchorPageBreakPosition = 0;
154a32c79c66f4a134e6650a1acf307523221110e4fVictor Chan    // The index of the first item on the previous page
1558b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private int mUpperPageBreakPosition = -1;
156a32c79c66f4a134e6650a1acf307523221110e4fVictor Chan    // The index of the first item on the next page
1578b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private int mLowerPageBreakPosition = -1;
1588b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /** Used in the bookkeeping of mario style scrolling to prevent extra calculations. **/
1598b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private int mLastChildPositionToRequestFocus = -1;
1608b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private int mSampleViewHeight = -1;
1618b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
1628b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
1638b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * Set the anchor to the following position on the next layout pass.
1648b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     */
1658b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private int mPendingScrollPosition = -1;
1668b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
1678b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public CarLayoutManager(Context context) {
1688b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        mContext = context;
1698b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
1708b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
1718b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    @Override
1728b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public RecyclerView.LayoutParams generateDefaultLayoutParams() {
1738b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        return new RecyclerView.LayoutParams(
1748b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                ViewGroup.LayoutParams.MATCH_PARENT,
1758b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                ViewGroup.LayoutParams.WRAP_CONTENT);
1768b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
1778b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
1788b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    @Override
1798b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public boolean canScrollVertically() {
1808b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        return true;
1818b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
1828b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
1838b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
1848b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * onLayoutChildren is sort of like a "reset" for the layout state. At a high level, it should:
1858b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * <ol>
1868b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     *    <li>Check the current views to get the current state of affairs
1878b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     *    <li>Detach all views from the window (a lightweight operation) so that rows
1888b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     *        not re-added will be removed after onLayoutChildren.
1898b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     *    <li>Re-add rows as necessary.
1908b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * </ol>
1918b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     *
1928b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * @see super#onLayoutChildren(RecyclerView.Recycler, RecyclerView.State)
1938b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     */
1948b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    @Override
1958b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
1968b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        /**
1978b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen         * The anchor view is the first fully visible view on screen at the beginning
1988b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen         * of onLayoutChildren (or 0 if there is none). This row will be laid out first. After that,
1998b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen         * layoutNextRow will layout rows above and below it until the boundaries of what should
2008b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen         * be laid out have been reached. See {@link #shouldLayoutNextRow(View, int)} for
2018b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen         * more information.
2028b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen         */
2038b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int anchorPosition = 0;
2048b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int anchorTop = -1;
2058b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (mPendingScrollPosition == -1) {
2068b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            View anchor = getFirstFullyVisibleChild();
2078b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            if (anchor != null) {
2088b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                anchorPosition = getPosition(anchor);
2098b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                anchorTop = getDecoratedTop(anchor);
2108b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
2118b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        } else {
2128b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            anchorPosition = mPendingScrollPosition;
2138b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            mPendingScrollPosition = -1;
2148b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            mAnchorPageBreakPosition = anchorPosition;
2158b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            mUpperPageBreakPosition = -1;
2168b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            mLowerPageBreakPosition = -1;
2178b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
2188b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
2198b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (DEBUG) {
2208b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            Log.v(TAG, String.format(
2218b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                    ":: onLayoutChildren anchorPosition:%s, anchorTop:%s,"
2228b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                            + " mPendingScrollPosition: %s, mAnchorPageBreakPosition:%s,"
2238b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                            + " mUpperPageBreakPosition:%s, mLowerPageBreakPosition:%s",
2248b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                    anchorPosition, anchorTop, mPendingScrollPosition, mAnchorPageBreakPosition,
2258b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                    mUpperPageBreakPosition, mLowerPageBreakPosition));
2268b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
2278b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
2288b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        /**
2298b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen         * Detach all attached view for 2 reasons:
2308b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen         * <ol>
2318b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen         *     <li> So that views are put in the scrap heap. This enables us to call
2328b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen         *          {@link RecyclerView.Recycler#getViewForPosition(int)} which will either return
2338b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen         *          one of these detached views if it is in the scrap heap, one from the
2348b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen         *          recycled pool (will only call onBind in the adapter), or create an entirely new
2358b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen         *          row if needed (will call onCreate and onBind in the adapter).
2368b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen         *     <li> So that views are automatically removed if they are not manually re-added.
2378b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen         * </ol>
2388b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen         */
2398b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        detachAndScrapAttachedViews(recycler);
2408b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
2418b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // Layout new rows.
2428b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        View anchor = layoutAnchor(recycler, anchorPosition, anchorTop);
2438b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (anchor != null) {
2448b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            View adjacentRow = anchor;
2458b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            while (shouldLayoutNextRow(state, adjacentRow, BEFORE)) {
2468b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                adjacentRow = layoutNextRow(recycler, adjacentRow, BEFORE);
2478b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
2488b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            adjacentRow = anchor;
2498b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            while (shouldLayoutNextRow(state, adjacentRow, AFTER)) {
2508b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                adjacentRow = layoutNextRow(recycler, adjacentRow, AFTER);
2518b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
2528b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
2538b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
2548b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        updatePageBreakPositions();
2558b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        offsetRows();
2568b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
2578b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (DEBUG&& getChildCount() > 1) {
2588b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            Log.v(TAG, "Currently showing " + getChildCount() + " views " +
2598b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                    getPosition(getChildAt(0)) + " to " +
2608b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                    getPosition(getChildAt(getChildCount() - 1)) + " anchor " + anchorPosition);
2618b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
2628b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
2638b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
2648b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
2658b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * scrollVerticallyBy does the work of what should happen when the list scrolls in addition
2668b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * to handling cases where the list hits the end. It should be lighter weight than
2678b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * onLayoutChildren. It doesn't have to detach all views. It only looks at the end of the list
2688b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * and removes views that have gone out of bounds and lays out new ones that scroll in.
2698b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     *
2708b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * @param dy The amount that the list is supposed to scroll.
2718b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     *               > 0 means the list is scrolling down.
2728b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     *               < 0 means the list is scrolling up.
2738b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * @param recycler The recycler that enables views to be reused or created as they scroll in.
2748b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * @param state Various information about the current state of affairs.
2758b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * @return The amount the list actually scrolled.
2768b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     *
2778b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * @see super#scrollVerticallyBy(int, RecyclerView.Recycler, RecyclerView.State)
2788b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     */
2798b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    @Override
2808b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public int scrollVerticallyBy(
2818b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            int dy, @NonNull RecyclerView.Recycler recycler, @NonNull RecyclerView.State state) {
2828b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // If the list is empty, we can prevent the overscroll glow from showing by just
2838b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // telling RecycerView that we scrolled.
2848b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (getItemCount() == 0) {
2858b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return dy;
2868b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
2878b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
2888b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // Prevent redundant computations if there is definitely nowhere to scroll to.
2898b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (getChildCount() <= 1 || dy == 0) {
2908b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return 0;
2918b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
2928b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
2938b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        View firstChild = getChildAt(0);
2948b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (firstChild == null) {
2958b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return 0;
2968b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
2978b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int firstChildPosition = getPosition(firstChild);
2988b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        RecyclerView.LayoutParams firstChildParams = getParams(firstChild);
2998b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int firstChildTopWithMargin = getDecoratedTop(firstChild) - firstChildParams.topMargin;
3008b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
3018b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        View lastFullyVisibleView = getChildAt(getLastFullyVisibleChildIndex());
3028b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (lastFullyVisibleView == null) {
3038b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return 0;
3048b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
3058b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        boolean isLastViewVisible = getPosition(lastFullyVisibleView) == getItemCount() - 1;
3068b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
3078b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        View firstFullyVisibleChild = getFirstFullyVisibleChild();
3088b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (firstFullyVisibleChild == null) {
3098b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return 0;
3108b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
3118b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int firstFullyVisiblePosition = getPosition(firstFullyVisibleChild);
3128b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        RecyclerView.LayoutParams firstFullyVisibleChildParams = getParams(firstFullyVisibleChild);
3138b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int topRemainingSpace = getDecoratedTop(firstFullyVisibleChild)
3148b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                - firstFullyVisibleChildParams.topMargin - getPaddingTop();
3158b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
3168b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (isLastViewVisible && firstFullyVisiblePosition == mAnchorPageBreakPosition
3178b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                && dy > topRemainingSpace && dy > 0) {
3188b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // Prevent dragging down more than 1 page. As a side effect, this also prevents you
3198b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // from dragging past the bottom because if you are on the second to last page, it
3208b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // prevents you from dragging past the last page.
3218b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            dy = topRemainingSpace;
3228b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            mReachedLimitOfDrag = true;
3238b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        } else if (dy < 0 && firstChildPosition == 0
3248b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                && firstChildTopWithMargin + Math.abs(dy) > getPaddingTop()) {
3258b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // Prevent scrolling past the beginning
3268b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            dy = firstChildTopWithMargin - getPaddingTop();
3278b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            mReachedLimitOfDrag = true;
3288b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        } else {
3298b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            mReachedLimitOfDrag = false;
3308b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
3318b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
3328b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        boolean isDragging = mScrollState == RecyclerView.SCROLL_STATE_DRAGGING;
3338b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (isDragging) {
3348b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            mLastDragDistance += dy;
3358b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
3368b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // We offset by -dy because the views translate in the opposite direction that the
3378b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // list scrolls (think about it.)
3388b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        offsetChildrenVertical(-dy);
3398b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
340a32c79c66f4a134e6650a1acf307523221110e4fVictor Chan        // The last item in the layout should never scroll above the viewport
341a32c79c66f4a134e6650a1acf307523221110e4fVictor Chan        View view = getChildAt(getChildCount() - 1);
342a32c79c66f4a134e6650a1acf307523221110e4fVictor Chan        if (view.getTop() < 0) {
343a32c79c66f4a134e6650a1acf307523221110e4fVictor Chan            view.setTop(0);
344a32c79c66f4a134e6650a1acf307523221110e4fVictor Chan        }
345a32c79c66f4a134e6650a1acf307523221110e4fVictor Chan
3468b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // This is the meat of this function. We remove views on the trailing edge of the scroll
3478b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // and add views at the leading edge as necessary.
3488b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        View adjacentRow;
3498b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (dy > 0) {
3508b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            recycleChildrenFromStart(recycler);
3518b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            adjacentRow = getChildAt(getChildCount() - 1);
3528b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            while (shouldLayoutNextRow(state, adjacentRow, AFTER)) {
3538b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                adjacentRow = layoutNextRow(recycler, adjacentRow, AFTER);
3548b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
3558b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        } else {
3568b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            recycleChildrenFromEnd(recycler);
3578b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            adjacentRow = getChildAt(0);
3588b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            while (shouldLayoutNextRow(state, adjacentRow, BEFORE)) {
3598b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                adjacentRow = layoutNextRow(recycler, adjacentRow, BEFORE);
3608b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
3618b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
3628b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // Now that the correct views are laid out, offset rows as necessary so we can do whatever
3638b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // fancy animation we want such as having the top view fly off the screen as the next one
3648b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // settles in to place.
3658b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        updatePageBreakPositions();
3668b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        offsetRows();
3678b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
3688b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (getChildCount() >  1) {
3698b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            if (DEBUG) {
3708b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                Log.v(TAG, String.format("Currently showing  %d views (%d to %d)",
3718b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                        getChildCount(), getPosition(getChildAt(0)),
3728b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                        getPosition(getChildAt(getChildCount() - 1))));
3738b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
3748b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
3758b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
3768b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        return dy;
3778b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
3788b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
3798b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    @Override
3808b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public void scrollToPosition(int position) {
3818b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        mPendingScrollPosition = position;
3828b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        requestLayout();
3838b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
3848b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
3858b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    @Override
3868b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public void smoothScrollToPosition(
3878b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            RecyclerView recyclerView, RecyclerView.State state, int position) {
3888b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        /**
3898b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen         * startSmoothScroll will handle stopping the old one if there is one.
3908b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen         * We only keep a copy of it to handle the translation of rows as they slide off the screen
3918b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen         * in {@link #offsetRowsWithPageBreak()}
3928b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen         */
3938b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        mSmoothScroller = new CarSmoothScroller(mContext, position);
3948b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        mSmoothScroller.setTargetPosition(position);
3958b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        startSmoothScroll(mSmoothScroller);
3968b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
3978b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
3988b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
3998b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * Miscellaneous bookkeeping.
4008b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     */
4018b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    @Override
4028b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public void onScrollStateChanged(int state) {
4038b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (DEBUG) {
4048b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            Log.v(TAG, ":: onScrollStateChanged " + state);
4058b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
4068b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (state == RecyclerView.SCROLL_STATE_IDLE) {
4078b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // If the focused view is off screen, give focus to one that is.
4088b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // If the first fully visible view is first in the list, focus the first item.
4098b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // Otherwise, focus the second so that you have the first item as scrolling context.
4108b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            View focusedChild = getFocusedChild();
4118b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            if (focusedChild != null
4128b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                    && (getDecoratedTop(focusedChild) >= getHeight() - getPaddingBottom()
4138b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                    || getDecoratedBottom(focusedChild) <= getPaddingTop())) {
4148b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                focusedChild.clearFocus();
4158b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                requestLayout();
4168b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
4178b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
4188b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        } else if (state == RecyclerView.SCROLL_STATE_DRAGGING) {
4198b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            mLastDragDistance = 0;
4208b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
4218b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
4228b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (state != RecyclerView.SCROLL_STATE_SETTLING) {
4238b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            mSmoothScroller = null;
4248b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
4258b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
4268b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        mScrollState = state;
4278b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        updatePageBreakPositions();
4288b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
4298b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
4308b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    @Override
4318b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public void onItemsChanged(RecyclerView recyclerView) {
4328b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        super.onItemsChanged(recyclerView);
4338b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (mItemsChangedListener != null) {
4348b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            mItemsChangedListener.onItemsChanged();
4358b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
4368b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // When item changed, our sample view height is no longer accurate, and need to be
4378b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // recomputed.
4388b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        mSampleViewHeight = -1;
4398b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
4408b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
4418b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
4428b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * Gives us the opportunity to override the order of the focused views.
4438b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * By default, it will just go from top to bottom. However, if there is no focused views, we
4448b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * take over the logic and start the focused views from the middle of what is visible and move
4458b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * from there until the end of the laid out views in the specified direction.
4468b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     */
4478b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    @Override
4488b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public boolean onAddFocusables(
4498b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            RecyclerView recyclerView, ArrayList<View> views, int direction, int focusableMode) {
4508b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        View focusedChild = getFocusedChild();
4518b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (focusedChild != null) {
4528b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // If there is a view that already has focus, we can just return false and the normal
4538b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // Android addFocusables will work fine.
4548b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return false;
4558b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
4568b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
4578b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // Now we know that there isn't a focused view. We need to set up focusables such that
4588b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // instead of just focusing the first item that has been laid out, it focuses starting
4598b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // from a visible item.
4608b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
4618b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int firstFullyVisibleChildIndex = getFirstFullyVisibleChildIndex();
4628b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (firstFullyVisibleChildIndex == -1) {
4638b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // Somehow there is a focused view but there is no fully visible view. There shouldn't
4648b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // be a way for this to happen but we'd better stop here and return instead of
4658b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // continuing on with -1.
4668b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            Log.w(TAG, "There is a focused child but no first fully visible child.");
4678b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return false;
4688b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
4698b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        View firstFullyVisibleChild = getChildAt(firstFullyVisibleChildIndex);
4708b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int firstFullyVisibleChildPosition = getPosition(firstFullyVisibleChild);
4718b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
4728b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int firstFocusableChildIndex = firstFullyVisibleChildIndex;
4738b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (firstFullyVisibleChildPosition > 0 && firstFocusableChildIndex + 1 < getItemCount()) {
4748b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // We are somewhere in the middle of the list. Instead of starting focus on the first
4758b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // item, start focus on the second item to give some context that we aren't at
4768b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // the beginning.
4778b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            firstFocusableChildIndex++;
4788b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
4798b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
4808b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (direction == View.FOCUS_FORWARD) {
4818b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // Iterate from the first focusable view to the end.
4828b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            for (int i = firstFocusableChildIndex; i < getChildCount(); i++) {
4838b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                views.add(getChildAt(i));
4848b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
4858b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return true;
4868b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        } else if (direction == View.FOCUS_BACKWARD) {
4878b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // Iterate from the first focusable view to the beginning.
4888b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            for (int i = firstFocusableChildIndex; i >= 0; i--) {
4898b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                views.add(getChildAt(i));
4908b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
4918b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return true;
4928b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
4938b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        return false;
4948b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
4958b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
4968b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    @Override
4978b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public View onFocusSearchFailed(View focused, int direction, RecyclerView.Recycler recycler,
4988b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                                    RecyclerView.State state) {
4998b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        return null;
5008b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
5018b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
5028b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
5038b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * This is the function that decides where to scroll to when a new view is focused.
5048b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * You can get the position of the currently focused child through the child parameter.
5058b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * Once you have that, determine where to smooth scroll to and scroll there.
5068b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     *
5078b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * @param parent The RecyclerView hosting this LayoutManager
5088b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * @param state Current state of RecyclerView
5098b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * @param child Direct child of the RecyclerView containing the newly focused view
5108b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * @param focused The newly focused view. This may be the same view as child or it may be null
5118b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * @return true if the default scroll behavior should be suppressed
5128b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     */
5138b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    @Override
5148b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public boolean onRequestChildFocus(RecyclerView parent, RecyclerView.State state,
5158b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                                       View child, View focused) {
5168b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (child == null) {
5178b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            Log.w(TAG, "onRequestChildFocus with a null child!");
5188b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return true;
5198b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
5208b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
5218b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (DEBUG) {
5228b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            Log.v(TAG, String.format(":: onRequestChildFocus child: %s, focused: %s", child,
5238b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                    focused));
5248b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
5258b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
5268b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // We have several distinct scrolling methods. Each implementation has been delegated
5278b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // to its own method.
5288b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (SCROLL_TYPE == MARIO) {
5298b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return onRequestChildFocusMarioStyle(parent, child);
5308b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        } else if (SCROLL_TYPE == SUPER_MARIO) {
5318b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return onRequestChildFocusSuperMarioStyle(parent, state, child);
5328b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        } else {
5338b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            throw new IllegalStateException("Unknown scroll type (" + SCROLL_TYPE + ")");
5348b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
5358b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
5368b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
5378b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
5388b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * Goal: the scrollbar maintains the same size throughout scrolling and that the scrollbar
5398b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * reaches the bottom of the screen when the last item is fully visible. This is because
5408b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * there are multiple points that could be considered the bottom since the last item can scroll
5418b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * past the bottom edge of the screen.
5428b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     *
5438b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * To find the extent, we divide the number of items that can fit on screen by the number of
5448b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * items in total.
5458b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     */
5468b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    @Override
5478b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public int computeVerticalScrollExtent(RecyclerView.State state) {
5488b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (getChildCount() <= 1) {
5498b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return 0;
5508b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
5518b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
5528b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int sampleViewHeight = getSampleViewHeight();
5538b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int availableHeight = getAvailableHeight();
5548b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int sampleViewsThatCanFitOnScreen = availableHeight / sampleViewHeight;
5558b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
5568b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (state.getItemCount() <= sampleViewsThatCanFitOnScreen) {
5578b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return SCROLL_RANGE;
5588b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        } else {
5598b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return SCROLL_RANGE * sampleViewsThatCanFitOnScreen / state.getItemCount();
5608b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
5618b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
5628b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
5638b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
5648b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * The scrolling offset is calculated by determining what position is at the top of the list.
5658b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * However, instead of using fixed integer positions for each row, the scroll position is
5668b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * factored in and the position is recalculated as a float that takes in to account the
5678b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * current scroll state. This results in a smooth animation for the scrollbar when the user
5688b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * scrolls the list.
5698b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     */
5708b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    @Override
5718b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public int computeVerticalScrollOffset(RecyclerView.State state) {
5728b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        View firstChild = getFirstFullyVisibleChild();
5738b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (firstChild == null) {
5748b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return 0;
5758b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
5768b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
5778b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        RecyclerView.LayoutParams params = getParams(firstChild);
5788b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int firstChildPosition = getPosition(firstChild);
5798b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
5808b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // Assume the previous view is the same height as the current one.
5818b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        float percentOfPreviousViewShowing = (getDecoratedTop(firstChild) - params.topMargin)
5828b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                / (float) (getDecoratedMeasuredHeight(firstChild)
5838b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                + params.topMargin + params.bottomMargin);
5848b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // If the previous view is actually larger than the current one then this the percent
5858b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // can be greater than 1.
5868b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        percentOfPreviousViewShowing = Math.min(percentOfPreviousViewShowing, 1);
5878b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
5888b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        float currentPosition = (float) firstChildPosition - percentOfPreviousViewShowing;
5898b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
5908b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int sampleViewHeight = getSampleViewHeight();
5918b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int availableHeight = getAvailableHeight();
5928b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int numberOfSampleViewsThatCanFitOnScreen = availableHeight / sampleViewHeight;
5938b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int positionWhenLastItemIsVisible =
5948b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                state.getItemCount() - numberOfSampleViewsThatCanFitOnScreen;
5958b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
5968b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (positionWhenLastItemIsVisible <= 0) {
5978b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return 0;
5988b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
5998b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
6008b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (currentPosition >= positionWhenLastItemIsVisible) {
6018b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return SCROLL_RANGE;
6028b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
6038b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
6048b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        return (int) (SCROLL_RANGE * currentPosition / positionWhenLastItemIsVisible);
6058b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
6068b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
6078b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
6088b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * The range of the scrollbar can be understood as the granularity of how we want the
6098b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * scrollbar to scroll.
6108b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     */
6118b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    @Override
6128b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public int computeVerticalScrollRange(RecyclerView.State state) {
6138b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        return SCROLL_RANGE;
6148b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
6158b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
6168b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
6178b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * @return The first view that starts on screen. It assumes that it fully fits on the screen
6188b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     *         though. If the first fully visible child is also taller than the screen then it will
6198b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     *         still be returned. However, since the LayoutManager snaps to view starts, having
6208b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     *         a row that tall would lead to a broken experience anyways.
6218b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     */
6228b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public int getFirstFullyVisibleChildIndex() {
6238b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        for (int i = 0; i < getChildCount(); i++) {
6248b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            View child = getChildAt(i);
6258b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            RecyclerView.LayoutParams params = getParams(child);
6268b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            if (getDecoratedTop(child) - params.topMargin >= getPaddingTop()) {
6278b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                return i;
6288b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
6298b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
6308b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        return -1;
6318b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
6328b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
6338b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public View getFirstFullyVisibleChild() {
6348b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int firstFullyVisibleChildIndex = getFirstFullyVisibleChildIndex();
6358b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        View firstChild = null;
6368b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (firstFullyVisibleChildIndex != -1) {
6378b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            firstChild = getChildAt(firstFullyVisibleChildIndex);
6388b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
6398b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        return firstChild;
6408b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
6418b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
6428b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
6438b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * @return The last view that ends on screen. It assumes that the start is also on screen
6448b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     *         though. If the last fully visible child is also taller than the screen then it will
6458b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     *         still be returned. However, since the LayoutManager snaps to view starts, having
6468b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     *         a row that tall would lead to a broken experience anyways.
6478b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     */
6488b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public int getLastFullyVisibleChildIndex() {
6498b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        for (int i = getChildCount() - 1; i >= 0; i--) {
6508b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            View child = getChildAt(i);
6518b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            RecyclerView.LayoutParams params = getParams(child);
6528b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            int childBottom = getDecoratedBottom(child) + params.bottomMargin;
6538b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            int listBottom = getHeight() - getPaddingBottom();
6548b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            if (childBottom <= listBottom) {
6558b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                return i;
6568b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
6578b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
6588b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        return -1;
6598b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
6608b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
6618b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
6628b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * @return Whether or not the first view is fully visible.
6638b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     */
6648b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public boolean isAtTop() {
6658b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // getFirstFullyVisibleChildIndex() can return -1 which indicates that there are no views
6668b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // and also means that the list is at the top.
6678b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        return getFirstFullyVisibleChildIndex() <= 0;
6688b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
6698b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
6708b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
6718b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * @return Whether or not the last view is fully visible.
6728b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     */
6738b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public boolean isAtBottom() {
6748b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int lastFullyVisibleChildIndex = getLastFullyVisibleChildIndex();
6758b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (lastFullyVisibleChildIndex == -1) {
6768b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return true;
6778b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
6788b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        View lastFullyVisibleChild = getChildAt(lastFullyVisibleChildIndex);
6798b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        return getPosition(lastFullyVisibleChild) == getItemCount() - 1;
6808b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
6818b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
6828b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public void setOffsetRows(boolean offsetRows) {
6838b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        mOffsetRows = offsetRows;
6848b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (offsetRows) {
6858b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            offsetRows();
6868b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        } else {
6878b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            int childCount = getChildCount();
6888b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            for (int i = 0; i < childCount; i++) {
6898b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                getChildAt(i).setTranslationY(0);
6908b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
6918b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
6928b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
6938b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
6948b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public void setRowOffsetMode(@RowOffsetMode int mode) {
6958b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (mode == mRowOffsetMode) {
6968b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return;
6978b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
6988b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        mRowOffsetMode = mode;
6998b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        offsetRows();
7008b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
7018b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
7028b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public void setItemsChangedListener(OnItemsChangedListener listener) {
7038b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        mItemsChangedListener = listener;
7048b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
7058b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
7068b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
7078b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * Finish the pagination taking into account where the gesture started (not where we are now).
7088b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     *
7098b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * @return Whether the list was scrolled as a result of the fling.
7108b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     */
7118b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public boolean settleScrollForFling(RecyclerView parent, int flingVelocity) {
7128b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (getChildCount() == 0) {
7138b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return false;
7148b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
7158b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
7168b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (mReachedLimitOfDrag) {
7178b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return false;
7188b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
7198b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
7208b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // If the fling was too slow or too short, settle on the first fully visible row instead.
7218b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (Math.abs(flingVelocity) <= FLING_THRESHOLD_TO_PAGINATE
7228b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                || Math.abs(mLastDragDistance) <= DRAG_DISTANCE_TO_PAGINATE) {
7238b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            int firstFullyVisibleChildIndex = getFirstFullyVisibleChildIndex();
7248b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            if (firstFullyVisibleChildIndex != -1) {
7258b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                int scrollPosition = getPosition(getChildAt(firstFullyVisibleChildIndex));
7268b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                parent.smoothScrollToPosition(scrollPosition);
7278b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                return true;
7288b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
7298b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return false;
7308b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
7318b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
7328b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // Finish the pagination taking into account where the gesture
7338b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // started (not where we are now).
7348b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        boolean isDownGesture = flingVelocity > 0
7358b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                || (flingVelocity == 0 && mLastDragDistance >= 0);
7368b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        boolean isUpGesture = flingVelocity < 0
7378b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                || (flingVelocity == 0 && mLastDragDistance < 0);
7388b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (isDownGesture && mLowerPageBreakPosition != -1) {
7398b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // If the last view is fully visible then only settle on the first fully visible view
7408b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // instead of the original page down position. However, don't page down if the last
7418b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // item has come fully into view.
7428b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            parent.smoothScrollToPosition(mAnchorPageBreakPosition);
7438b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return true;
7448b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        } else if (isUpGesture && mUpperPageBreakPosition != -1) {
7458b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            parent.smoothScrollToPosition(mUpperPageBreakPosition);
7468b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return true;
7478b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        } else {
7488b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            Log.e(TAG, "Error setting scroll for fling! flingVelocity: \t" + flingVelocity +
7498b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                    "\tlastDragDistance: " + mLastDragDistance + "\tpageUpAtStartOfDrag: " +
7508b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                    mUpperPageBreakPosition + "\tpageDownAtStartOfDrag: " +
7518b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                    mLowerPageBreakPosition);
7528b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // As a last resort, at the last smooth scroller target position if there is one.
7538b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            if (mSmoothScroller != null) {
7548b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                parent.smoothScrollToPosition(mSmoothScroller.getTargetPosition());
7558b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                return true;
7568b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
7578b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
7588b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        return false;
7598b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
7608b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
7618b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
7628b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * @return The position that paging up from the current position would settle at.
7638b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     */
7648b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public int getPageUpPosition() {
7658b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        return mUpperPageBreakPosition;
7668b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
7678b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
7688b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
7698b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * @return The position that paging down from the current position would settle at.
7708b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     */
7718b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public int getPageDownPosition() {
7728b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        return mLowerPageBreakPosition;
7738b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
7748b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
7758b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
7768b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * Layout the anchor row. The anchor row is the first fully visible row.
7778b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     *
7788b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * @param anchorTop The decorated top of the anchor. If it is not known or should be reset
7798b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     *                  to the top, pass -1.
7808b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     */
7818b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private View layoutAnchor(RecyclerView.Recycler recycler, int anchorPosition, int anchorTop) {
7828b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (anchorPosition > getItemCount() - 1) {
7838b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return null;
7848b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
7858b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        View anchor = recycler.getViewForPosition(anchorPosition);
7868b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        RecyclerView.LayoutParams params = getParams(anchor);
7878b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        measureChildWithMargins(anchor, 0, 0);
7888b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int left = getPaddingLeft() + params.leftMargin;
7898b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int top = (anchorTop == -1) ? params.topMargin : anchorTop;
7908b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int right = left + getDecoratedMeasuredWidth(anchor);
7918b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int bottom = top + getDecoratedMeasuredHeight(anchor);
7928b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        layoutDecorated(anchor, left, top, right, bottom);
7938b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        addView(anchor);
7948b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        return anchor;
7958b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
7968b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
7978b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
7988b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * Lays out the next row in the specified direction next to the specified adjacent row.
7998b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     *
8008b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * @param recycler The recycler from which a new view can be created.
8018b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * @param adjacentRow The View of the adjacent row which will be used to position the new one.
8028b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * @param layoutDirection The side of the adjacent row that the new row will be laid out on.
8038b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     *
8048b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * @return The new row that was laid out.
8058b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     */
8068b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private View layoutNextRow(RecyclerView.Recycler recycler, View adjacentRow,
8078b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                               @LayoutDirection int layoutDirection) {
8088b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
8098b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int adjacentRowPosition = getPosition(adjacentRow);
8108b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int newRowPosition = adjacentRowPosition;
8118b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (layoutDirection == BEFORE) {
8128b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            newRowPosition = adjacentRowPosition - 1;
8138b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        } else if (layoutDirection == AFTER) {
8148b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            newRowPosition = adjacentRowPosition + 1;
8158b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
8168b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
8178b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // Because we detach all rows in onLayoutChildren, this will often just return a view from
8188b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // the scrap heap.
8198b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        View newRow = recycler.getViewForPosition(newRowPosition);
8208b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
8218b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        measureChildWithMargins(newRow, 0, 0);
8228b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        RecyclerView.LayoutParams newRowParams =
8238b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                (RecyclerView.LayoutParams) newRow.getLayoutParams();
8248b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        RecyclerView.LayoutParams adjacentRowParams =
8258b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                (RecyclerView.LayoutParams) adjacentRow.getLayoutParams();
8268b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int left = getPaddingLeft() + newRowParams.leftMargin;
8278b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int right = left + getDecoratedMeasuredWidth(newRow);
8288b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int top, bottom;
8298b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (layoutDirection == BEFORE) {
8308b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            bottom = adjacentRow.getTop() - adjacentRowParams.topMargin - newRowParams.bottomMargin;
8318b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            top = bottom - getDecoratedMeasuredHeight(newRow);
8328b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        } else {
8338b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            top = getDecoratedBottom(adjacentRow) +
8348b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                    adjacentRowParams.bottomMargin + newRowParams.topMargin;
8358b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            bottom = top + getDecoratedMeasuredHeight(newRow);
8368b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
8378b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        layoutDecorated(newRow, left, top, right, bottom);
8388b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
8398b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (layoutDirection == BEFORE) {
8408b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            addView(newRow, 0);
8418b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        } else {
8428b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            addView(newRow);
8438b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
8448b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
8458b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        return newRow;
8468b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
8478b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
8488b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
8498b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * @return Whether another row should be laid out in the specified direction.
8508b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     */
8518b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private boolean shouldLayoutNextRow(RecyclerView.State state, View adjacentRow,
8528b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                                        @LayoutDirection int layoutDirection) {
8538b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int adjacentRowPosition = getPosition(adjacentRow);
8548b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
8558b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (layoutDirection == BEFORE) {
8568b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            if (adjacentRowPosition == 0) {
8578b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                // We already laid out the first row.
8588b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                return false;
8598b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
8608b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        } else if (layoutDirection == AFTER) {
8618b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            if (adjacentRowPosition >= state.getItemCount() - 1) {
8628b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                // We already laid out the last row.
8638b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                return false;
8648b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
8658b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
8668b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
8678b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // If we are scrolling layout views until the target position.
8688b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (mSmoothScroller != null) {
8698b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            if (layoutDirection == BEFORE
8708b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                    && adjacentRowPosition >= mSmoothScroller.getTargetPosition()) {
8718b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                return true;
8728b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            } else if (layoutDirection == AFTER
8738b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                    && adjacentRowPosition <= mSmoothScroller.getTargetPosition()) {
8748b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                return true;
8758b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
8768b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
8778b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
8788b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        View focusedRow = getFocusedChild();
8798b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (focusedRow != null) {
8808b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            int focusedRowPosition = getPosition(focusedRow);
8818b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            if (layoutDirection == BEFORE && adjacentRowPosition
8828b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                    >= focusedRowPosition - NUM_EXTRA_ROWS_TO_LAYOUT_PAST_FOCUS) {
8838b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                return true;
8848b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            } else if (layoutDirection == AFTER && adjacentRowPosition
8858b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                    <= focusedRowPosition + NUM_EXTRA_ROWS_TO_LAYOUT_PAST_FOCUS) {
8868b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                return true;
8878b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
8888b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
8898b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
8908b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        RecyclerView.LayoutParams params = getParams(adjacentRow);
8918b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int adjacentRowTop = getDecoratedTop(adjacentRow) - params.topMargin;
8928b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int adjacentRowBottom = getDecoratedBottom(adjacentRow) - params.bottomMargin;
8938b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (layoutDirection == BEFORE
8948b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                && adjacentRowTop < getPaddingTop() - getHeight()) {
8958b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // View is more than 1 page past the top of the screen and also past where the user has
8968b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // scrolled to. We want to keep one page past the top to make the scroll up calculation
8978b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // easier and scrolling smoother.
8988b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return false;
8998b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        } else if (layoutDirection == AFTER
9008b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                && adjacentRowBottom > getHeight() - getPaddingBottom()) {
9018b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // View is off of the bottom and also past where the user has scrolled to.
9028b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return false;
9038b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
9048b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
9058b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        return true;
9068b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
9078b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
9088b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
9098b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * Remove and recycle views that are no longer needed.
9108b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     */
9118b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private void recycleChildrenFromStart(RecyclerView.Recycler recycler) {
9128b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // Start laying out children one page before the top of the viewport.
9138b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int childrenStart = getPaddingTop() - getHeight();
9148b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
9158b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int focusedChildPosition = Integer.MAX_VALUE;
9168b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        View focusedChild = getFocusedChild();
9178b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (focusedChild != null) {
9188b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            focusedChildPosition = getPosition(focusedChild);
9198b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
9208b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
9218b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // Count the number of views that should be removed.
9228b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int detachedCount = 0;
9238b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int childCount = getChildCount();
9248b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        for (int i = 0; i < childCount; i++) {
9258b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            final View child = getChildAt(i);
9268b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            int childEnd = getDecoratedBottom(child);
9278b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            int childPosition = getPosition(child);
9288b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
9298b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            if (childEnd >= childrenStart || childPosition >= focusedChildPosition - 1) {
9308b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                break;
9318b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
9328b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
9338b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            detachedCount++;
9348b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
9358b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
9368b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // Remove the number of views counted above. Done by removing the first child n times.
9378b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        while (--detachedCount >= 0) {
9388b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            final View child = getChildAt(0);
9398b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            removeAndRecycleView(child, recycler);
9408b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
9418b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
9428b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
9438b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
9448b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * Remove and recycle views that are no longer needed.
9458b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     */
9468b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private void recycleChildrenFromEnd(RecyclerView.Recycler recycler) {
9478b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // Layout views until the end of the viewport.
9488b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int childrenEnd = getHeight();
9498b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
9508b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int focusedChildPosition = Integer.MIN_VALUE + 1;
9518b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        View focusedChild = getFocusedChild();
9528b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (focusedChild != null) {
9538b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            focusedChildPosition = getPosition(focusedChild);
9548b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
9558b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
9568b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // Count the number of views that should be removed.
9578b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int firstDetachedPos = 0;
9588b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int detachedCount = 0;
9598b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int childCount = getChildCount();
9608b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        for (int i = childCount - 1; i >= 0; i--) {
9618b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            final View child = getChildAt(i);
9628b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            int childStart = getDecoratedTop(child);
9638b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            int childPosition = getPosition(child);
9648b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
9658b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            if (childStart <= childrenEnd || childPosition <= focusedChildPosition - 1) {
9668b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                break;
9678b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
9688b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
9698b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            firstDetachedPos = i;
9708b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            detachedCount++;
9718b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
9728b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
9738b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        while (--detachedCount >= 0) {
9748b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            final View child = getChildAt(firstDetachedPos);
9758b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            removeAndRecycleView(child, recycler);
9768b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
9778b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
9788b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
9798b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
9808b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * Offset rows to do fancy animations. If {@link #mOffsetRows} is false, this will do nothing.
9818b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     *
9828b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * @see #offsetRowsIndividually()
9838b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * @see #offsetRowsByPage()
9848b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     */
9858b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public void offsetRows() {
9868b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (!mOffsetRows) {
9878b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return;
9888b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
9898b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
9908b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (mRowOffsetMode == ROW_OFFSET_MODE_PAGE) {
9918b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            offsetRowsByPage();
9928b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        } else if (mRowOffsetMode == ROW_OFFSET_MODE_INDIVIDUAL) {
9938b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            offsetRowsIndividually();
9948b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
9958b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
9968b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
9978b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
9988b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * Offset the single row that is scrolling off the screen such that by the time the next row
9998b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * reaches the top, it will have accelerated completely off of the screen.
10008b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     */
10018b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private void offsetRowsIndividually() {
10028b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (getChildCount() == 0) {
10038b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            if (DEBUG) {
10048b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                Log.d(TAG, ":: offsetRowsIndividually getChildCount=0");
10058b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
10068b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return;
10078b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
10088b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
10098b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // Identify the dangling row. It will be the first row that is at the top of the
10108b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // list or above.
10118b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int danglingChildIndex = -1;
10128b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        for (int i = getChildCount() - 1; i >= 0; i--) {
10138b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            View child = getChildAt(i);
10148b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            if (getDecoratedTop(child) - getParams(child).topMargin <= getPaddingTop()) {
10158b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                danglingChildIndex = i;
10168b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                break;
10178b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
10188b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
10198b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
10208b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        mAnchorPageBreakPosition = danglingChildIndex;
10218b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
10228b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (DEBUG) {
10238b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            Log.v(TAG, ":: offsetRowsIndividually danglingChildIndex: " + danglingChildIndex);
10248b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
10258b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
10268b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // Calculate the total amount that the view will need to scroll in order to go completely
10278b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // off screen.
10288b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        RecyclerView rv = (RecyclerView) getChildAt(0).getParent();
10298b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int[] locs = new int[2];
10308b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        rv.getLocationInWindow(locs);
10318b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int listTopInWindow = locs[1] + rv.getPaddingTop();
10328b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int maxDanglingViewTranslation;
10338b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
10348b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int childCount = getChildCount();
10358b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        for (int i = 0; i < childCount; i++) {
10368b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            View child = getChildAt(i);
10378b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            RecyclerView.LayoutParams params = getParams(child);
10388b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
10398b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            maxDanglingViewTranslation = listTopInWindow;
10408b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // If the child has a negative margin, we'll actually need to translate the view a
10418b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // little but further to get it completely off screen.
10428b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            if (params.topMargin < 0) {
10438b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                maxDanglingViewTranslation -= params.topMargin;
10448b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
10458b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            if (params.bottomMargin < 0) {
10468b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                maxDanglingViewTranslation -= params.bottomMargin;
10478b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
10488b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
10498b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            if (i < danglingChildIndex) {
10508b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                child.setAlpha(0f);
10518b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            } else if (i > danglingChildIndex) {
10528b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                child.setAlpha(1f);
10538b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                child.setTranslationY(0);
10548b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            } else {
10558b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                int totalScrollDistance = getDecoratedMeasuredHeight(child) +
10568b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                        params.topMargin + params.bottomMargin;
10578b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
10588b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                int distanceLeftInScroll = getDecoratedBottom(child) +
10598b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                        params.bottomMargin - getPaddingTop();
10608b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                float percentageIntoScroll = 1 - distanceLeftInScroll / (float) totalScrollDistance;
10618b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                float interpolatedPercentage =
10628b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                        mDanglingRowInterpolator.getInterpolation(percentageIntoScroll);
10638b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
10648b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                child.setAlpha(1f);
10658b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                child.setTranslationY(-(maxDanglingViewTranslation * interpolatedPercentage));
10668b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
10678b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
10688b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
10698b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
10708b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
10718b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * When the list scrolls, the entire page of rows will offset in one contiguous block. This
10728b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * significantly reduces the amount of extra motion at the top of the screen.
10738b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     */
10748b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private void offsetRowsByPage() {
10758b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        View anchorView = findViewByPosition(mAnchorPageBreakPosition);
10768b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (anchorView == null) {
10778b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            if (DEBUG) {
10788b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                Log.d(TAG, ":: offsetRowsByPage anchorView null");
10798b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
10808b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return;
10818b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
10828b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int anchorViewTop = getDecoratedTop(anchorView) - getParams(anchorView).topMargin;
10838b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
10848b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        View upperPageBreakView = findViewByPosition(mUpperPageBreakPosition);
10858b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int upperViewTop = getDecoratedTop(upperPageBreakView)
10868b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                - getParams(upperPageBreakView).topMargin;
10878b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
10888b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int scrollDistance = upperViewTop - anchorViewTop;
10898b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
10908b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int distanceLeft = anchorViewTop - getPaddingTop();
10918b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        float scrollPercentage = (Math.abs(scrollDistance) - distanceLeft)
10928b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                / (float) Math.abs(scrollDistance);
10938b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
10948b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (DEBUG) {
10958b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            Log.d(TAG, String.format(
10968b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                    ":: offsetRowsByPage scrollDistance:%s, distanceLeft:%s, scrollPercentage:%s",
10978b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                    scrollDistance, distanceLeft, scrollPercentage));
10988b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
10998b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
11008b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // Calculate the total amount that the view will need to scroll in order to go completely
11018b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // off screen.
11028b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        RecyclerView rv = (RecyclerView) getChildAt(0).getParent();
11038b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int[] locs = new int[2];
11048b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        rv.getLocationInWindow(locs);
11058b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int listTopInWindow = locs[1] + rv.getPaddingTop();
11068b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
11078b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int childCount = getChildCount();
11088b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        for (int i = 0; i < childCount; i++) {
11098b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            View child = getChildAt(i);
11108b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            int position = getPosition(child);
11118b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            if (position < mUpperPageBreakPosition) {
11128b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                child.setAlpha(0f);
11138b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                child.setTranslationY(-listTopInWindow);
11148b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            } else if (position < mAnchorPageBreakPosition) {
11158b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                // If the child has a negative margin, we need to offset the row by a little bit
11168b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                // extra so that it moves completely off screen.
11178b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                RecyclerView.LayoutParams params = getParams(child);
11188b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                int extraTranslation = 0;
11198b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                if (params.topMargin < 0) {
11208b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                    extraTranslation -= params.topMargin;
11218b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                }
11228b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                if (params.bottomMargin < 0) {
11238b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                    extraTranslation -= params.bottomMargin;
11248b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                }
11258b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                int translation = (int) ((listTopInWindow + extraTranslation)
11268b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                        * mDanglingRowInterpolator.getInterpolation(scrollPercentage));
11278b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                child.setAlpha(1f);
11288b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                child.setTranslationY(-translation);
11298b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            } else {
11308b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                child.setAlpha(1f);
11318b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                child.setTranslationY(0);
11328b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
11338b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
11348b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
11358b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
11368b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
11378b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * Update the page break positions based on the position of the views on screen. This should
11388b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * be called whenever view move or change such as during a scroll or layout.
11398b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     */
11408b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private void updatePageBreakPositions() {
11418b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (getChildCount() == 0) {
11428b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            if (DEBUG) {
11438b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                Log.d(TAG, ":: updatePageBreakPosition getChildCount: 0");
11448b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
11458b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return;
11468b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
11478b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
11488b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (DEBUG) {
11498b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            Log.v(TAG, String.format(":: #BEFORE updatePageBreakPositions " +
11508b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                            "mAnchorPageBreakPosition:%s, mUpperPageBreakPosition:%s, "
11518b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                            + "mLowerPageBreakPosition:%s",
11528b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                    mAnchorPageBreakPosition, mUpperPageBreakPosition, mLowerPageBreakPosition));
11538b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
11548b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
11558b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // If the item count has changed, our page boundaries may no longer be accurate. This will
11568b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // force the page boundaries to reset around the current view that is closest to the top.
11578b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (getItemCount() != mItemCountDuringLastPageBreakUpdate) {
11588b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            if (DEBUG) {
11598b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                Log.d(TAG, "Item count changed. Resetting page break positions.");
11608b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
11618b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            mAnchorPageBreakPosition = getPosition(getFirstFullyVisibleChild());
11628b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
11638b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        mItemCountDuringLastPageBreakUpdate = getItemCount();
11648b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
11658b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (mAnchorPageBreakPosition == -1) {
11668b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            Log.w(TAG, "Unable to update anchor positions. There is no anchor position.");
11678b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return;
11688b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
11698b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
11708b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        View anchorPageBreakView = findViewByPosition(mAnchorPageBreakPosition);
11718b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (anchorPageBreakView == null) {
11728b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return;
11738b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
11748b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int topMargin = getParams(anchorPageBreakView).topMargin;
11758b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int anchorTop = getDecoratedTop(anchorPageBreakView) - topMargin;
11768b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        View upperPageBreakView = findViewByPosition(mUpperPageBreakPosition);
11778b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int upperPageBreakTop = upperPageBreakView == null ? Integer.MIN_VALUE :
11788b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                getDecoratedTop(upperPageBreakView) - getParams(upperPageBreakView).topMargin;
11798b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
11808b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (DEBUG) {
11818b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            Log.v(TAG, String.format(":: #MID updatePageBreakPositions topMargin:%s, anchorTop:%s"
1182a32c79c66f4a134e6650a1acf307523221110e4fVictor Chan                            + " mAnchorPageBreakPosition:%s, mUpperPageBreakPosition:%s, "
11838b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                            + "mLowerPageBreakPosition:%s", topMargin, anchorTop,
11848b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                    mAnchorPageBreakPosition, mUpperPageBreakPosition, mLowerPageBreakPosition));
11858b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
11868b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
11878b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (anchorTop < getPaddingTop()) {
11888b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // The anchor has moved above the viewport. We are now on the next page. Shift the page
11898b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // break positions and calculate a new lower one.
11908b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            mUpperPageBreakPosition = mAnchorPageBreakPosition;
11918b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            mAnchorPageBreakPosition = mLowerPageBreakPosition;
11928b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            mLowerPageBreakPosition = calculateNextPageBreakPosition(mAnchorPageBreakPosition);
11938b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        } else if (mAnchorPageBreakPosition > 0 && upperPageBreakTop >= getPaddingTop()) {
11948b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // The anchor has moved below the viewport. We are now on the previous page. Shift
11958b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // the page break positions and calculate a new upper one.
11968b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            mLowerPageBreakPosition = mAnchorPageBreakPosition;
11978b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            mAnchorPageBreakPosition = mUpperPageBreakPosition;
11988b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            mUpperPageBreakPosition = calculatePreviousPageBreakPosition(mAnchorPageBreakPosition);
11998b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        } else {
12008b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            mUpperPageBreakPosition = calculatePreviousPageBreakPosition(mAnchorPageBreakPosition);
12018b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            mLowerPageBreakPosition = calculateNextPageBreakPosition(mAnchorPageBreakPosition);
12028b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
12038b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
12048b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (DEBUG) {
12058b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            Log.v(TAG, String.format(":: #AFTER updatePageBreakPositions " +
12068b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                            "mAnchorPageBreakPosition:%s, mUpperPageBreakPosition:%s, "
12078b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                            + "mLowerPageBreakPosition:%s",
12088b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                    mAnchorPageBreakPosition, mUpperPageBreakPosition, mLowerPageBreakPosition));
12098b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
12108b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
12118b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
12128b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
12138b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * @return The page break position of the page before the anchor page break position. However,
12148b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     *         if it reaches the end of the laid out children or position 0, it will just return
12158b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     *         that.
12168b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     */
12178b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private int calculatePreviousPageBreakPosition(int position) {
12188b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (position == -1) {
12198b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return -1;
12208b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
12218b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        View referenceView = findViewByPosition(position);
12228b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int referenceViewTop = getDecoratedTop(referenceView) - getParams(referenceView).topMargin;
12238b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
12248b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int previousPagePosition = position;
12258b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        while (previousPagePosition > 0) {
12268b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            previousPagePosition--;
12278b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            View child = findViewByPosition(previousPagePosition);
12288b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            if (child == null) {
12298b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                // View has not been laid out yet.
12308b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                return previousPagePosition + 1;
12318b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
12328b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
12338b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            int childTop = getDecoratedTop(child) - getParams(child).topMargin;
12348b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
12358b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            if (childTop < referenceViewTop - getHeight()) {
12368b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                return previousPagePosition + 1;
12378b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
12388b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
12398b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // Beginning of the list.
12408b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        return 0;
12418b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
12428b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
12438b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
12448b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * @return The page break position of the next page after the anchor page break position.
12458b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     *         However, if it reaches the end of the laid out children or end of the list, it will
12468b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     *         just return that.
12478b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     */
12488b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private int calculateNextPageBreakPosition(int position) {
12498b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (position == -1) {
12508b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return -1;
12518b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
12528b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
12538b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        View referenceView = findViewByPosition(position);
12548b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (referenceView == null) {
12558b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return position;
12568b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
12578b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int referenceViewTop = getDecoratedTop(referenceView) - getParams(referenceView).topMargin;
12588b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
12598b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int nextPagePosition = position;
1260a32c79c66f4a134e6650a1acf307523221110e4fVictor Chan
1261a32c79c66f4a134e6650a1acf307523221110e4fVictor Chan        // Search for the first child item after the referenceView that didn't fully fit on to the
1262a32c79c66f4a134e6650a1acf307523221110e4fVictor Chan        // screen. The next page should start from the item before this child, so that users have
1263a32c79c66f4a134e6650a1acf307523221110e4fVictor Chan        // a visual anchoring point of the page change.
12648b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        while (position < getItemCount() - 1) {
12658b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            nextPagePosition++;
12668b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            View child = findViewByPosition(nextPagePosition);
12678b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            if (child == null) {
12688b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                // The next view has not been laid out yet.
12698b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                return nextPagePosition - 1;
12708b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
12718b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
12728b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            int childBottom = getDecoratedBottom(child) + getParams(child).bottomMargin;
12738b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            if (childBottom - referenceViewTop > getHeight() - getPaddingTop()) {
1274a32c79c66f4a134e6650a1acf307523221110e4fVictor Chan                // If choosing the previous child causes the view to snap back to the referenceView
1275a32c79c66f4a134e6650a1acf307523221110e4fVictor Chan                // position, then skip that and go directly to the child. This avoids the case
1276a32c79c66f4a134e6650a1acf307523221110e4fVictor Chan                // where a tall card in the layout causes the view to constantly snap back to
1277a32c79c66f4a134e6650a1acf307523221110e4fVictor Chan                // the top when scrolled.
1278a32c79c66f4a134e6650a1acf307523221110e4fVictor Chan                return nextPagePosition - 1 == position ? nextPagePosition : nextPagePosition - 1;
12798b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
12808b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
12818b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // End of the list.
12828b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        return nextPagePosition;
12838b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
12848b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
12858b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
12868b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * In this style, the focus will scroll down to the middle of the screen and lock there
12878b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * so that moving in either direction will move the entire list by 1.
12888b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     */
12898b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private boolean onRequestChildFocusMarioStyle(RecyclerView parent, View child) {
12908b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int focusedPosition = getPosition(child);
12918b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (focusedPosition == mLastChildPositionToRequestFocus) {
12928b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return true;
12938b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
12948b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        mLastChildPositionToRequestFocus = focusedPosition;
12958b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
12968b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int availableHeight = getAvailableHeight();
12978b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int focusedChildTop = getDecoratedTop(child);
12988b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int focusedChildBottom = getDecoratedBottom(child);
12998b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
13008b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int childIndex = parent.indexOfChild(child);
13018b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // Iterate through children starting at the focused child to find the child above it to
13028b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // smooth scroll to such that the focused child will be as close to the middle of the screen
13038b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // as possible.
13048b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        for (int i = childIndex; i >= 0; i--) {
13058b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            View childAtI = getChildAt(i);
13068b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            if (childAtI == null) {
13078b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                Log.e(TAG, "Child is null at index " + i);
13088b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                continue;
13098b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
13108b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // We haven't found a view that is more than half of the recycler view height above it
13118b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // but we've reached the top so we can't go any further.
13128b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            if (i == 0) {
13138b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                parent.smoothScrollToPosition(getPosition(childAtI));
13148b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                break;
13158b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
13168b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
13178b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // Because we want to scroll to the first view that is less than half of the screen
13188b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // away from the focused view, we "look ahead" one view. When the look ahead view
13198b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // is more than availableHeight / 2 away, the current child at i is the one we want to
13208b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // scroll to. However, sometimes, that view can be null (ie, if the view is in
13218b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // transition). In that case, just skip that view.
13228b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
13238b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            View childBefore = getChildAt(i - 1);
13248b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            if (childBefore == null) {
13258b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                continue;
13268b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
13278b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            int distanceToChildBeforeFromTop = focusedChildTop - getDecoratedTop(childBefore);
13288b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            int distanceToChildBeforeFromBottom = focusedChildBottom - getDecoratedTop(childBefore);
13298b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
13308b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            if (distanceToChildBeforeFromTop > availableHeight / 2
13318b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                    || distanceToChildBeforeFromBottom > availableHeight) {
13328b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                parent.smoothScrollToPosition(getPosition(childAtI));
13338b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                break;
13348b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
13358b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
13368b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        return true;
13378b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
13388b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
13398b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
13408b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * In this style, you can free scroll in the middle of the list but if you get to the edge,
13418b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * the list will advance to ensure that there is context ahead of the focused item.
13428b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     */
13438b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private boolean onRequestChildFocusSuperMarioStyle(RecyclerView parent,
13448b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                                                       RecyclerView.State state, View child) {
13458b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int focusedPosition = getPosition(child);
13468b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (focusedPosition == mLastChildPositionToRequestFocus) {
13478b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return true;
13488b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
13498b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        mLastChildPositionToRequestFocus = focusedPosition;
13508b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
13518b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int bottomEdgeThatMustBeOnScreen;
13528b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int focusedIndex = parent.indexOfChild(child);
13538b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        // The amount of the last card at the end that must be showing to count as visible.
13548b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int peekAmount = mContext.getResources()
13558b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                .getDimensionPixelSize(R.dimen.car_last_card_peek_amount);
13568b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (focusedPosition == state.getItemCount() - 1) {
13578b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // The last item is focused.
13588b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            bottomEdgeThatMustBeOnScreen = getDecoratedBottom(child);
13598b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        } else if (focusedIndex == getChildCount() - 1) {
13608b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // The last laid out item is focused. Scroll enough so that the next card has at least
13618b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // the peek size visible
13628b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            ViewGroup.MarginLayoutParams params =
13638b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                    (ViewGroup.MarginLayoutParams) child.getLayoutParams();
13648b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // We add params.topMargin as an estimate because we don't actually know the top margin
13658b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // of the next row.
13668b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            bottomEdgeThatMustBeOnScreen = getDecoratedBottom(child) +
13678b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                    params.bottomMargin + params.topMargin + peekAmount;
13688b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        } else {
13698b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            View nextChild = getChildAt(focusedIndex + 1);
13708b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            bottomEdgeThatMustBeOnScreen = getDecoratedTop(nextChild) + peekAmount;
13718b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
13728b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
13738b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (bottomEdgeThatMustBeOnScreen > getHeight()) {
13748b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // We're going to have to scroll because the bottom edge that must be on screen is past
13758b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // the bottom.
13768b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            int topEdgeToFindViewUnder = getPaddingTop() +
13778b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                    bottomEdgeThatMustBeOnScreen - getHeight();
13788b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
13798b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            View nextChild = null;
13808b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            for (int i = 0; i < getChildCount(); i++) {
13818b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                View potentialNextChild = getChildAt(i);
13828b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                RecyclerView.LayoutParams params = getParams(potentialNextChild);
13838b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                float top = getDecoratedTop(potentialNextChild) - params.topMargin;
13848b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                if (top >= topEdgeToFindViewUnder) {
13858b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                    nextChild = potentialNextChild;
13868b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                    break;
13878b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                }
13888b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
13898b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
13908b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            if (nextChild == null) {
13918b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                Log.e(TAG, "There is no view under " + topEdgeToFindViewUnder);
13928b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                return true;
13938b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
13948b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            int nextChildPosition = getPosition(nextChild);
13958b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            parent.smoothScrollToPosition(nextChildPosition);
13968b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        } else {
13978b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            int firstFullyVisibleIndex = getFirstFullyVisibleChildIndex();
13988b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            if (focusedIndex <= firstFullyVisibleIndex) {
13998b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                parent.smoothScrollToPosition(Math.max(focusedPosition - 1, 0));
14008b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
14018b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
14028b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        return true;
14038b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
14048b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
14058b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
14068b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * We don't actually know the size of every single view, only what is currently laid out.
14078b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * This makes it difficult to do accurate scrollbar calculations. However, lists in the car
14088b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * often consist of views with identical heights. Because of that, we can use
14098b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * a single sample view to do our calculations for. The main exceptions are in the first items
14108b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * of a list (hero card, last call card, etc) so if the first view is at position 0, we pick
14118b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * the next one.
14128b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     *
14138b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * @return The decorated measured height of the sample view plus its margins.
14148b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     */
14158b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private int getSampleViewHeight() {
14168b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (mSampleViewHeight != -1) {
14178b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return mSampleViewHeight;
14188b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
14198b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int sampleViewIndex = getFirstFullyVisibleChildIndex();
14208b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        View sampleView = getChildAt(sampleViewIndex);
14218b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (getPosition(sampleView) == 0 && sampleViewIndex < getChildCount() - 1) {
14228b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            sampleView = getChildAt(++sampleViewIndex);
14238b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
14248b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        RecyclerView.LayoutParams params = getParams(sampleView);
14258b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int height =
14268b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                getDecoratedMeasuredHeight(sampleView) + params.topMargin + params.bottomMargin;
14278b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (height == 0) {
14288b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // This can happen if the view isn't measured yet.
14298b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            Log.w(TAG, "The sample view has a height of 0. Returning a dummy value for now " +
14308b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                    "that won't be cached.");
14318b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            height = mContext.getResources().getDimensionPixelSize(R.dimen.car_sample_row_height);
14328b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        } else {
14338b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            mSampleViewHeight = height;
14348b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
14358b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        return height;
14368b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
14378b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
14388b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
14398b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * @return The height of the RecyclerView excluding padding.
14408b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     */
14418b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private int getAvailableHeight() {
14428b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        return getHeight() - getPaddingTop() - getPaddingBottom();
14438b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
14448b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
14458b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
14468b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * @return {@link RecyclerView.LayoutParams} for the given view or null if it isn't a child
14478b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     *         of {@link RecyclerView}.
14488b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     */
14498b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private static RecyclerView.LayoutParams getParams(View view) {
14508b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        return (RecyclerView.LayoutParams) view.getLayoutParams();
14518b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
14528b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
14538b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
14548b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * Custom {@link LinearSmoothScroller} that has:
14558b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     *     a) Custom control over the speed of scrolls.
14568b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     *     b) Scrolling snaps to start. All of our scrolling logic depends on that.
14578b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     *     c) Keeps track of some state of the current scroll so that can aid in things like
14588b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     *        the scrollbar calculations.
14598b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     */
14608b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private final class CarSmoothScroller extends LinearSmoothScroller {
14618b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        /** This value (150) was hand tuned by UX for what felt right. **/
14628b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        private static final float MILLISECONDS_PER_INCH = 150f;
14638b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        /** This value (0.45) was hand tuned by UX for what felt right. **/
14648b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        private static final float DECELERATION_TIME_DIVISOR = 0.45f;
14658b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        private static final int NON_TOUCH_MAX_DECELERATION_MS = 1000;
14668b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
14678b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        /** This value (1.8) was hand tuned by UX for what felt right. **/
14688b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        private final Interpolator mInterpolator = new DecelerateInterpolator(1.8f);
14698b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
14708b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        private final boolean mHasTouch;
14718b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        private final int mTargetPosition;
14728b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
14738b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
14748b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        public CarSmoothScroller(Context context, int targetPosition) {
14758b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            super(context);
14768b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            mTargetPosition = targetPosition;
14778b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            mHasTouch = mContext.getResources().getBoolean(R.bool.car_true_for_touch);
14788b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
14798b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
14808b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        @Override
14818b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        public PointF computeScrollVectorForPosition(int i) {
14828b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            if (getChildCount() == 0) {
14838b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                return null;
14848b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
14858b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            final int firstChildPos = getPosition(getChildAt(getFirstFullyVisibleChildIndex()));
14868b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            final int direction = (mTargetPosition < firstChildPos) ? -1 : 1;
14878b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return new PointF(0, direction);
14888b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
14898b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
14908b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        @Override
14918b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        protected int getVerticalSnapPreference() {
14928b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // This is key for most of the scrolling logic that guarantees that scrolling
14938b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            // will settle with a view aligned to the top.
14948b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return LinearSmoothScroller.SNAP_TO_START;
14958b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
14968b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
14978b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        @Override
14988b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        protected void onTargetFound(View targetView, RecyclerView.State state, Action action) {
14998b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            int dy = calculateDyToMakeVisible(targetView, SNAP_TO_START);
15008b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            if (dy == 0) {
15018b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                if (DEBUG) {
15028b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                    Log.d(TAG, "Scroll distance is 0");
15038b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                }
15048b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                return;
15058b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
15068b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
15078b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            final int time = calculateTimeForDeceleration(dy);
15088b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            if (time > 0) {
15098b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                action.update(0, -dy, time, mInterpolator);
15108b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
15118b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
15128b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
15138b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        @Override
15148b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
15158b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return MILLISECONDS_PER_INCH / displayMetrics.densityDpi;
15168b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
15178b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
15188b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        @Override
15198b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        protected int calculateTimeForDeceleration(int dx) {
15208b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            int time = (int) Math.ceil(calculateTimeForScrolling(dx) / DECELERATION_TIME_DIVISOR);
15218b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return mHasTouch ? time : Math.min(time, NON_TOUCH_MAX_DECELERATION_MS);
15228b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
15238b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
15248b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        public int getTargetPosition() {
15258b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return mTargetPosition;
15268b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
15278b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
15288b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen}
1529