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 android.support.percent; 18 19 import android.content.Context; 20 import android.content.res.TypedArray; 21 import android.support.annotation.RequiresApi; 22 import android.util.AttributeSet; 23 import android.view.ViewGroup; 24 import android.widget.FrameLayout; 25 26 /** 27 * Subclass of {@link android.widget.FrameLayout} that supports percentage based dimensions and 28 * margins. 29 * 30 * You can specify dimension or a margin of child by using attributes with "Percent" suffix. Follow 31 * this example: 32 * 33 * <pre class="prettyprint"> 34 * <android.support.percent.PercentFrameLayout 35 * xmlns:android="http://schemas.android.com/apk/res/android" 36 * xmlns:app="http://schemas.android.com/apk/res-auto" 37 * android:layout_width="match_parent" 38 * android:layout_height="match_parent"> 39 * <ImageView 40 * app:layout_widthPercent="50%" 41 * app:layout_heightPercent="50%" 42 * app:layout_marginTopPercent="25%" 43 * app:layout_marginLeftPercent="25%"/> 44 * </android.support.percent.PercentFrameLayout> 45 * </pre> 46 * 47 * The attributes that you can use are: 48 * <ul> 49 * <li>{@code layout_widthPercent} 50 * <li>{@code layout_heightPercent} 51 * <li>{@code layout_marginPercent} 52 * <li>{@code layout_marginLeftPercent} 53 * <li>{@code layout_marginTopPercent} 54 * <li>{@code layout_marginRightPercent} 55 * <li>{@code layout_marginBottomPercent} 56 * <li>{@code layout_marginStartPercent} 57 * <li>{@code layout_marginEndPercent} 58 * <li>{@code layout_aspectRatio} 59 * </ul> 60 * 61 * It is not necessary to specify {@code layout_width/height} if you specify {@code 62 * layout_widthPercent.} However, if you want the view to be able to take up more space than what 63 * percentage value permits, you can add {@code layout_width/height="wrap_content"}. In that case 64 * if the percentage size is too small for the View's content, it will be resized using 65 * {@code wrap_content} rule. 66 * 67 * <p> 68 * You can also make one dimension be a fraction of the other by setting only width or height and 69 * using {@code layout_aspectRatio} for the second one to be calculated automatically. For 70 * example, if you would like to achieve 16:9 aspect ratio, you can write: 71 * <pre class="prettyprint"> 72 * android:layout_width="300dp" 73 * app:layout_aspectRatio="178%" 74 * </pre> 75 * This will make the aspect ratio 16:9 (1.78:1) with the width fixed at 300dp and height adjusted 76 * accordingly. 77 * 78 * @deprecated consider using ConstraintLayout and associated layouts instead. The following shows 79 * how to replicate the functionality of percentage layouts with a ConstraintLayout. The Guidelines 80 * are used to define each percentage break point, and then a Button view is stretched to fill 81 * the gap: 82 * 83 * <pre class="prettyprint"> 84 * <android.support.constraint.ConstraintLayout 85 * xmlns:android="http://schemas.android.com/apk/res/android" 86 * xmlns:app="http://schemas.android.com/apk/res-auto" 87 * android:layout_width="match_parent" 88 * android:layout_height="match_parent"> 89 * 90 * <android.support.constraint.Guideline 91 * android:layout_width="wrap_content" 92 * android:layout_height="wrap_content" 93 * android:id="@+id/left_guideline" 94 * app:layout_constraintGuide_percent=".15" 95 * android:orientation="vertical"/> 96 * 97 * <android.support.constraint.Guideline 98 * android:layout_width="wrap_content" 99 * android:layout_height="wrap_content" 100 * android:id="@+id/right_guideline" 101 * app:layout_constraintGuide_percent=".85" 102 * android:orientation="vertical"/> 103 * 104 * <android.support.constraint.Guideline 105 * android:layout_width="wrap_content" 106 * android:layout_height="wrap_content" 107 * android:id="@+id/top_guideline" 108 * app:layout_constraintGuide_percent=".15" 109 * android:orientation="horizontal"/> 110 * 111 * <android.support.constraint.Guideline 112 * android:layout_width="wrap_content" 113 * android:layout_height="wrap_content" 114 * android:id="@+id/bottom_guideline" 115 * app:layout_constraintGuide_percent=".85" 116 * android:orientation="horizontal"/> 117 * 118 * <Button 119 * android:text="Button" 120 * android:layout_width="0dp" 121 * android:layout_height="0dp" 122 * android:id="@+id/button" 123 * app:layout_constraintLeft_toLeftOf="@+id/left_guideline" 124 * app:layout_constraintRight_toRightOf="@+id/right_guideline" 125 * app:layout_constraintTop_toTopOf="@+id/top_guideline" 126 * app:layout_constraintBottom_toBottomOf="@+id/bottom_guideline" /> 127 * 128 * </android.support.constraint.ConstraintLayout> 129 */ 130 @Deprecated 131 public class PercentFrameLayout extends FrameLayout { 132 private final PercentLayoutHelper mHelper = new PercentLayoutHelper(this); 133 PercentFrameLayout(Context context)134 public PercentFrameLayout(Context context) { 135 super(context); 136 } 137 PercentFrameLayout(Context context, AttributeSet attrs)138 public PercentFrameLayout(Context context, AttributeSet attrs) { 139 super(context, attrs); 140 } 141 PercentFrameLayout(Context context, AttributeSet attrs, int defStyleAttr)142 public PercentFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) { 143 super(context, attrs, defStyleAttr); 144 } 145 146 @Override generateDefaultLayoutParams()147 protected LayoutParams generateDefaultLayoutParams() { 148 return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); 149 } 150 151 @Override generateLayoutParams(AttributeSet attrs)152 public LayoutParams generateLayoutParams(AttributeSet attrs) { 153 return new LayoutParams(getContext(), attrs); 154 } 155 156 @Override onMeasure(int widthMeasureSpec, int heightMeasureSpec)157 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 158 mHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec); 159 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 160 if (mHelper.handleMeasuredStateTooSmall()) { 161 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 162 } 163 } 164 165 @Override onLayout(boolean changed, int left, int top, int right, int bottom)166 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 167 super.onLayout(changed, left, top, right, bottom); 168 mHelper.restoreOriginalParams(); 169 } 170 171 /** 172 * @deprecated this class is deprecated along with its parent class. 173 */ 174 @Deprecated 175 public static class LayoutParams extends FrameLayout.LayoutParams 176 implements PercentLayoutHelper.PercentLayoutParams { 177 private PercentLayoutHelper.PercentLayoutInfo mPercentLayoutInfo; 178 LayoutParams(Context c, AttributeSet attrs)179 public LayoutParams(Context c, AttributeSet attrs) { 180 super(c, attrs); 181 mPercentLayoutInfo = PercentLayoutHelper.getPercentLayoutInfo(c, attrs); 182 } 183 LayoutParams(int width, int height)184 public LayoutParams(int width, int height) { 185 super(width, height); 186 } 187 LayoutParams(int width, int height, int gravity)188 public LayoutParams(int width, int height, int gravity) { 189 super(width, height, gravity); 190 } 191 LayoutParams(ViewGroup.LayoutParams source)192 public LayoutParams(ViewGroup.LayoutParams source) { 193 super(source); 194 } 195 LayoutParams(MarginLayoutParams source)196 public LayoutParams(MarginLayoutParams source) { 197 super(source); 198 } 199 LayoutParams(FrameLayout.LayoutParams source)200 public LayoutParams(FrameLayout.LayoutParams source) { 201 super((MarginLayoutParams) source); 202 gravity = source.gravity; 203 } 204 205 @RequiresApi(19) LayoutParams(LayoutParams source)206 public LayoutParams(LayoutParams source) { 207 // The copy constructor used here is only supported on API 19+. 208 this((FrameLayout.LayoutParams) source); 209 mPercentLayoutInfo = source.mPercentLayoutInfo; 210 } 211 212 @Override getPercentLayoutInfo()213 public PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo() { 214 if (mPercentLayoutInfo == null) { 215 mPercentLayoutInfo = new PercentLayoutHelper.PercentLayoutInfo(); 216 } 217 218 return mPercentLayoutInfo; 219 } 220 221 @Override setBaseAttributes(TypedArray a, int widthAttr, int heightAttr)222 protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) { 223 PercentLayoutHelper.fetchWidthAndHeight(this, a, widthAttr, heightAttr); 224 } 225 } 226 } 227