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