1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5  * except 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
10  * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11  * KIND, either express or implied. See the License for the specific language governing
12  * permissions and limitations under the License.
13  */
14 
15 package com.android.car.developeroptions.widget;
16 
17 import android.content.Context;
18 import android.content.res.TypedArray;
19 import android.util.AttributeSet;
20 import android.widget.FrameLayout;
21 
22 import androidx.annotation.VisibleForTesting;
23 
24 import com.android.car.developeroptions.R;
25 
26 /**
27  * A {@link FrameLayout} with customizable aspect ratio.
28  * This is used to avoid dynamically calculating the height for the frame. Default aspect
29  * ratio will be 1 if none is set in layout attribute.
30  */
31 public final class AspectRatioFrameLayout extends FrameLayout {
32 
33     private static final float ASPECT_RATIO_CHANGE_THREASHOLD = 0.01f;
34 
35     @VisibleForTesting
36     float mAspectRatio = 1.0f;
37 
AspectRatioFrameLayout(Context context)38     public AspectRatioFrameLayout(Context context) {
39         this(context, null);
40     }
41 
AspectRatioFrameLayout(Context context, AttributeSet attrs)42     public AspectRatioFrameLayout(Context context, AttributeSet attrs) {
43         this(context, attrs, 0);
44     }
45 
AspectRatioFrameLayout(Context context, AttributeSet attrs, int defStyle)46     public AspectRatioFrameLayout(Context context, AttributeSet attrs, int defStyle) {
47         super(context, attrs, defStyle);
48         if (attrs != null) {
49             TypedArray array =
50                     context.obtainStyledAttributes(attrs, R.styleable.AspectRatioFrameLayout);
51             mAspectRatio = array.getFloat(
52                     R.styleable.AspectRatioFrameLayout_aspectRatio, 1.0f);
53             array.recycle();
54         }
55     }
56 
setAspectRatio(float aspectRadio)57     public void setAspectRatio(float aspectRadio) {
58         mAspectRatio = aspectRadio;
59     }
60 
61     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)62     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
63         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
64         int width = getMeasuredWidth();
65         int height = getMeasuredHeight();
66         if (width == 0 || height == 0) {
67             return;
68         }
69         final float viewAspectRatio = (float) width / height;
70         final float aspectRatioDiff = mAspectRatio - viewAspectRatio;
71         if (Math.abs(aspectRatioDiff) <= ASPECT_RATIO_CHANGE_THREASHOLD) {
72             // Close enough, skip.
73             return;
74         }
75 
76         width = (int) (height * mAspectRatio);
77 
78         super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
79                 MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
80     }
81 
82 }
83