在我的应用程序中,我想在布局顶部的FrameLayout
中显示相机预览。
这就是我想要实现的目标:
+---------------+
| Camera Prev. |
| |
+---------------+
| |
| |
+---------------+
| |
| |
+---------------+
屏幕分为3个区域,在最顶部的区域我想看到相机预览。 预览应具有正确的宽高比,因此不会拉伸任何内容。至少应该固定高度。宽度不必与整个屏幕尺寸宽度相匹配。
这是我的Layout.xml:
<!--CAMERA PREVIEW-->
<FrameLayout
android:id="@+id/camera_preview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1"
android:layout_marginBottom="5dp"/>
<!--ANTOTHER SURFACE VIEW-->
<FrameLayout
android:id="@+id/control_layer"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#FF000000"
android:layout_weight="1"
android:layout_marginBottom="5dp"/>
<!--CONTROLS AND BUTTONS-->
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="fill_parent"
android:layout_gravity="center_horizontal"
android:layout_weight="1">
<Button
android:id="@+id/buttonGraphGLStart"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#FF00FF00"
android:layout_centerInParent="true"
android:layout_alignParentTop="true"
android:text="Start"/>
</LinearLayout>
这种布局有效,我看到三个高度相等的区域。现在我要在第一个区域显示相机预览。以下是我的代码:
public class GraphGLFragment extends Fragment {
private Camera mCamera;
private CameraPreview mPreview;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View view = inflater.inflate(R.layout.fragment_graphgl, null);
mCamera = getCameraInstance();
mPreview = new CameraPreview(this.getActivity(), mCamera);
FrameLayout frameLayout = (FrameLayout) view.findViewById(R.id.camera_preview);
frameLayout.addView(mPreview);
return view;
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.graph_video_menu, menu);
super.onCreateOptionsMenu(menu, inflater);
}
/**
* A safe way to get an instance of the Camera object.
*/
public static Camera getCameraInstance() {
Camera c = null;
try {
c = Camera.open();
} catch (Exception e) {
// Camera is not available (in use or does not exist)
}
return c; // returns null if camera is unavailable
}
@Override
public void onResume() {
super.onResume();
}
@Override
public void onPause() {
super.onPause();
}
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private static final String TAG = "CameraPreview";
private Context mContext;
private SurfaceHolder mHolder;
private Camera mCamera;
private List<Camera.Size> mSupportedPreviewSizes;
private Camera.Size mPreviewSize;
public CameraPreview(Context context, Camera camera) {
super(context);
mContext = context;
mCamera = camera;
// supported preview sizes
mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
for(Camera.Size str: mSupportedPreviewSizes)
Log.e(TAG, str.width + "/" + str.height);
// 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);
}
public void surfaceCreated(SurfaceHolder holder) {
// empty. surfaceChanged will take care of stuff
}
public void surfaceDestroyed(SurfaceHolder holder) {
// empty. Take care of releasing the Camera preview in your activity.
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
Log.e(TAG, "surfaceChanged => w=" + w + ", h=" + 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;
}
// stop preview before making changes
try {
mCamera.stopPreview();
} catch (Exception e){
// ignore: tried to stop a non-existent preview
}
// set preview size and make any resize, rotate or reformatting changes here
// start preview with new settings
try {
Camera.Parameters parameters = mCamera.getParameters();
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
mCamera.setParameters(parameters);
mCamera.setDisplayOrientation(90);
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (Exception e){
Log.d(TAG, "Error starting camera preview: " + e.getMessage());
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
if (mSupportedPreviewSizes != null) {
mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
}
float ratio;
if(mPreviewSize.height >= mPreviewSize.width)
ratio = (float) mPreviewSize.height / (float) mPreviewSize.width;
else
ratio = (float) mPreviewSize.width / (float) mPreviewSize.height;
// One of these methods should be used, second method squishes preview slightly
setMeasuredDimension(width, (int) (width * ratio));
// setMeasuredDimension((int) (width * ratio), height);
}
private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {
final double ASPECT_TOLERANCE = 0.1;
double targetRatio = (double) h / w;
if (sizes == null)
return null;
Camera.Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = h;
for (Camera.Size size : sizes) {
double ratio = (double) size.height / size.width;
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)
continue;
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
if (optimalSize == null) {
minDiff = Double.MAX_VALUE;
for (Camera.Size size : sizes) {
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
}
Log.d(TAG,optimalSize.width + " "+ optimalSize.height + "-----");
return optimalSize;
}
}
}
没有错误,宽高比也正确(没有拉伸图像),但预览太大。我没有看到整个预览。我想根据主机camer_preview FrameLayout
的可用大小,无法正确计算预览大小
有谁知道这里的问题是什么?
在surfaceCHanged()
中,表面的w = 1536,h = 2730,这太大了......
答案 0 :(得分:0)
您的设备处于纵向模式,相应地旋转预览。要保持纵横比,您必须使表面高于宽
----------
| |
| |
| |
| |
| |
| |
| |
----------
达到结果:
----------
| |
| |
|XXXXXXXX|
|XXXXXXXX|
|XXXXXXXX|
|XXXXXXXX|
|XXXXXXXX|
----------
你应该裁剪图片。最简单的方法是在表面顶部显示UI的其他部分。或者,您可以使用SurfaceTexture并操纵OpenGL变换来裁剪图像。
几天前,我回答了类似的问题:How to get normal Camera view in Surfaceview android?。
答案 1 :(得分:0)
如果要显示完整预览(即没有裁剪),但可以牺牲预览曲面的宽度(以保持纵横比),请更改onMeasure()
中的代码:
setMeasuredDimension((int)(height/ratio), height);
setLayoutParams(new Frameayout.LayoutParams((int)(height/ratio), height, Gravity.CENTER));
这是预期的布局:
----------
|XX XX|
|XX XX|
|XX XX|
|XXXXXXXX|
|XXXXXXXX|
|XXXXXXXX|
|XXXXXXXX|
----------