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.