概述相机几乎是每个APP都要用到的功能,万一老板让你定制相机方不方?反正我是有点方。关于相机的两天奋斗总结免费送给你。 启动相机的两种方式1.直接启动系统相机 Intent intent = new Intent()
或者指定返回图片的名称mCurrentPhotoFile Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE)
2.自定义启动相机 今天以第二种为例。效果图如下 
自定义相机的一般步骤- 创建显示相机画面的布局,Android已经为我们选定好SurfaceView
- 通过SurfaceView#getHolder()获得链接Camera和SurfaceView的SurfaceHolder
- Camame.open()打开相机
- 通过SurfaceHolder链接Camera和SurfaceView
一般步骤的代码演示public class CameraSurfaceView extends SurfaceView implements SurfaceHolder.Callback, Camera.AutoFocusCallback {
private static final String TAG = "CameraSurfaceView";
private Context mContext;
private SurfaceHolder holder;
private Camera mCamera;
private int mScreenWidth;
private int mScreenHeight;
public CameraSurfaceView(Context context) {
this(context, null);
}
public CameraSurfaceView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CameraSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
getScreenMetrix(context);
initView();
}
private void getScreenMetrix(Context context) {
WindowManager WM = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics outMetrics = new DisplayMetrics();
WM.getDefaultDisplay().getMetrics(outMetrics);
mScreenWidth = outMetrics.widthPixels;
mScreenHeight = outMetrics.heightPixels;
}
private void initView() {
holder = getHolder();
添加相机和自动聚焦限权 <uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera.autofocus" />
将CameraSurfaceView放在布局文件中,这里建议最外层为FrameLayout,后面会用到。如此,我们便有了一个没有照相功能的相机。初次之外,仔细观察相机显示画面,图片是不是变形严重?那是因为我们还没有为相机设置各种参数。在预览前要设置摄像头的分辨率、预览分辨率和图片分辨率的宽高比保持一致。这样图片才不会变形。这是个比较难以理解的部分,想深刻理解还需读者自己动手去实践。 private void setCameraParams(Camera camera, int width, int height) {
Log.i(TAG,"setCameraParams width="+width+" height="+height);
Camera.Parameters parameters = mCamera.getParameters();
*/
private Camera.Size getProperSize(List pictureSizeList, float screenRatio) {
Log.i(TAG, "screenRatio=" + screenRatio);
Camera.Size result = null;
for (Camera.Size size : pictureSizeList) {
float currentRatio = ((float) size.width) / size.height;
if (currentRatio - screenRatio == 0) {
result = size;
break;
}
}
if (null == result) {
for (Camera.Size size : pictureSizeList) {
float curRatio = ((float) size.width) / size.height;
if (curRatio == 4f / 3) {
进去的是屏幕宽高,出来的是调整好了的参数。在surfaceChanged方法中执行mCamera.startPreview(); 前调用setCameraParams(mCamera, mScreenWidth, mScreenHeight); 就可以了。最后要在AndroidManifest.xml里设置activity的方向android:screenOrientation="portrait" 代码里有很多注释,其中也有我自己调试时候的Log,大家可以自己调试下,看看不同参数的效果。昨天调参数搞到一点多,都在折腾这个函数。唉,一把辛酸泪。 身为一个相机,居然不能照相?真是太丢脸了!下面给我们的相机添加上照相的功能。照相核心代码就一句:mCamera.takePicture(null, null, jpeg); 可以看到takePicture方法有三个参数,分别是ShutterCallback、PictureCallback和PictureCallback。这里我们只用了PictureCallback
在jpeg的onPictureTaken里。我们将存储照片信息的byte[] data解析成bitmap,然后转换成JPG格式的图片保存在SD卡中。注意finally中最后两句mCamera.stopPreview();// 关闭预览 mCamera.startPreview();// 开启预览 上文也提到:当调用camera.takePiture方法后,camera关闭了预览,这时需要调用startPreview()来重新开启预览。如果不再次开启预览,则会一直停留在拍摄照片画面。为了方便外部调用拍照。这里我暴露了一个方法供外部拍照。 public void takePicture(){
在布局文件中添加一个Button,点击Button执行takePicture()方法。不要忘了添加写SD卡限权 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
至此,一个具有照相并保存拍摄图片功能的相机就做出来了。But,我们就此满足了吗?要是为了这些简单的功能我也不会写这篇博客。这只是个开始 真正的开始昨天看见别的APP在照相的时候,屏幕上居然可以显示像效果图那样的框框啦、辅助点啦、图片bulabulabula~。在网上搜索一番实现方式,再加上一些自己的理解,构成了这篇博客。 上文布局文件一直没有贴,现在贴出来大家先扫一眼,有些控件会在接下来展示
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.dyk.cameratest.view.CameraSurfaceView
android:id="@+id/cameraSurfaceView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.dyk.cameratest.view.RectOnCamera
android:layout_width="match_parent"
android:layout_height="match_parent" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="20dp"
android:id="@+id/takePic"
android:layout_width="80dp"
android:layout_height="50dp"
android:background="#88427ac7"
android:text="拍照"
android:textColor="#aaa" />
RelativeLayout>
FrameLayout>
布局文件的最外层是个FrameLayout,我们知道FrameLayout是自带覆盖效果的。由来这个思路接下来就很简单了。编程重要的是思想,思想有了,其余的就剩具体的实现细节。 自定义边边框框为了和CameraSurfaceView区分开,再自定义一个RectOnCamera专门用来画边边框框这些东西。这样做还一个好处是方便维护,不至于将所有东西都放在一个View中。 RectOnCamerapackage com.dyk.cameratest.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
/**
* Created by dyk on 2016/4/7.
*/
public class RectOnCamera extends View {
private static final String TAG = "CameraSurfaceView";
private int mScreenWidth;
private int mScreenHeight;
private Paint mPaint;
private RectF mRectF;
这里简单的画了一个类似二维码扫描的框框,还有一个类似聚焦的内外圆。那么问题来了,聚焦的内外圆要随着手指滑而改变位置,而且要有聚焦的效果。可又和具有聚焦功能的CameraSurfaceView不是同一个类,不仅如此聚焦内外圆还完全覆盖了CameraSurfaceView。要处理这种问题,需要接口回调。这就是思想下面的细节。现在虽然确定接口回调,但还有一个问题,CameraSurfaceView类和RectOnCamera类中都没有对方的对象或者引用。没错,通过共同持有RectOnCamera和CameraSurfaceView的Activity可以实现此功能。下面是具体的实现方法。 动起来首先,想要随着手指的滑动而改变RectOnCamera的位置肯定是要复写onTouchEvent()方法 @Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_UP:
int x = (int) event.getX();
int y = (int) event.getY();
centerPoint = new Point(x, y);
invalidate();
return true;
}
return true;
}
其次,定义回调接口 private IAutoFocus mIAutoFocus;
/** 聚焦的回调接口 */
public interface IAutoFocus{
void autoFocus();
}
public void setIAutoFocus(IAutoFocus mIAutoFocus) {
this.mIAutoFocus = mIAutoFocus;
}
在onTouchEvent()中return前加入 if (mIAutoFocus != null){
mIAutoFocus.autoFocus();
}
至此我们的回调接口已经定义好了,此时还需要CameraSurfaceView暴露一个聚焦方法,以便Activity调用 public void setAutoFocus(){
mCamera.autoFocus(this);
}
准备工作已经全部完成,下面请看Activity的具体实现:
|