Skip to content

Most visited

Recently visited

navigation
HdrViewfinder / src / com.example.android.hdrviewfinder /

FixedAspectSurfaceView.java

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

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Take a one-minute survey?
Help us improve Android tools and documentation.