1 /* 2 * Copyright 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package androidx.leanback.widget; 17 18 import android.view.View; 19 20 import org.jspecify.annotations.NonNull; 21 22 /** 23 * Optional facet provided by {@link androidx.recyclerview.widget.RecyclerView.Adapter} or 24 * {@link androidx.recyclerview.widget.RecyclerView.ViewHolder} for 25 * use in {@link HorizontalGridView} and {@link VerticalGridView}. 26 * <p> 27 * ItemAlignmentFacet contains single or multiple {@link ItemAlignmentDef}s. First 28 * {@link ItemAlignmentDef} describes the default alignment position for ViewHolder, it also 29 * overrides the default item alignment settings on {@link VerticalGridView} and 30 * {@link HorizontalGridView} (see {@link BaseGridView#setItemAlignmentOffset(int)} etc). One 31 * ItemAlignmentFacet can have multiple {@link ItemAlignmentDef}s, e.g. having two aligned positions 32 * when child1 gets focus or child2 gets focus. Grid view will visit focused view and its 33 * ancestors till the root of ViewHolder to match {@link ItemAlignmentDef}s' 34 * {@link ItemAlignmentDef#getItemAlignmentFocusViewId()}. Once a match found, the 35 * {@link ItemAlignmentDef} is used to calculate alignment position. 36 */ 37 public final class ItemAlignmentFacet { 38 39 /** 40 * Value indicates that percent is not used. Equivalent to 0. 41 */ 42 public static final float ITEM_ALIGN_OFFSET_PERCENT_DISABLED = -1; 43 44 /** 45 * Definition of an alignment position under a view. 46 */ 47 public static class ItemAlignmentDef { 48 int mViewId = View.NO_ID; 49 int mFocusViewId = View.NO_ID; 50 int mOffset = 0; 51 float mOffsetPercent = 50f; 52 boolean mOffsetWithPadding = false; 53 private boolean mAlignToBaseline; 54 55 /** 56 * Sets number of pixels to the end of low edge. Supports right to left layout direction. 57 * 58 * @param offset In left to right or vertical case, it's the offset added to left/top edge. 59 * In right to left case, it's the offset subtracted from right edge. 60 */ setItemAlignmentOffset(int offset)61 public final void setItemAlignmentOffset(int offset) { 62 mOffset = offset; 63 } 64 65 /** 66 * Returns number of pixels to the end of low edge. Supports right to left layout direction. 67 * In left to right or vertical case, it's the offset added to left/top edge. In right to 68 * left case, it's the offset subtracted from right edge. 69 * 70 * @return Number of pixels to the end of low edge. 71 */ getItemAlignmentOffset()72 public final int getItemAlignmentOffset() { 73 return mOffset; 74 } 75 76 /** 77 * Sets whether applies padding to item alignment when 78 * {@link #getItemAlignmentOffsetPercent()} is 0 or 100. 79 * <p>When true: 80 * Applies start/top padding if {@link #getItemAlignmentOffsetPercent()} is 0. 81 * Applies end/bottom padding if {@link #getItemAlignmentOffsetPercent()} is 100. 82 * Does not apply padding if {@link #getItemAlignmentOffsetPercent()} is neither 0 nor 100. 83 * </p> 84 * <p>When false: does not apply padding</p> 85 */ setItemAlignmentOffsetWithPadding(boolean withPadding)86 public final void setItemAlignmentOffsetWithPadding(boolean withPadding) { 87 mOffsetWithPadding = withPadding; 88 } 89 90 /** 91 * Returns true if applies padding to item alignment when 92 * {@link #getItemAlignmentOffsetPercent()} is 0 or 100; returns false otherwise. 93 * <p>When true: 94 * Applies start/top padding when {@link #getItemAlignmentOffsetPercent()} is 0. 95 * Applies end/bottom padding when {@link #getItemAlignmentOffsetPercent()} is 100. 96 * Does not apply padding if {@link #getItemAlignmentOffsetPercent()} is neither 0 nor 100. 97 * </p> 98 * <p>When false: does not apply padding</p> 99 */ isItemAlignmentOffsetWithPadding()100 public final boolean isItemAlignmentOffsetWithPadding() { 101 return mOffsetWithPadding; 102 } 103 104 /** 105 * Sets the offset percent for item alignment in addition to offset. E.g., 40 106 * means 40% of width/height from the low edge. In the right to left case, it's the 40% 107 * width from right edge. Use {@link #ITEM_ALIGN_OFFSET_PERCENT_DISABLED} to disable. 108 */ setItemAlignmentOffsetPercent(float percent)109 public final void setItemAlignmentOffsetPercent(float percent) { 110 if ((percent < 0 || percent > 100) 111 && percent != ITEM_ALIGN_OFFSET_PERCENT_DISABLED) { 112 throw new IllegalArgumentException(); 113 } 114 mOffsetPercent = percent; 115 } 116 117 /** 118 * Gets the offset percent for item alignment in addition to offset. E.g., 40 119 * means 40% of the width from the low edge. In the right to left case, it's the 40% from 120 * right edge. Use {@link #ITEM_ALIGN_OFFSET_PERCENT_DISABLED} to disable. 121 */ getItemAlignmentOffsetPercent()122 public final float getItemAlignmentOffsetPercent() { 123 return mOffsetPercent; 124 } 125 126 /** 127 * Sets Id of which child view to be aligned. View.NO_ID refers to root view and should 128 * be only used in first one. Different view ids of {@link ItemAlignmentFacet 129 * #getAlignmentDefs()} define multiple alignment steps within one itemView, e.g. there are 130 * two child views R.id.child1 and R.id.child2. App may allocated two 131 * {@link ItemAlignmentDef}s, one with view id R.id.child1, the other with view id 132 * R.id.child2. Note this id may or may not be same as the child view that takes focus. 133 * 134 * @param viewId The id of child view that will be aligned to. 135 * @see #setItemAlignmentFocusViewId(int) 136 */ setItemAlignmentViewId(int viewId)137 public final void setItemAlignmentViewId(int viewId) { 138 mViewId = viewId; 139 } 140 141 /** 142 * Returns Id of which child view to be aligned. View.NO_ID refers to root view and should 143 * be only used in first one. Different view ids of {@link ItemAlignmentFacet 144 * #getAlignmentDefs()} define multiple alignment steps within one itemView, e.g. there are 145 * two child views R.id.child1 and R.id.child2. App may allocated two 146 * {@link ItemAlignmentDef}s, one with view id R.id.child1, the other with view id 147 * R.id.child2. Note this id may or may not be same as the child view that takes focus. 148 * 149 * @see #setItemAlignmentFocusViewId(int) 150 */ getItemAlignmentViewId()151 public final int getItemAlignmentViewId() { 152 return mViewId; 153 } 154 155 /** 156 * Sets Id of which child view take focus for alignment. When not set, it will use 157 * use same id of {@link #getItemAlignmentViewId()}. 158 * 159 * @param viewId The id of child view that will be focused to. 160 */ setItemAlignmentFocusViewId(int viewId)161 public final void setItemAlignmentFocusViewId(int viewId) { 162 mFocusViewId = viewId; 163 } 164 165 /** 166 * Returns Id of which child view take focus for alignment. When not set, it will use 167 * use same id of {@link #getItemAlignmentViewId()} 168 */ getItemAlignmentFocusViewId()169 public final int getItemAlignmentFocusViewId() { 170 return mFocusViewId != View.NO_ID ? mFocusViewId : mViewId; 171 } 172 173 /** 174 * When true, align to {@link View#getBaseline()} for the view of with id equals 175 * {@link #getItemAlignmentViewId()}; false otherwise. 176 * 177 * @param alignToBaseline Boolean indicating whether to align to view baseline. 178 */ setAlignedToTextViewBaseline(boolean alignToBaseline)179 public final void setAlignedToTextViewBaseline(boolean alignToBaseline) { 180 this.mAlignToBaseline = alignToBaseline; 181 } 182 183 /** 184 * Returns true when View should be aligned to {@link View#getBaseline()} 185 */ isAlignedToTextViewBaseLine()186 public boolean isAlignedToTextViewBaseLine() { 187 return mAlignToBaseline; 188 } 189 } 190 191 private ItemAlignmentDef[] mAlignmentDefs = new ItemAlignmentDef[]{new ItemAlignmentDef()}; 192 isMultiAlignment()193 public boolean isMultiAlignment() { 194 return mAlignmentDefs.length > 1; 195 } 196 197 /** 198 * Sets definitions of alignment positions. 199 */ setAlignmentDefs( @uppressWarnings"ArrayReturn") ItemAlignmentDef @onNull [] defs)200 public void setAlignmentDefs( 201 @SuppressWarnings("ArrayReturn") ItemAlignmentDef @NonNull [] defs) { 202 if (defs == null || defs.length < 1) { 203 throw new IllegalArgumentException(); 204 } 205 mAlignmentDefs = defs; 206 } 207 208 /** 209 * Returns read only definitions of alignment positions. 210 */ 211 @SuppressWarnings("ArrayReturn") getAlignmentDefs()212 public ItemAlignmentDef @NonNull [] getAlignmentDefs() { 213 return mAlignmentDefs; 214 } 215 } 216