I'm trying to develop my own camera activity, but I have a problem that I'm not unable to solve...
What I want, is something very similiar to instagram photo frame, and this is what I get:
When I should get something like this:
and...
when I should get something like:
I think I'm maanaging the SurfaceView and Camera preview well, only using
Camera.Parameters parameters = camera.getParameters();
camera.setDisplayOrientation(90);
and Custom SurfaceView:
public class SquaredSurfaceView extends SurfaceView {
private int width;
private int height;
public SquaredSurfaceView(Context context) {
super(context);
}
public SquaredSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SquaredSurfaceView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
width = MeasureSpec.getSize(widthMeasureSpec);
height = width;
setMeasuredDimension(width, width);
}
public int getViewWidth() {
return width;
}
public int getViewHeight() {
return height;
}
}
What I'm doing wrong?? :-(
As said before you need to find the correct preview size (the one with aspect ratio 1:1) and probably you have to use FrameLayout for the SurfacePreview. It seems that you have and aspect ratio problem maybe you have the right preview size but you are placing it in an incorrect layout.
Another solution might be (just like Instagram does) to make your camera at full size and then hide some areas of the layout just to make it look like a square. Then by software you would have to cut the image to make it a real square.
Hope this helps you
The solution that works for me is the 2nd Answer, but because we need to rotate the camera 90º is necessary to switch the WIDTH with the HEIGTH, something like this...
camera.setDisplayOrientation(90);
Camera.Parameters params= camera.getParameters();
surfaceView.getLayoutParams().width=params.getPreviewSize().height;
surfaceView.getLayoutParams().height=params.getPreviewSize().width;
Hope this solution helps!! :D
my solution is more like building a square mask and then to place it over the preview surface.
You will need 3 things mainly, first a square frame component. I've made a custom component:
package com.example.squaredviewer;
import android.content.Context;
import android.util.AttributeSet;
import android.view.Display;
import android.view.WindowManager;
import android.widget.RelativeLayout;
/**
* Created by yadirhb on 14-08-2015.
*/
public class SquaredFrame extends RelativeLayout{
public SquaredFrame(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int size = Math.min(getMeasuredWidth(), getMeasuredHeight());
setMeasuredDimension(size, size);
}
}
Depending of the Android API version for the which you're developing, you will maybe need to add another constructor overload. For Kitkat this is just fine.
The second step is to build the layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:visibility="visible">
<RelativeLayout
android:id="@+id/camera_preview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="false"
android:layout_alignParentTop="false"
android:layout_centerInParent="true"
android:background="#ffffff">
</RelativeLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#131008">
</LinearLayout>
<com.example.squaredviewer.SquaredFrame
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"></com.example.squaredviewer.SquaredFrame>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#131008" />
</LinearLayout>
</RelativeLayout>
Notice that the RelativeLayout "camera_preview" is the one used to render the preview, it is centered and has a LinearLayout which contains the squared component. This is actually the "mask" and it covers the camera preview.
Notice also that except the SquaredFrame, which is transparent, the others two are background coloured with black.
Now the surface view, for the camera preview in which the surface is sized acording the aspect ratio.
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private final String TAG = "PIC-FRAME";
private SurfaceHolder mHolder;
private Camera mCamera;
private Display display;
public CameraPreview(Activity context, Camera camera) {
super(context);
mCamera = camera;
display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = getHolder();
mHolder.addCallback(this);
// deprecated setting, but required on Android versions prior to 3.0
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
setKeepScreenOn(true);
}
public void surfaceDestroyed(SurfaceHolder holder) {
// empty. Take care of releasing the Camera preview in your activity.
this.getHolder().removeCallback(this);
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// If your preview can change or rotate, take care of those events here.
// Make sure to stop the preview before resizing or reformatting it.
if (mHolder.getSurface() == null) {
// preview surface does not exist
return;
}
try {
// stop preview before making changes
mCamera.stopPreview();
// set preview size and make any resize, rotate or
// reformatting changes here
// Now that the size is known, set up the camera parameters and begin
// the preview.
Camera.Parameters parameters = mCamera.getParameters();
// You need to choose the most appropriate previewSize for your app
Camera.Size previewSize = parametes.getSupportedPreviewSizes().get(0);
parameters.setPreviewSize(previewSize.width, previewSize.height);
// start preview with new settings
mCamera.setParameters(parameters);
// Set the holder size based on the aspect ratio
int size = Math.min(display.getWidth(), display.getHeight());
double ratio = (double) previewSize.width / previewSize.height;
mHolder.setFixedSize((int)(size * ratio), size);
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (Exception e) {
Log.d(TAG, "Error starting camera preview: " + e.getMessage());
}
}
}
}
Now everything must be tied in the activity class
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_picture_taker);
mDecorView = getWindow().getDecorView();
//mCamera = a camera instance;
// Create our Preview view and set it as the content of our activity.
mPreview = new CameraPreview(this, mCamera);
//Layout where camera preview is shown.
RelativeLayout preview = (RelativeLayout) findViewById(R.id.camera_preview);
//FrameLayout stack controllers inside and superpose them.
preview.addView(mPreview, 0);
// TODO
}
A little long, but I hope it be helpful for more than one. :-)
I had the same problem with a square view - resolved it simply by setting the SurfaceView's camera size to the size of the view where I wanted to draw it. No complicated calculations and it works for me. See my response here for the whole method: https://stackoverflow.com/a/39430615/5181489
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
...
params.setPreviewSize(viewParams.height, viewParams.width);
camera.setParameters(params);
You can use this to get pictureSize:
public static void initialCameraPictureSize(Context context, android.hardware.Camera.Parameters parameters) {
List list = parameters.getSupportedPictureSizes();
if(list != null) {
android.hardware.Camera.Size size = null;
Iterator iterator = list.iterator();
do {
if(!iterator.hasNext())
break;
android.hardware.Camera.Size size1 = (android.hardware.Camera.Size)iterator.next();
if(Math.abs(3F * ((float)size1.width / 4F) - (float)size1.height) < 0.1F * (float)size1.width && (size == null || size1.height > size.height && size1.width < 3000))
size = size1;
} while(true);
if(size != null)
parameters.setPictureSize(size.width, size.height);
else
Log.e("CameraSettings", "No supported picture size found");
}
}
It's so hard. Not easily.
First thing : You define 2 black Linear Layout
to get the UI as you posted.
Second thing : you need cut the picture from full picture to square picture.
How to cut, you need use scaleBitmap
method.
Or you want real Custom Camera? Can check in here