Android SurfaceView Preview Deformation Perfect Solution

  • 2021-08-31 09:11:33
  • OfStack

This question is searched by Baidu, which basically means to find the preview size of camera which is similar to the proportion of SurfaceView, but it is found that the preview is still a little mean. Look at the following callback to know why.


  @Override
  public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    Log.i(TAG, "surfaceChanged: " + width + " " + height);
  }

From the data printed by the callback above, we know that taking a similar proportion can't solve the fundamental problem.

Therefore, for this kind of solution, I just want to say that only close is useful.

So since we know that the width and height of surfaceChanged is the rendering width and height of SurfaceView, we can find a way to make the aspect ratio of surfaceChanged and the ratio of camera 1, so look at the source code of SurfaceView:


protected void updateWindow(boolean force, boolean redrawNeeded) {
    ... Code omission 

    int myWidth = mRequestedWidth;
    if (myWidth <= 0) myWidth = getWidth();
    int myHeight = mRequestedHeight;
    if (myHeight <= 0) myHeight = getHeight();

    ... Code omission 
      
    if (creating || formatChanged || sizeChanged
        || visibleChanged || realSizeChanged) {
      if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
          + "surfaceChanged -- format=" + mFormat
          + " w=" + myWidth + " h=" + myHeight);
      if (callbacks == null) {
        callbacks = getSurfaceCallbacks();
      }
      for (SurfaceHolder.Callback c : callbacks) {
        c.surfaceChanged(mSurfaceHolder, mFormat, myWidth, myHeight);
      }
    }
    
    ... Code omission 
  }

It can be seen that the width and height are actually getHeight and getWidth of the calling View or mRequestedWidth and mRequestedHeight.

Familiar with the customization of View, we know that getHeight and getWidth are closely related to onMeasure of View, so we think of rewriting onMeasure method.

Then see the assignment of mRequestedWidth and mRequestedHeight from the source code


@Override
    public void setFixedSize(int width, int height) {
      if (mRequestedWidth != width || mRequestedHeight != height) {
        mRequestedWidth = width;
        mRequestedHeight = height;
        requestLayout();
      }
    }

Here is the complete class code:


public class ResizeAbleSurfaceView extends SurfaceView {

  private int mWidth = -1;
  private int mHeight = -1;

  public ResizeAbleSurfaceView(Context context) {
    super(context);
  }

  public ResizeAbleSurfaceView(Context context, AttributeSet attrs) {
    super(context, attrs);
  }

  public ResizeAbleSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
  }

  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    if (-1 == mWidth || -1 == mHeight) {
      super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
    else {
      setMeasuredDimension(mWidth, mHeight);
    }
  }

  public void resize(int width, int height) {
    mWidth = width;
    mHeight = height;
    getHolder().setFixedSize(width, height);
    requestLayout();
    invalidate(); 
  }
}

Just remember to call the resize method when instantiating.

Note that the preview size ratio with camera is 1, and remember to pass the width and height correctly, otherwise it may not be full screen


Related articles: