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.

Get the latest Android developer news and tips that will help you find success on Google Play.

* Required Fields

Hooray!

Follow Google Developers on WeChat