1f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski/* 2f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * Copyright (C) 2015 The Android Open Source Project 3f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * 4f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * Licensed under the Apache License, Version 2.0 (the "License"); 5f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * you may not use this file except in compliance with the License. 6f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * You may obtain a copy of the License at 7f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * 8f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * http://www.apache.org/licenses/LICENSE-2.0 9f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * 10f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * Unless required by applicable law or agreed to in writing, software 11f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * distributed under the License is distributed on an "AS IS" BASIS, 12f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * See the License for the specific language governing permissions and 14f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * limitations under the License. 15f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski */ 16f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski 17f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynskipackage android.support.percent; 18f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski 19f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynskiimport android.content.Context; 20f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynskiimport android.content.res.TypedArray; 21f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynskiimport android.util.AttributeSet; 22f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynskiimport android.view.ViewGroup; 23f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynskiimport android.widget.RelativeLayout; 24f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski 25f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski/** 26f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * Subclass of {@link android.widget.RelativeLayout} that supports percentage based dimensions and 27f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * margins. 28f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * 29f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * You can specify dimension or a margin of child by using attributes with "Percent" suffix. Follow 30f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * this example: 31f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * 32f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * <pre class="prettyprint"> 33f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * <android.support.percent.PercentRelativeLayout 34f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * xmlns:android="http://schemas.android.com/apk/res/android" 35f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * xmlns:app="http://schemas.android.com/apk/res-auto" 36f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * android:layout_width="match_parent" 37300da29db1ab98a0e0e1ac5bf15c836f40ebfd5fFilip Gruszczynski * android:layout_height="match_parent"> 38f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * <ImageView 39f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * app:layout_widthPercent="50%" 40f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * app:layout_heightPercent="50%" 41f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * app:layout_marginTopPercent="25%" 42f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * app:layout_marginLeftPercent="25%"/> 43b17223fee6f704203a441db2fbaaefc61fef91c5Andrew Solovay * </android.support.percent.PercentFrameLayout> 44f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * </pre> 45f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * 46f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * The attributes that you can use are: 47f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * <ul> 48f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * <li>{@code layout_widthPercent} 49f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * <li>{@code layout_heightPercent} 50f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * <li>{@code layout_marginPercent} 51f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * <li>{@code layout_marginLeftPercent} 52f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * <li>{@code layout_marginTopPercent} 53f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * <li>{@code layout_marginRightPercent} 54f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * <li>{@code layout_marginBottomPercent} 55f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * <li>{@code layout_marginStartPercent} 56f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * <li>{@code layout_marginEndPercent} 57300da29db1ab98a0e0e1ac5bf15c836f40ebfd5fFilip Gruszczynski * <li>{@code layout_aspectRatio} 58f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * </ul> 59f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * 60f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * It is not necessary to specify {@code layout_width/height} if you specify {@code 61f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * layout_widthPercent.} However, if you want the view to be able to take up more space than what 62f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * percentage value permits, you can add {@code layout_width/height="wrap_content"}. In that case 63f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * if the percentage size is too small for the View's content, it will be resized using 64f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski * {@code wrap_content} rule. 65300da29db1ab98a0e0e1ac5bf15c836f40ebfd5fFilip Gruszczynski * 66300da29db1ab98a0e0e1ac5bf15c836f40ebfd5fFilip Gruszczynski * <p> 67300da29db1ab98a0e0e1ac5bf15c836f40ebfd5fFilip Gruszczynski * You can also make one dimension be a fraction of the other by setting only width or height and 68300da29db1ab98a0e0e1ac5bf15c836f40ebfd5fFilip Gruszczynski * using {@code layout_aspectRatio} for the second one to be calculated automatically. For 69300da29db1ab98a0e0e1ac5bf15c836f40ebfd5fFilip Gruszczynski * example, if you would like to achieve 16:9 aspect ratio, you can write: 70300da29db1ab98a0e0e1ac5bf15c836f40ebfd5fFilip Gruszczynski * <pre class="prettyprint"> 71300da29db1ab98a0e0e1ac5bf15c836f40ebfd5fFilip Gruszczynski * android:layout_width="300dp" 72300da29db1ab98a0e0e1ac5bf15c836f40ebfd5fFilip Gruszczynski * app:layout_aspectRatio="178%" 73300da29db1ab98a0e0e1ac5bf15c836f40ebfd5fFilip Gruszczynski * </pre> 74300da29db1ab98a0e0e1ac5bf15c836f40ebfd5fFilip Gruszczynski * This will make the aspect ratio 16:9 (1.78:1) with the width fixed at 300dp and height adjusted 75300da29db1ab98a0e0e1ac5bf15c836f40ebfd5fFilip Gruszczynski * accordingly. 76f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski */ 77f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynskipublic class PercentRelativeLayout extends RelativeLayout { 78f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski private final PercentLayoutHelper mHelper = new PercentLayoutHelper(this); 79f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski 80f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski public PercentRelativeLayout(Context context) { 81f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski super(context); 82f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski } 83f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski 84f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski public PercentRelativeLayout(Context context, AttributeSet attrs) { 85f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski super(context, attrs); 86f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski } 87f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski 88f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski public PercentRelativeLayout(Context context, AttributeSet attrs, int defStyle) { 89f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski super(context, attrs, defStyle); 90f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski } 91f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski 92f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski @Override 931ddfd71aa6dd24a5f87f93c55dd774f5eb7320e4Bryce Lee protected LayoutParams generateDefaultLayoutParams() { 941ddfd71aa6dd24a5f87f93c55dd774f5eb7320e4Bryce Lee return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); 951ddfd71aa6dd24a5f87f93c55dd774f5eb7320e4Bryce Lee } 961ddfd71aa6dd24a5f87f93c55dd774f5eb7320e4Bryce Lee 971ddfd71aa6dd24a5f87f93c55dd774f5eb7320e4Bryce Lee @Override 98f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski public LayoutParams generateLayoutParams(AttributeSet attrs) { 99f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski return new LayoutParams(getContext(), attrs); 100f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski } 101f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski 102f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski @Override 103f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 104f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski mHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec); 105f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski super.onMeasure(widthMeasureSpec, heightMeasureSpec); 106f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski if (mHelper.handleMeasuredStateTooSmall()) { 107f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski super.onMeasure(widthMeasureSpec, heightMeasureSpec); 108f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski } 109f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski } 110f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski 111f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski @Override 112f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 113f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski super.onLayout(changed, left, top, right, bottom); 114f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski mHelper.restoreOriginalParams(); 115f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski } 116f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski 117f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski public static class LayoutParams extends RelativeLayout.LayoutParams 118f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski implements PercentLayoutHelper.PercentLayoutParams { 119f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski private PercentLayoutHelper.PercentLayoutInfo mPercentLayoutInfo; 120f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski 121f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski public LayoutParams(Context c, AttributeSet attrs) { 122f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski super(c, attrs); 123f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski mPercentLayoutInfo = PercentLayoutHelper.getPercentLayoutInfo(c, attrs); 124f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski } 125f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski 126f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski public LayoutParams(int width, int height) { 127f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski super(width, height); 128f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski } 129f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski 130f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski public LayoutParams(ViewGroup.LayoutParams source) { 131f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski super(source); 132f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski } 133f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski 134f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski public LayoutParams(MarginLayoutParams source) { 135f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski super(source); 136f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski } 137f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski 138f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski @Override 139f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski public PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo() { 140796a29bc6265696029863ec22ee753ff4065a8dbBryce Lee if (mPercentLayoutInfo == null) { 141796a29bc6265696029863ec22ee753ff4065a8dbBryce Lee mPercentLayoutInfo = new PercentLayoutHelper.PercentLayoutInfo(); 142796a29bc6265696029863ec22ee753ff4065a8dbBryce Lee } 143796a29bc6265696029863ec22ee753ff4065a8dbBryce Lee 144f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski return mPercentLayoutInfo; 145f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski } 146f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski 147f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski @Override 148f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) { 149f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski PercentLayoutHelper.fetchWidthAndHeight(this, a, widthAttr, heightAttr); 150f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski } 151f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski } 152f9cabe2ad76a19d555b5b656d8167bdb167c9d03Filip Gruszczynski} 153