WindowAlignment.java revision 8b068ddbbf22a246eab49ec25a2f7c3abfbdca51
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 * in compliance with the License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the License
10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 * or implied. See the License for the specific language governing permissions and limitations under
12 * the License.
13 */
14
15package android.support.v17.leanback.widget;
16
17import static android.support.v17.leanback.widget.BaseListView.WINDOW_ALIGN_LOW_EDGE;
18import static android.support.v17.leanback.widget.BaseListView.WINDOW_ALIGN_HIGH_EDGE;
19import static android.support.v17.leanback.widget.BaseListView.WINDOW_ALIGN_BOTH_EDGE;
20import static android.support.v17.leanback.widget.BaseListView.WINDOW_ALIGN_OFFSET_PERCENT_DISABLED;
21
22import static android.support.v7.widget.RecyclerView.HORIZONTAL;
23
24/**
25 * Maintains Window Alignment information of two axis.
26 */
27class WindowAlignment {
28
29    /**
30     * Maintains alignment information in one direction.
31     */
32    public static class Axis {
33        /**
34         * mScrollCenter is used to calculate dynamic transformation based on how far a view
35         * is from the mScrollCenter. For example, the views with center close to mScrollCenter
36         * will be scaled up.
37         */
38        private float mScrollCenter;
39        /**
40         * Right or bottom edge of last child.
41         */
42        private int mMaxEdge;
43        /**
44         * Left or top edge of first child, typically should be zero.
45         */
46        private int mMinEdge;
47
48        private int mWindowAlignment = WINDOW_ALIGN_BOTH_EDGE;
49
50        private int mWindowAlignmentOffset = 0;
51
52        private float mWindowAlignmentOffsetPercent = 50f;
53
54        private int mSize;
55
56        private int mPaddingLow;
57
58        private int mPaddingHigh;
59
60        private String mName; // for debugging
61
62        public Axis(String name) {
63            reset();
64            mName = name;
65        }
66
67        final public int getWindowAlignment() {
68            return mWindowAlignment;
69        }
70
71        final public void setWindowAlignment(int windowAlignment) {
72            mWindowAlignment = windowAlignment;
73        }
74
75        final public int getWindowAlignmentOffset() {
76            return mWindowAlignmentOffset;
77        }
78
79        final public void setWindowAlignmentOffset(int offset) {
80            mWindowAlignmentOffset = offset;
81        }
82
83        final public void setWindowAlignmentOffsetPercent(float percent) {
84            if ((percent < 0 || percent > 100)
85                    && percent != WINDOW_ALIGN_OFFSET_PERCENT_DISABLED) {
86                throw new IllegalArgumentException();
87            }
88            mWindowAlignmentOffsetPercent = percent;
89        }
90
91        final public float getWindowAlignmentOffsetPercent() {
92            return mWindowAlignmentOffsetPercent;
93        }
94
95        final public int getScrollCenter() {
96            return (int) mScrollCenter;
97        }
98
99        /** set minEdge,  Integer.MIN_VALUE means unknown*/
100        final public void setMinEdge(int minEdge) {
101            mMinEdge = minEdge;
102        }
103
104        public void invalidateScrollMin() {
105            mMinEdge = Integer.MIN_VALUE;
106        }
107
108        /** update max edge,  Integer.MAX_VALUE means unknown*/
109        final public void setMaxEdge(int maxEdge) {
110            mMaxEdge = maxEdge;
111        }
112
113        public void invalidateScrollMax() {
114            mMaxEdge = Integer.MAX_VALUE;
115        }
116
117        final public float updateScrollCenter(float scrollTarget) {
118            mScrollCenter = scrollTarget;
119            return scrollTarget;
120        }
121
122        private void reset() {
123            mScrollCenter = Integer.MIN_VALUE;
124            mMinEdge = Integer.MIN_VALUE;
125            mMaxEdge = Integer.MAX_VALUE;
126        }
127
128        final public boolean isMinUnknown() {
129            return mMinEdge == Integer.MIN_VALUE;
130        }
131
132        final public boolean isMaxUnknown() {
133            return mMaxEdge == Integer.MAX_VALUE;
134        }
135
136        final public void setSize(int size) {
137            mSize = size;
138        }
139
140        final public int getSize() {
141            return mSize;
142        }
143
144        final public void setPadding(int paddingLow, int paddingHigh) {
145            mPaddingLow = paddingLow;
146            mPaddingHigh = paddingHigh;
147        }
148
149        final public int getPaddingLow() {
150            return mPaddingLow;
151        }
152
153        final public int getPaddingHigh() {
154            return mPaddingHigh;
155        }
156
157        final public int getClientSize() {
158            return mSize - mPaddingLow - mPaddingHigh;
159        }
160
161        final public int getSystemScrollPos() {
162            return getSystemScrollPos((int) mScrollCenter);
163        }
164
165        final public int getSystemScrollPos(int scrollCenter) {
166            int middlePosition;
167            if (mWindowAlignmentOffset >= 0) {
168                middlePosition = mWindowAlignmentOffset - mPaddingLow;
169            } else {
170                middlePosition = mSize + mWindowAlignmentOffset - mPaddingLow;
171            }
172            if (mWindowAlignmentOffsetPercent != WINDOW_ALIGN_OFFSET_PERCENT_DISABLED) {
173                middlePosition += (int) (mSize * mWindowAlignmentOffsetPercent / 100);
174            }
175            int clientSize = getClientSize();
176            int afterMiddlePosition = clientSize - middlePosition;
177            boolean isMinUnknown = isMinUnknown();
178            boolean isMaxUnknown = isMaxUnknown();
179            if (!isMinUnknown && !isMaxUnknown &&
180                    (mWindowAlignment & WINDOW_ALIGN_BOTH_EDGE) == WINDOW_ALIGN_BOTH_EDGE) {
181                if (mMaxEdge - mMinEdge <= clientSize) {
182                    // total children size is less than view port and we want to align
183                    // both edge:  align first child to left edge of view port
184                    return mMinEdge - mPaddingLow;
185                }
186            }
187            if (!isMinUnknown) {
188                if ((mWindowAlignment & WINDOW_ALIGN_LOW_EDGE) != 0 &&
189                        scrollCenter - mMinEdge <= middlePosition) {
190                    // scroll center is within half of view port size: align the left edge
191                    // of first child to the left edge of view port
192                    return mMinEdge - mPaddingLow;
193                }
194            }
195            if (!isMaxUnknown) {
196                if ((mWindowAlignment & WINDOW_ALIGN_HIGH_EDGE) != 0 &&
197                        mMaxEdge - scrollCenter <= afterMiddlePosition) {
198                    // scroll center is very close to the right edge of view port : align the
199                    // right edge of last children (plus expanded size) to view port's right
200                    return mMaxEdge -mPaddingLow - (clientSize);
201                }
202            }
203            // else put scroll center in middle of view port
204            return scrollCenter - middlePosition - mPaddingLow;
205        }
206
207        @Override
208        public String toString() {
209            return "center: " + mScrollCenter + " min:" + mMinEdge +
210                    " max:" + mMaxEdge;
211        }
212
213    }
214
215    private int mOrientation = HORIZONTAL;
216
217    final public Axis vertical = new Axis("vertical");
218
219    final public Axis horizontal = new Axis("horizontal");
220
221    private Axis mMainAxis = horizontal;
222
223    private Axis mSecondAxis = vertical;
224
225    final public Axis mainAxis() {
226        return mMainAxis;
227    }
228
229    final public Axis secondAxis() {
230        return mSecondAxis;
231    }
232
233    final public void setOrientation(int orientation) {
234        mOrientation = orientation;
235        if (mOrientation == HORIZONTAL) {
236            mMainAxis = horizontal;
237            mSecondAxis = vertical;
238        } else {
239            mMainAxis = vertical;
240            mSecondAxis = horizontal;
241        }
242    }
243
244    final public int getOrientation() {
245        return mOrientation;
246    }
247
248    final public void reset() {
249        mainAxis().reset();
250    }
251
252    @Override
253    public String toString() {
254        return new StringBuffer().append("horizontal=")
255                .append(horizontal.toString())
256                .append("vertical=")
257                .append(vertical.toString())
258                .toString();
259    }
260
261}
262