There is a develop guide about Camera
in the Android Developer. However, if you follow the guide and build an app, you’ll find that the ratio of the image displayed on the screen is wrong. I’ll try to provide a solution about this problem in this article.
Before we start, maybe you’ll find that there is another example code about Camera
here . After give it a try, you’ll get a correct result. Then, what is the difference between these two codes?
In the codes from ApiDemo, there are three methods onMeasure(...)
, onLayout(...)
and getOptimalPreviewSize(...)
in the class Preview
. From the Android Reference , the method onMeasure(...)
is used to determine the size requirements for this view and all of its children. The method onLayout(...)
has to be called when this view should assign a size and position to all of its children. Therefore, the onMeasure(...)
method will be called earier than the onLayout(...)
method.
We’ve already been familiar with these two method so far. The next step is get a reasonable size of SurfaceView
and modify the size to what we need. Just look the codes below:
1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// We purposely disregard child measurements because act as a
// wrapper to a SurfaceView that centers the camera preview instead
// of stretching it.
final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
setMeasuredDimension(width, height);
if (mSupportedPreviewSizes != null) {
mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
}
}
and
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (changed && getChildCount() > 0) {
final View child = getChildAt(0);
final int width = r - l;
final int height = b - t;
int previewWidth = width;
int previewHeight = height;
if (mPreviewSize != null) {
previewWidth = mPreviewSize.width;
previewHeight = mPreviewSize.height;
}
// Center the child SurfaceView within the parent.
if (width previewHeight > height previewWidth) {
final int scaledChildWidth = previewWidth height / previewHeight;
child.layout((width - scaledChildWidth) / 2, 0,
(width + scaledChildWidth) / 2, height);
} else {
final int scaledChildHeight = previewHeight width / previewWidth;
child.layout(0, (height - scaledChildHeight) / 2,
width, (height + scaledChildHeight) / 2);
}
}
}
Last, in order to use CameraPreview
in the xml
files, we need to add the other constructors:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public CameraPreview(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public CameraPreview(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
public CameraPreview(Context context) {
super(context);
init(context);
}
public void init(Context context) {
mSurfaceView = new SurfaceView(context);
addView(mSurfaceView);
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = mSurfaceView.getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
Here is the whole codes。 You’ll find that the real size of CameraPreview
is a little different from the size of screen. That’s the reason why we will get wrong ratio if we just use the match_parent
parameter.