1 /*
2  * Copyright (C) 2015 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 
17 package androidx.constraintlayout.widget;
18 
19 import android.annotation.SuppressLint;
20 import android.content.Context;
21 import android.graphics.Canvas;
22 import android.util.AttributeSet;
23 import android.view.View;
24 
25 import org.jspecify.annotations.NonNull;
26 
27 /**
28  * Utility class representing a Guideline helper object for
29  * {@link ConstraintLayout}.
30  * Helper objects are not displayed on device
31  * (they are marked as {@code View.GONE}) and are only used
32  * for layout purposes. They only work within a
33  * {@link ConstraintLayout}.
34  *<p>
35  * A Guideline can be either horizontal or vertical:
36  * <ul>
37  *     <li>Vertical Guidelines have a width of zero and the height of their
38  *     {@link ConstraintLayout} parent</li>
39  *     <li>Horizontal Guidelines have a height of zero and the width of their
40  *     {@link ConstraintLayout} parent</li>
41  * </ul>
42  *<p>
43  * Positioning a Guideline is possible in three different ways:
44  * <ul>
45  *     <li>specifying a fixed distance from the left or the top of a layout
46  *     ({@code layout_constraintGuide_begin})</li>
47  *     <li>specifying a fixed distance from the right or the bottom of a layout
48  *     ({@code layout_constraintGuide_end})</li>
49  *     <li>specifying a percentage of the width or the height of a layout
50  *     ({@code layout_constraintGuide_percent})</li>
51  * </ul>
52  * <p>
53  * Widgets can then be constrained to a Guideline,
54  * allowing multiple widgets to be positioned easily from
55  * one Guideline, or allowing reactive layout behavior by using percent positioning.
56  * <p>
57  * See the list of attributes in
58  * {@link androidx.constraintlayout.widget.ConstraintLayout.LayoutParams} to set a Guideline
59  * in XML, as well as the corresponding {@link ConstraintSet#setGuidelineBegin},
60  * {@link ConstraintSet#setGuidelineEnd}
61  * and {@link ConstraintSet#setGuidelinePercent} functions in {@link ConstraintSet}.
62  * <p>
63  *   Example of a {@code Button} constrained to a vertical {@code Guideline}:
64  *   <pre>{@code
65  *          <androidx.constraintlayout.widget.ConstraintLayout
66  *              xmlns:android="http://schemas.android.com/apk/res/android"
67  *              xmlns:app="http://schemas.android.com/apk/res-auto"
68  *              xmlns:tools="http://schemas.android.com/tools"
69  *              android:layout_width="match_parent"
70  *              android:layout_height="match_parent">
71  *
72  *              <androidx.constraintlayout.widget.Guideline
73  *                  android:layout_width="wrap_content"
74  *                  android:layout_height="wrap_content"
75  *                  android:id="@+id/guideline"
76  *                  app:layout_constraintGuide_begin="100dp"
77  *                  android:orientation="vertical"/>
78  *              <Button
79  *                  android:text="Button"
80  *                  android:layout_width="wrap_content"
81  *                  android:layout_height="wrap_content"
82  *                  android:id="@+id/button"
83  *                  app:layout_constraintLeft_toLeftOf="@+id/guideline"
84  *                  android:layout_marginTop="16dp"
85  *                  app:layout_constraintTop_toTopOf="parent" />
86  *          </androidx.constraintlayout.widget.ConstraintLayout>
87  *        }
88  *  </pre>
89  * <p/>
90  */
91 public class Guideline extends View {
92     private boolean mFilterRedundantCalls = true;
Guideline(Context context)93     public Guideline(Context context) {
94         super(context);
95         super.setVisibility(View.GONE);
96     }
97 
Guideline(Context context, AttributeSet attrs)98     public Guideline(Context context, AttributeSet attrs) {
99         super(context, attrs);
100         super.setVisibility(View.GONE);
101     }
102 
Guideline(Context context, AttributeSet attrs, int defStyleAttr)103     public Guideline(Context context, AttributeSet attrs, int defStyleAttr) {
104         super(context, attrs, defStyleAttr);
105         super.setVisibility(View.GONE);
106     }
107 
Guideline(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)108     public Guideline(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
109         super(context, attrs, defStyleAttr);
110         super.setVisibility(View.GONE);
111     }
112 
113     /**
114      *
115      */
116     @Override
setVisibility(int visibility)117     public void setVisibility(int visibility) {
118     }
119 
120     /**
121      * We are overriding draw and not calling super.draw() here because
122      * Helper objects are not displayed on device.
123      *
124      *
125      */
126     @SuppressLint("MissingSuperCall")
127     @Override
draw(@onNull Canvas canvas)128     public void draw(@NonNull Canvas canvas) {
129 
130     }
131 
132     /**
133      *
134      */
135     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)136     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
137         setMeasuredDimension(0, 0);
138     }
139 
140     /**
141      * Set the guideline's distance from the top or left edge.
142      *
143      * @param margin the distance to the top or left edge
144      */
setGuidelineBegin(int margin)145     public void setGuidelineBegin(int margin) {
146         ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) getLayoutParams();
147         if (mFilterRedundantCalls && params.guideBegin == margin) {
148             return;
149         }
150         params.guideBegin = margin;
151         setLayoutParams(params);
152     }
153 
154     /**
155      * Set a guideline's distance to end.
156      *
157      * @param margin the margin to the right or bottom side of container
158      */
setGuidelineEnd(int margin)159     public void setGuidelineEnd(int margin) {
160         ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) getLayoutParams();
161         if (mFilterRedundantCalls && params.guideEnd == margin) {
162             return;
163         }
164         params.guideEnd = margin;
165         setLayoutParams(params);
166     }
167 
168     /**
169      * Set a Guideline's percent.
170      * @param ratio the ratio between the gap on the left and right 0.0 is top/left 0.5 is middle
171      */
setGuidelinePercent(float ratio)172     public void setGuidelinePercent(float ratio) {
173         ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) getLayoutParams();
174         if (mFilterRedundantCalls && params.guidePercent == ratio) {
175             return;
176         }
177         params.guidePercent = ratio;
178         setLayoutParams(params);
179     }
180 
181     /**
182      * filter redundant calls to setGuidelineBegin, setGuidelineEnd & setGuidelinePercent.
183      *
184      * By default calling setGuidelineStart,setGuideLineEnd and setGuidelinePercent will do nothing
185      * if the value is the same as the current value. This can disable that behaviour and call
186      * setLayoutParams(..) while will call requestLayout
187      *
188      * @param filter default true set false to always generate a setLayoutParams
189      */
setFilterRedundantCalls(boolean filter)190     public void setFilterRedundantCalls(boolean filter) {
191         this.mFilterRedundantCalls = filter;
192     }
193 }
194