1 /*
2  * Copyright (C) 2015 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 package androidx.leanback.widget;
15 
16 import android.view.View;
17 
18 import androidx.recyclerview.widget.RecyclerView;
19 
20 /**
21  * Optional facet provided by {@link RecyclerView.Adapter} or {@link RecyclerView.ViewHolder} for
22  * use in {@link HorizontalGridView} and {@link VerticalGridView}. Apps using {@link Presenter} may
23  * set facet using {@link Presenter#setFacet(Class, Object)} or
24  * {@link Presenter.ViewHolder#setFacet(Class, Object)}. Facet on ViewHolder has a higher priority
25  * than Presenter or Adapter.
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 final static 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          * @param offset In left to right or vertical case, it's the offset added to left/top edge.
58          *               In right to left case, it's the offset subtracted from right edge.
59          */
setItemAlignmentOffset(int offset)60         public final void setItemAlignmentOffset(int offset) {
61             mOffset = offset;
62         }
63 
64         /**
65          * Returns number of pixels to the end of low edge. Supports right to left layout direction.
66          * In left to right or vertical case, it's the offset added to left/top edge. In right to
67          * left case, it's the offset subtracted from right edge.
68          * @return Number of pixels to the end of low edge.
69          */
getItemAlignmentOffset()70         public final int getItemAlignmentOffset() {
71             return mOffset;
72         }
73 
74         /**
75          * Sets whether applies padding to item alignment when
76          * {@link #getItemAlignmentOffsetPercent()} is 0 or 100.
77          * <p>When true:
78          * Applies start/top padding if {@link #getItemAlignmentOffsetPercent()} is 0.
79          * Applies end/bottom padding if {@link #getItemAlignmentOffsetPercent()} is 100.
80          * Does not apply padding if {@link #getItemAlignmentOffsetPercent()} is neither 0 nor 100.
81          * </p>
82          * <p>When false: does not apply padding</p>
83          */
setItemAlignmentOffsetWithPadding(boolean withPadding)84         public final void setItemAlignmentOffsetWithPadding(boolean withPadding) {
85             mOffsetWithPadding = withPadding;
86         }
87 
88         /**
89          * Returns true if applies padding to item alignment when
90          * {@link #getItemAlignmentOffsetPercent()} is 0 or 100; returns false otherwise.
91          * <p>When true:
92          * Applies start/top padding when {@link #getItemAlignmentOffsetPercent()} is 0.
93          * Applies end/bottom padding when {@link #getItemAlignmentOffsetPercent()} is 100.
94          * Does not apply padding if {@link #getItemAlignmentOffsetPercent()} is neither 0 nor 100.
95          * </p>
96          * <p>When false: does not apply padding</p>
97          */
isItemAlignmentOffsetWithPadding()98         public final boolean isItemAlignmentOffsetWithPadding() {
99             return mOffsetWithPadding;
100         }
101 
102         /**
103          * Sets the offset percent for item alignment in addition to offset.  E.g., 40
104          * means 40% of width/height from the low edge. In the right to left case, it's the 40%
105          * width from right edge. Use {@link #ITEM_ALIGN_OFFSET_PERCENT_DISABLED} to disable.
106          */
setItemAlignmentOffsetPercent(float percent)107         public final void setItemAlignmentOffsetPercent(float percent) {
108             if ((percent < 0 || percent > 100)
109                     && percent != ITEM_ALIGN_OFFSET_PERCENT_DISABLED) {
110                 throw new IllegalArgumentException();
111             }
112             mOffsetPercent = percent;
113         }
114 
115         /**
116          * Gets the offset percent for item alignment in addition to offset. E.g., 40
117          * means 40% of the width from the low edge. In the right to left case, it's the 40% from
118          * right edge. Use {@link #ITEM_ALIGN_OFFSET_PERCENT_DISABLED} to disable.
119          */
getItemAlignmentOffsetPercent()120         public final float getItemAlignmentOffsetPercent() {
121             return mOffsetPercent;
122         }
123 
124         /**
125          * Sets Id of which child view to be aligned.  View.NO_ID refers to root view and should
126          * be only used in first one.  Different view ids of {@link ItemAlignmentFacet
127          * #getAlignmentDefs()} define multiple alignment steps within one itemView, e.g. there are
128          * two child views R.id.child1 and R.id.child2. App may allocated two
129          * {@link ItemAlignmentDef}s, one with view id R.id.child1, the other with view id
130          * R.id.child2. Note this id may or may not be same as the child view that takes focus.
131          *
132          * @param viewId The id of child view that will be aligned to.
133          * @see #setItemAlignmentFocusViewId(int)
134          */
setItemAlignmentViewId(int viewId)135         public final void setItemAlignmentViewId(int viewId) {
136             mViewId = viewId;
137         }
138 
139         /**
140          * Returns Id of which child view to be aligned.  View.NO_ID refers to root view and should
141          * be only used in first one.  Different view ids of {@link ItemAlignmentFacet
142          * #getAlignmentDefs()} define multiple alignment steps within one itemView, e.g. there are
143          * two child views R.id.child1 and R.id.child2. App may allocated two
144          * {@link ItemAlignmentDef}s, one with view id R.id.child1, the other with view id
145          * R.id.child2. Note this id may or may not be same as the child view that takes focus.
146          *
147          * @see #setItemAlignmentFocusViewId(int)
148          */
getItemAlignmentViewId()149         public final int getItemAlignmentViewId() {
150             return mViewId;
151         }
152 
153         /**
154          * Sets Id of which child view take focus for alignment.  When not set, it will use
155          * use same id of {@link #getItemAlignmentViewId()}.
156          * @param viewId The id of child view that will be focused to.
157          */
setItemAlignmentFocusViewId(int viewId)158         public final void setItemAlignmentFocusViewId(int viewId) {
159             mFocusViewId = viewId;
160         }
161 
162         /**
163          * Returns Id of which child view take focus for alignment.  When not set, it will use
164          * use same id of {@link #getItemAlignmentViewId()}
165          */
getItemAlignmentFocusViewId()166         public final int getItemAlignmentFocusViewId() {
167             return mFocusViewId != View.NO_ID ? mFocusViewId : mViewId;
168         }
169 
170         /**
171          * When true, align to {@link View#getBaseline()} for the view of with id equals
172          * {@link #getItemAlignmentViewId()}; false otherwise.
173          * @param alignToBaseline Boolean indicating whether to align to view baseline.
174          */
setAlignedToTextViewBaseline(boolean alignToBaseline)175         public final void setAlignedToTextViewBaseline(boolean alignToBaseline) {
176             this.mAlignToBaseline = alignToBaseline;
177         }
178 
179         /**
180          * Returns true when View should be aligned to {@link View#getBaseline()}
181          */
isAlignedToTextViewBaseLine()182         public boolean isAlignedToTextViewBaseLine() {
183             return mAlignToBaseline;
184         }
185     }
186 
187     private ItemAlignmentDef[] mAlignmentDefs = new ItemAlignmentDef[]{new ItemAlignmentDef()};
188 
isMultiAlignment()189     public boolean isMultiAlignment() {
190         return mAlignmentDefs.length > 1;
191     }
192 
193     /**
194      * Sets definitions of alignment positions.
195      */
setAlignmentDefs(ItemAlignmentDef[] defs)196     public void setAlignmentDefs(ItemAlignmentDef[] defs) {
197         if (defs == null || defs.length < 1) {
198             throw new IllegalArgumentException();
199         }
200         mAlignmentDefs = defs;
201     }
202 
203     /**
204      * Returns read only definitions of alignment positions.
205      */
getAlignmentDefs()206     public ItemAlignmentDef[] getAlignmentDefs() {
207         return mAlignmentDefs;
208     }
209 
210 }
211