WindowAlignment.java revision 8e3566285de4ac771d6188f62fe947e23d371a3d
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.BaseGridView.WINDOW_ALIGN_LOW_EDGE; 18import static android.support.v17.leanback.widget.BaseGridView.WINDOW_ALIGN_HIGH_EDGE; 19import static android.support.v17.leanback.widget.BaseGridView.WINDOW_ALIGN_BOTH_EDGE; 20import static android.support.v17.leanback.widget.BaseGridView.WINDOW_ALIGN_OFFSET_PERCENT_DISABLED; 21 22import static android.support.v7.widget.RecyclerView.HORIZONTAL; 23 24import android.view.View; 25 26/** 27 * Maintains Window Alignment information of two axis. 28 */ 29class WindowAlignment { 30 31 /** 32 * Maintains alignment information in one direction. 33 */ 34 public static class Axis { 35 /** 36 * mScrollCenter is used to calculate dynamic transformation based on how far a view 37 * is from the mScrollCenter. For example, the views with center close to mScrollCenter 38 * will be scaled up. 39 */ 40 private float mScrollCenter; 41 /** 42 * Right or bottom edge of last child. 43 */ 44 private int mMaxEdge; 45 /** 46 * Left or top edge of first child, typically should be zero. 47 */ 48 private int mMinEdge; 49 /** 50 * Max Scroll value 51 */ 52 private int mMaxScroll; 53 /** 54 * Min Scroll value 55 */ 56 private int mMinScroll; 57 58 private int mWindowAlignment = WINDOW_ALIGN_BOTH_EDGE; 59 60 private int mWindowAlignmentOffset = 0; 61 62 private float mWindowAlignmentOffsetPercent = 50f; 63 64 private int mSize; 65 66 private int mPaddingLow; 67 68 private int mPaddingHigh; 69 70 private boolean mReversedFlow = false; 71 72 private String mName; // for debugging 73 74 public Axis(String name) { 75 reset(); 76 mName = name; 77 } 78 79 final public int getWindowAlignment() { 80 return mWindowAlignment; 81 } 82 83 final public void setWindowAlignment(int windowAlignment) { 84 mWindowAlignment = windowAlignment; 85 } 86 87 final public int getWindowAlignmentOffset() { 88 return mWindowAlignmentOffset; 89 } 90 91 final public void setWindowAlignmentOffset(int offset) { 92 mWindowAlignmentOffset = offset; 93 } 94 95 final public void setWindowAlignmentOffsetPercent(float percent) { 96 if ((percent < 0 || percent > 100) 97 && percent != WINDOW_ALIGN_OFFSET_PERCENT_DISABLED) { 98 throw new IllegalArgumentException(); 99 } 100 mWindowAlignmentOffsetPercent = percent; 101 } 102 103 final public float getWindowAlignmentOffsetPercent() { 104 return mWindowAlignmentOffsetPercent; 105 } 106 107 final public int getScrollCenter() { 108 return (int) mScrollCenter; 109 } 110 111 /** set minEdge, Integer.MIN_VALUE means unknown*/ 112 final public void setMinEdge(int minEdge) { 113 mMinEdge = minEdge; 114 } 115 116 final public int getMinEdge() { 117 return mMinEdge; 118 } 119 120 /** set minScroll, Integer.MIN_VALUE means unknown*/ 121 final public void setMinScroll(int minScroll) { 122 mMinScroll = minScroll; 123 } 124 125 final public int getMinScroll() { 126 return mMinScroll; 127 } 128 129 final public void setLayoutDirection(int layoutDirection) { 130 mReversedFlow = layoutDirection == View.LAYOUT_DIRECTION_RTL; 131 } 132 133 final public void invalidateScrollMin() { 134 mMinEdge = Integer.MIN_VALUE; 135 mMinScroll = Integer.MIN_VALUE; 136 } 137 138 /** update max edge, Integer.MAX_VALUE means unknown*/ 139 final public void setMaxEdge(int maxEdge) { 140 mMaxEdge = maxEdge; 141 } 142 143 final public int getMaxEdge() { 144 return mMaxEdge; 145 } 146 147 /** update max scroll, Integer.MAX_VALUE means unknown*/ 148 final public void setMaxScroll(int maxScroll) { 149 mMaxScroll = maxScroll; 150 } 151 152 final public int getMaxScroll() { 153 return mMaxScroll; 154 } 155 156 final public void invalidateScrollMax() { 157 mMaxEdge = Integer.MAX_VALUE; 158 mMaxScroll = Integer.MAX_VALUE; 159 } 160 161 final public float updateScrollCenter(float scrollTarget) { 162 mScrollCenter = scrollTarget; 163 return scrollTarget; 164 } 165 166 private void reset() { 167 mScrollCenter = Integer.MIN_VALUE; 168 mMinEdge = Integer.MIN_VALUE; 169 mMaxEdge = Integer.MAX_VALUE; 170 } 171 172 final public boolean isMinUnknown() { 173 return mMinEdge == Integer.MIN_VALUE; 174 } 175 176 final public boolean isMaxUnknown() { 177 return mMaxEdge == Integer.MAX_VALUE; 178 } 179 180 final public void setSize(int size) { 181 mSize = size; 182 } 183 184 final public int getSize() { 185 return mSize; 186 } 187 188 final public void setPadding(int paddingLow, int paddingHigh) { 189 mPaddingLow = paddingLow; 190 mPaddingHigh = paddingHigh; 191 } 192 193 final public int getPaddingLow() { 194 return mPaddingLow; 195 } 196 197 final public int getPaddingHigh() { 198 return mPaddingHigh; 199 } 200 201 final public int getClientSize() { 202 return mSize - mPaddingLow - mPaddingHigh; 203 } 204 205 final public int getSystemScrollPos(boolean isFirst, boolean isLast) { 206 return getSystemScrollPos((int) mScrollCenter, isFirst, isLast); 207 } 208 209 final public int getSystemScrollPos(int scrollCenter, boolean isFirst, boolean isLast) { 210 int middlePosition; 211 if (mWindowAlignmentOffset >= 0) { 212 middlePosition = mWindowAlignmentOffset - mPaddingLow; 213 } else { 214 middlePosition = mSize + mWindowAlignmentOffset - mPaddingLow; 215 } 216 if (mWindowAlignmentOffsetPercent != WINDOW_ALIGN_OFFSET_PERCENT_DISABLED) { 217 middlePosition += (int) (mSize * mWindowAlignmentOffsetPercent / 100); 218 } 219 int clientSize = getClientSize(); 220 int afterMiddlePosition = clientSize - middlePosition; 221 boolean isMinUnknown = isMinUnknown(); 222 boolean isMaxUnknown = isMaxUnknown(); 223 boolean isAtMin = (!mReversedFlow) ? isFirst : isLast; 224 boolean isAtMax = (!mReversedFlow) ? isLast : isFirst; 225 if (!isMinUnknown && !isMaxUnknown && 226 (mWindowAlignment & WINDOW_ALIGN_BOTH_EDGE) == WINDOW_ALIGN_BOTH_EDGE) { 227 if (mMaxEdge - mMinEdge <= clientSize) { 228 // total children size is less than view port and we want to align 229 // both edge: align first child to start edge of view port 230 int result = (!mReversedFlow) ? 231 mMinEdge - mPaddingLow : 232 mMaxEdge - mPaddingLow - clientSize; 233 return result; 234 } 235 } 236 if (!isMinUnknown) { 237 if ((mWindowAlignment & WINDOW_ALIGN_LOW_EDGE) != 0 && 238 (isAtMin || scrollCenter - mMinEdge <= middlePosition)) { 239 // scroll center is within half of view port size: align the start edge 240 // of first child to the start edge of view port 241 int result = mMinEdge - mPaddingLow; 242 return result; 243 } 244 } 245 if (!isMaxUnknown) { 246 if ((mWindowAlignment & WINDOW_ALIGN_HIGH_EDGE) != 0 && 247 (isAtMax || mMaxEdge - scrollCenter <= afterMiddlePosition)) { 248 // scroll center is very close to the end edge of view port : align the 249 // end edge of last children (plus expanded size) to view port's end 250 int result = mMaxEdge - mPaddingLow - clientSize; 251 return result; 252 } 253 } 254 // else put scroll center in middle of view port 255 int result = scrollCenter - middlePosition - mPaddingLow; 256 return result; 257 } 258 259 @Override 260 public String toString() { 261 return "center: " + mScrollCenter + " min:" + mMinEdge + 262 " max:" + mMaxEdge; 263 } 264 265 } 266 267 private int mOrientation = HORIZONTAL; 268 269 final public Axis vertical = new Axis("vertical"); 270 271 final public Axis horizontal = new Axis("horizontal"); 272 273 private Axis mMainAxis = horizontal; 274 275 private Axis mSecondAxis = vertical; 276 277 final public Axis mainAxis() { 278 return mMainAxis; 279 } 280 281 final public Axis secondAxis() { 282 return mSecondAxis; 283 } 284 285 final public void setOrientation(int orientation) { 286 mOrientation = orientation; 287 if (mOrientation == HORIZONTAL) { 288 mMainAxis = horizontal; 289 mSecondAxis = vertical; 290 } else { 291 mMainAxis = vertical; 292 mSecondAxis = horizontal; 293 } 294 } 295 296 final public int getOrientation() { 297 return mOrientation; 298 } 299 300 final public void setLayoutDirection(int layoutDirection) { 301 horizontal.setLayoutDirection(layoutDirection); 302 } 303 304 final public void reset() { 305 mainAxis().reset(); 306 } 307 308 @Override 309 public String toString() { 310 return new StringBuffer().append("horizontal=") 311 .append(horizontal.toString()) 312 .append("; vertical=") 313 .append(vertical.toString()) 314 .toString(); 315 } 316 317} 318