1 /* 2 * Copyright (C) 2014 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.appcompat.widget; 18 19 import static android.view.View.MeasureSpec.AT_MOST; 20 import static android.view.View.MeasureSpec.EXACTLY; 21 import static android.view.View.MeasureSpec.getMode; 22 23 import static androidx.annotation.RestrictTo.Scope.LIBRARY; 24 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP; 25 26 import android.content.Context; 27 import android.graphics.Rect; 28 import android.util.AttributeSet; 29 import android.util.DisplayMetrics; 30 import android.util.TypedValue; 31 import android.widget.FrameLayout; 32 33 import androidx.annotation.RestrictTo; 34 import androidx.core.view.ViewCompat; 35 36 /** 37 * @hide 38 */ 39 @RestrictTo(LIBRARY) 40 public class ContentFrameLayout extends FrameLayout { 41 42 public interface OnAttachListener { onDetachedFromWindow()43 void onDetachedFromWindow(); onAttachedFromWindow()44 void onAttachedFromWindow(); 45 } 46 47 private TypedValue mMinWidthMajor; 48 private TypedValue mMinWidthMinor; 49 private TypedValue mFixedWidthMajor; 50 private TypedValue mFixedWidthMinor; 51 private TypedValue mFixedHeightMajor; 52 private TypedValue mFixedHeightMinor; 53 54 private final Rect mDecorPadding; 55 56 private OnAttachListener mAttachListener; 57 ContentFrameLayout(Context context)58 public ContentFrameLayout(Context context) { 59 this(context, null); 60 } 61 ContentFrameLayout(Context context, AttributeSet attrs)62 public ContentFrameLayout(Context context, AttributeSet attrs) { 63 this(context, attrs, 0); 64 } 65 ContentFrameLayout(Context context, AttributeSet attrs, int defStyleAttr)66 public ContentFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) { 67 super(context, attrs, defStyleAttr); 68 mDecorPadding = new Rect(); 69 } 70 71 /** 72 * @hide 73 */ 74 @RestrictTo(LIBRARY_GROUP) dispatchFitSystemWindows(Rect insets)75 public void dispatchFitSystemWindows(Rect insets) { 76 fitSystemWindows(insets); 77 } 78 setAttachListener(OnAttachListener attachListener)79 public void setAttachListener(OnAttachListener attachListener) { 80 mAttachListener = attachListener; 81 } 82 83 /** 84 * Notify this view of the window decor view's padding. We use these values when working out 85 * our size for the window size attributes. 86 * 87 * @hide 88 */ 89 @RestrictTo(LIBRARY_GROUP) setDecorPadding(int left, int top, int right, int bottom)90 public void setDecorPadding(int left, int top, int right, int bottom) { 91 mDecorPadding.set(left, top, right, bottom); 92 if (ViewCompat.isLaidOut(this)) { 93 requestLayout(); 94 } 95 } 96 97 @Override onMeasure(int widthMeasureSpec, int heightMeasureSpec)98 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 99 final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics(); 100 final boolean isPortrait = metrics.widthPixels < metrics.heightPixels; 101 102 final int widthMode = getMode(widthMeasureSpec); 103 final int heightMode = getMode(heightMeasureSpec); 104 105 boolean fixedWidth = false; 106 if (widthMode == AT_MOST) { 107 final TypedValue tvw = isPortrait ? mFixedWidthMinor : mFixedWidthMajor; 108 if (tvw != null && tvw.type != TypedValue.TYPE_NULL) { 109 int w = 0; 110 if (tvw.type == TypedValue.TYPE_DIMENSION) { 111 w = (int) tvw.getDimension(metrics); 112 } else if (tvw.type == TypedValue.TYPE_FRACTION) { 113 w = (int) tvw.getFraction(metrics.widthPixels, metrics.widthPixels); 114 } 115 if (w > 0) { 116 w -= (mDecorPadding.left + mDecorPadding.right); 117 final int widthSize = MeasureSpec.getSize(widthMeasureSpec); 118 widthMeasureSpec = MeasureSpec.makeMeasureSpec( 119 Math.min(w, widthSize), EXACTLY); 120 fixedWidth = true; 121 } 122 } 123 } 124 125 if (heightMode == AT_MOST) { 126 final TypedValue tvh = isPortrait ? mFixedHeightMajor : mFixedHeightMinor; 127 if (tvh != null && tvh.type != TypedValue.TYPE_NULL) { 128 int h = 0; 129 if (tvh.type == TypedValue.TYPE_DIMENSION) { 130 h = (int) tvh.getDimension(metrics); 131 } else if (tvh.type == TypedValue.TYPE_FRACTION) { 132 h = (int) tvh.getFraction(metrics.heightPixels, metrics.heightPixels); 133 } 134 if (h > 0) { 135 h -= (mDecorPadding.top + mDecorPadding.bottom); 136 final int heightSize = MeasureSpec.getSize(heightMeasureSpec); 137 heightMeasureSpec = MeasureSpec.makeMeasureSpec( 138 Math.min(h, heightSize), EXACTLY); 139 } 140 } 141 } 142 143 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 144 145 int width = getMeasuredWidth(); 146 boolean measure = false; 147 148 widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, EXACTLY); 149 150 if (!fixedWidth && widthMode == AT_MOST) { 151 final TypedValue tv = isPortrait ? mMinWidthMinor : mMinWidthMajor; 152 if (tv != null && tv.type != TypedValue.TYPE_NULL) { 153 int min = 0; 154 if (tv.type == TypedValue.TYPE_DIMENSION) { 155 min = (int) tv.getDimension(metrics); 156 } else if (tv.type == TypedValue.TYPE_FRACTION) { 157 min = (int) tv.getFraction(metrics.widthPixels, metrics.widthPixels); 158 } 159 if (min > 0) { 160 min -= (mDecorPadding.left + mDecorPadding.right); 161 } 162 if (width < min) { 163 widthMeasureSpec = MeasureSpec.makeMeasureSpec(min, EXACTLY); 164 measure = true; 165 } 166 } 167 } 168 169 if (measure) { 170 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 171 } 172 } 173 getMinWidthMajor()174 public TypedValue getMinWidthMajor() { 175 if (mMinWidthMajor == null) mMinWidthMajor = new TypedValue(); 176 return mMinWidthMajor; 177 } 178 getMinWidthMinor()179 public TypedValue getMinWidthMinor() { 180 if (mMinWidthMinor == null) mMinWidthMinor = new TypedValue(); 181 return mMinWidthMinor; 182 } 183 getFixedWidthMajor()184 public TypedValue getFixedWidthMajor() { 185 if (mFixedWidthMajor == null) mFixedWidthMajor = new TypedValue(); 186 return mFixedWidthMajor; 187 } 188 getFixedWidthMinor()189 public TypedValue getFixedWidthMinor() { 190 if (mFixedWidthMinor == null) mFixedWidthMinor = new TypedValue(); 191 return mFixedWidthMinor; 192 } 193 getFixedHeightMajor()194 public TypedValue getFixedHeightMajor() { 195 if (mFixedHeightMajor == null) mFixedHeightMajor = new TypedValue(); 196 return mFixedHeightMajor; 197 } 198 getFixedHeightMinor()199 public TypedValue getFixedHeightMinor() { 200 if (mFixedHeightMinor == null) mFixedHeightMinor = new TypedValue(); 201 return mFixedHeightMinor; 202 } 203 204 @Override onAttachedToWindow()205 protected void onAttachedToWindow() { 206 super.onAttachedToWindow(); 207 if (mAttachListener != null) { 208 mAttachListener.onAttachedFromWindow(); 209 } 210 } 211 212 @Override onDetachedFromWindow()213 protected void onDetachedFromWindow() { 214 super.onDetachedFromWindow(); 215 if (mAttachListener != null) { 216 mAttachListener.onDetachedFromWindow(); 217 } 218 } 219 } 220