Home | History | Annotate | Download | only in testingcamera2
      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 com.android.testingcamera2;
     18 
     19 import android.content.Context;
     20 import android.content.res.TypedArray;
     21 import android.util.AttributeSet;
     22 import android.util.Log;
     23 import android.view.SurfaceView;
     24 import android.view.View;
     25 import android.view.ViewGroup.LayoutParams;
     26 
     27 /**
     28  * A SurfaceView that maintains its aspect ratio to be a desired target value.
     29  *
     30  * <p>Depending on the layout, the FixedAspectSurfaceView may not be able to maintain the
     31  * requested aspect ratio. This can happen if both the width and the height are exactly
     32  * determined by the layout.  To avoid this, ensure that either the height or the width is
     33  * adjustable by the view; for example, by setting the layout parameters to be WRAP_CONTENT for
     34  * the dimension that is best adjusted to maintain the aspect ratio.</p>
     35  */
     36 public class FixedAspectSurfaceView extends SurfaceView {
     37 
     38     private static final String TAG = "FixedAspectSurfaceView";
     39 
     40     /**
     41      * Desired width/height ratio
     42      */
     43     private float mAspectRatio;
     44 
     45     public FixedAspectSurfaceView(Context context, AttributeSet attrs) {
     46         super(context, attrs);
     47 
     48         // Get initial aspect ratio from custom attributes
     49         TypedArray a =
     50                 context.getTheme().obtainStyledAttributes(attrs,
     51                         R.styleable.FixedAspectSurfaceView, 0, 0);
     52         setAspectRatio(a.getFloat(
     53                 R.styleable.FixedAspectSurfaceView_aspectRatio, 1.f));
     54         a.recycle();
     55     }
     56 
     57     /**
     58      * Set the desired aspect ratio for this view.
     59      *
     60      * @param aspect the desired width/height ratio in the current UI orientation. Must be a
     61      *               positive value.
     62      */
     63     public void setAspectRatio(float aspect) {
     64         if (aspect <= 0) {
     65             throw new IllegalArgumentException("Aspect ratio must be positive");
     66         }
     67         mAspectRatio = aspect;
     68         requestLayout();
     69     }
     70 
     71     @Override
     72     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
     73 
     74         int widthMode = MeasureSpec.getMode(widthMeasureSpec);
     75         int heightMode = MeasureSpec.getMode(heightMeasureSpec);
     76         int width = MeasureSpec.getSize(widthMeasureSpec);
     77         int height = MeasureSpec.getSize(heightMeasureSpec);
     78 
     79         // General goal: Adjust dimensions to maintain the requested aspect ratio as much
     80         // as possible. Depending on the measure specs handed down, this may not be possible
     81 
     82         // Only set one of these to true
     83         boolean scaleWidth = false;
     84         boolean scaleHeight = false;
     85 
     86         // Sort out which dimension to scale, if either can be. There are 9 combinations of
     87         // possible measure specs; a few cases below handle multiple combinations
     88         if (widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY) {
     89             // Can't adjust sizes at all, do nothing
     90         } else if (widthMode == MeasureSpec.EXACTLY) {
     91             // Width is fixed, heightMode either AT_MOST or UNSPECIFIED, so adjust height
     92             scaleHeight = true;
     93         } else if (heightMode == MeasureSpec.EXACTLY) {
     94             // Height is fixed, widthMode either AT_MOST or UNSPECIFIED, so adjust width
     95             scaleWidth = true;
     96         } else if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
     97             // Need to fit into box <= [width, height] in size.
     98             // Maximize the View's area while maintaining aspect ratio
     99             // This means keeping one dimension as large as possible and shrinking the other
    100             float boxAspectRatio = width / (float) height;
    101             if (boxAspectRatio > mAspectRatio) {
    102                 // Box is wider than requested aspect; pillarbox
    103                 scaleWidth = true;
    104             } else {
    105                 // Box is narrower than requested aspect; letterbox
    106                 scaleHeight = true;
    107             }
    108         } else if (widthMode == MeasureSpec.AT_MOST) {
    109             // Maximize width, heightSpec is UNSPECIFIED
    110             scaleHeight = true;
    111         } else if (heightMode == MeasureSpec.AT_MOST) {
    112             // Maximize height, widthSpec is UNSPECIFIED
    113             scaleWidth = true;
    114         } else {
    115             // Both MeasureSpecs are UNSPECIFIED. This is probably a pathological layout,
    116             // with width == height == 0
    117             // but arbitrarily scale height anyway
    118             scaleHeight = true;
    119         }
    120 
    121         // Do the scaling
    122         if (scaleWidth) {
    123             width = (int) (height * mAspectRatio);
    124         } else if (scaleHeight) {
    125             height = (int) (width / mAspectRatio);
    126         }
    127 
    128         // Override width/height if needed for EXACTLY and AT_MOST specs
    129         width = View.resolveSizeAndState(width, widthMeasureSpec, 0);
    130         height = View.resolveSizeAndState(height, heightMeasureSpec, 0);
    131 
    132         // Finally set the calculated dimensions
    133         setMeasuredDimension(width, height);
    134     }
    135 }
    136