概述:

有些开发者可能会需要一个自定义的相机用户接口, 以实现自己独特样式的相机和特殊的功能. 创建一个自定义相机activity比调用系统相机需要更多的代码, 但是它可以为用户提供更加丰富的体验.

注意: 这里介绍的是使用老版本的Camera类, 该类已经不推荐使用. 推荐使用的类是android.hardware.camera2.该类在API21中引入. 

为一个APP创造一个自定义相机接口的普通步骤是这样的:

1.      检测和访问相机– 创建代码来检查是否存在相机并申请访问.

2.      创建一个Preview类– 创建一个相机预览类, 该类继承SurfaceView并实现SurfaceHolder接口. 这个类负责预览相机拍摄到的图像.

3.      创建一个Preview用的Layout – 一旦有了相机预览类, 就得创建一个layout来合并预览和给用户提供的操作界面.

4.      为拍照功能设置监听– 为我们的启动拍照或者视频的接口控制连接监听器来响应用户的操作, 比如点击按钮拍照.

5.      抓取和保存文件– 写代码实现抓取图片或者视频, 并保存输出.

6.      释放camera – 用完之后, APP必须适时释放以供其它APP使用.

相机硬件是一种共享资源, 它必须被小心的使用这样就不会让我们的APP与其它需要使用它的APP发生冲突. 下面的小节将会讨论如何检测相机硬件, 如何请求访问相机, 如何抓取图片或者视频和用完之后如何释放相机资源.

注意, 请一定记得在使用完毕后及时释放相机资源. 否则所有后续尝试访问相机的APP包括我们自己的APP都无法再访问相机资源, 并很可能引起APP闪退.

检测相机硬件:

如果我们的APP没有在manifest中声明需要使用相机资源, 我们应该在运行时检测相机资源是否可用. 想要实现这个操作, 需要使用PackageManager.hasSystemFeature()方法, 栗子:

/** Check if this device has a camera */
private boolean checkCameraHardware(Context context){
    if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){
        // this device has a camera
        return true;
    } else {
        // no camera on this device
        return false;
    }
}

Android设备可以拥有多个相机, 比如一个用于拍照的主摄像头和一个用于视频通话或者自拍的前置摄像头. Android 2.3及更高版本允许我们使用Camera.getNumberOfCameras()方法检测可用相机的数量.

访问相机:

如果在APP运行的设备上确定有一个相机, 那么可以使用Camera的实例来请求访问它(或者使用intent访问). 想要在底层访问相机, 需要使用Camera.open()方法并确认捕获任何异常, 栗子:

/** A safe way to get an instance of the Camera object. */
public static Camera getCameraInstance(){Camera c = null;try {c = Camera.open(); // attempt to get a Camera instance}catch (Exception e){// Camera is not available (in use or does not exist)}return c; // returns null if camera is unavailable
}

要注意在使用Camera.open()方法访问相机的时候必须检查异常. 如果相机不存在或者检查失败的话会导致APP挂掉.

在Android 2.3及更高版本中, 我们可以使用Camera.open(int)访问指定的相机. 上面的栗子将会访问第一个, 就是后置摄像机.

检查相机功能:

一旦取得了对相机的访问, 我们就可以通过Camera.getParameters()方法获得更多的相机功能的信息, 该方法会返回一个Camera.Parameters对象, 可以通过它查看相机功能. 当使用API 9 及更高版本时, 使用Camera.getCameraInfo(0方法来决定相机是在设备的前面还是后面, 以及画面的方向.

创建一个预览类:

对于用户来说, 如果他们想拍出靠谱的图片或者视频, 他们必须能看到设备相机所看到的内容. 这时候就需要一个预览的窗口让用户可以看到相机拍到的东西, 相机预览类是SurfaceView, 它可以显示从相机直播的图像数据.

下面的栗子展示了如何创建一个基本的相机预览类, 并可以放在View layout中. 这个类实现了SurfaceHolder.Callback接口, 它可以用来捕捉view创建和回收的事件.

/** A basic Camera preview class */
public classCameraPreview extendsSurfaceView implementsSurfaceHolder.Callback{
    private SurfaceHolder mHolder;
    private Camera mCamera;

public CameraPreview(Context context,Camera camera){
        super(context);
        mCamera = camera;

// 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){
        // The Surface has been created, now tell the camera where to draw thepreview.
        try {
            mCamera.setPreviewDisplay(holder);
            mCamera.startPreview();
        } catch(IOException e){
            Log.d(TAG,"Errorsetting camera preview: " + e.getMessage());
        }
    }

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) {
        // 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 {
            mCamera.setPreviewDisplay(mHolder);
            mCamera.startPreview();

} catch(Exception e){
            Log.d(TAG,"Errorstarting camera preview: " + e.getMessage());
        }
    }
}

如果想要为相机预览设置一个指定的大小, 则需要在surfaceChanged()中设置. 当设置预览窗口的大小的时候, 必须使用从getSupportedPreviewSizes()提供的值. 不要在setPreviewSize()方法中设置随意的值.

在layout中放置preview:

相机预览类(比如前一节的栗子)必须被放置在layout中并跟它的用户控制接口(比如拍照或者摄像键)放在一起. 本的小节将会展示如何创建一个基础的layout和activity用来预览. 下面的layout代码提供了一个非常基础的view, 它可以用来显示一个相机的预览. 在这个栗子中, FrameLayout标签用于作为相机预览类的容器. 使用这种layout是为了让更多的图片信息或者控件可以覆盖在相机的预览图像上.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
  <FrameLayout
    android:id="@+id/camera_preview"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_weight="1"
    />

<Button
    android:id="@+id/button_capture"
    android:text="Capture"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    />
</LinearLayout>

在大多数设备上, 默认的相机方向是横向的. 这个栗子中的layout指定了一个横向的layout, 并且使用下面的代码修正APP的屏幕方向为横向. 为简单起见, 可以在manifest中通过这段代码修改APP预览activity的方向到横屏:

<activity android:name=".CameraActivity"
          android:label="@string/app_name"

android:screenOrientation="landscape">
          <!-- configure this activity to use landscapeorientation -->

<intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.LAUNCHER"/>
    </intent-filter>
</activity>

一个相机预览窗口并不一定必须是横屏模式, 从Android2.2开始, 我们可以使用setDisplayOrientation()方法来设置预览图像的方向. 为了在用户旋转屏幕的时候改变预览方向, 在surfaceChanged()方法中, 首先通过Camera.stopPreview()停止预览, 然后改变方向, 之后再用Camera.startPreview()方法重新启动预览.

在相机预览的activity中, 向FrameLayout中添加预览类. 相机activity必须确保相机暂停或者关闭的时候释放相机资源. 下面的代码展示了如何修改相机activity来关联预览类:

public class CameraActivity extends Activity {

private Camera mCamera;
    private CameraPreview mPreview;

@Override
    public void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

// Create an instance of Camera
        mCamera = getCameraInstance();

// Create our Preview view and set it as the content of our activity.
        mPreview = new CameraPreview(this, mCamera);
        FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
        preview.addView(mPreview);
    }
}

getCameraInstance()方法是在”访问相机”小节实现的.

捕捉图片(拍照..):

一旦预览类创建完毕, 并且已经可以在一个layout中显示, 就表示已经为捕捉图片做好了准备. 在APP代码中我们必须为用户接口设置监听器以相应用户的操作. 想要获取一个图片, 要使用Camera.takePicture()方法. 该方法接收三个参数用来获取相机传回的数据. 为了接收接收JPEG格式的数据, 我们必须实现一个Camera.PictureCallback接口来接收图片数据, 并将它写入一个文件中. 下面的代码展示了一个基础的Camera.PictureCallback接口的实现, 用于保存一个从相机接收的图像:

private PictureCallback mPicture = new PictureCallback(){

@Override
    public void onPictureTaken(byte[] data,Camera camera){

File pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE);
        if (pictureFile== null){
            Log.d(TAG,"Errorcreating media file, check storage permissions: "+
                e.getMessage());
            return;
        }

try {
            FileOutputStream fos = new FileOutputStream(pictureFile);
            fos.write(data);
            fos.close();
        } catch(FileNotFoundException e){
            Log.d(TAG,"File notfound: " + e.getMessage());
        } catch(IOException e){
            Log.d(TAG,"Erroraccessing file: " + e.getMessage());
        }
    }
};

通过Camera.takePicture()方法来触发拍照操作. 下面的栗子展示了如何从一个button的View.OnClickListener中调用该方法:

// Add a listener to the Capture button
Button captureButton = (Button) findViewById(id.button_capture);
captureButton.setOnClickListener(
    new View.OnClickListener(){
        @Override
        public void onClick(View v){
            // get an image from the camera
            mCamera.takePicture(null,null, mPicture);
        }
    }
);

mPicture变量在之前的栗子中定义.

注意: 一定要记得在APP用完相机之后释放它. 释放方法是Camera.release().

捕捉视频(摄像…):

使用Android framework捕捉视频需要对Camera对象很仔细的使用和管理, 并且需要跟MediaRecorder类协作使用. 当使用Camera类录制视频的时候, 我们必须管理Camera.lock()和Camera.unlock()方法来允许MediaRecorder访问相机硬件, 除此之外还有Cemra.open()和Camera.release()方法.

从Android4.0开始, Camera.lock()和Camera.unlock()方法已经可以自动管理了.

不同于使用设备拍照, 录像需要一个非常特殊的顺序. 详情如下:

1.      打开相机– 使用Camera.open()方法来获取一个相机对象的实例.

2.      连接预览窗口– 通过Camera.setPreviewDisplay()方法连接一个SurfaceView到相机, 这样就可以预览相机的图像了.

3.      开始预览– 调用Camera.startPreview()方法来启动显示直播的相机图像.

4.      开始录制视频– 想要成功录制视频, 必须完成下面的步骤:

a)        解锁相机– 通过Camera.unlock()方法为MediaRecorder解锁相机.

b)        配置 MediaRecorder – 按照下面指定的顺序调用MediaRecorder中的下列方法.

i.             setCamera – 设置相机用于录像, 使用APP当前的Camera实例.

ii.             setAudioSource() – 设置音频源, 使用MediaRecorder.AudioSource.CAMCORDER.

iii.             setVideoSource() – 设置视频源, 使用MediaRecorder.VideoSource.CAMCORDER.

iv.             设置视频输出的编码格式. 对于Android2.2及更高版本, 使用MediaRecorder.setProfile方法, 并使用CamcorderProfile.get()获取一个profile实例. 对于Android2.2以前的版本, 必须设置视频输出格式和编码参数: setOutputFormat()方法用于设置输出格式, 默认设置是MediaRecorder.OutputFormat.MPEG_4. setAudioEncoder()方法用于设置声音编码类型,默认值是MediaRecorder.AudioEncoder.AMR_NB. setVideoEncoder()方法用于设置视频编码类型,默认值是MediaRecorder.VideoEncoder.MPEG_4_SP.

v.             setOutputFile() – 设置输出文件, 使用”保存媒体文件”小节中的getOutputMediaFile(MEDIA_TYPE_VIDEO).toString()方法.

vi.             setPreviewDisplay() – 为APP指定SurfaceView预览layout. 使用”连接预览窗口”中相同的对象.

注意, 我们必须按照上面的顺序调用这些MediaRecorder配置方法, 否则APP将会出错, 录制也将会失败.

c)        准备MediaRecorder – 通过MediaRecorder.prepare()方法及提供的配置设置项准备MediaRecorder.

d)        启动MediaRecorder – 使用MediaRecorder.start()方法启动录制视频.

5.      停止录制视频– 按顺序调用下面的方法来完成视频录制:

a)        停止MediaRecorder – 使用MediaRecorder.stop()方法停止视频录制.

b)        重置MediaRecorder – 可选, 从recorder中移除配置设置项. 方法是MediaRecorder.reset().

c)        释放MediaRecorder – 使用MediaRecorder.release()方法释放MediaRecorder.

d)        锁定相机– 使用Camera.lock()锁定相机, 这样未来的MediaRecorder会话就可以使用它了. 从Android4.0开始, 该方法不用调用了, 除非MediaRecorder.prepare()方法调用失败.

6.      停止预览– 当activity已经完成使用相机, 使用Camera.stopPreview()来停止预览.

7.      释放相机– 使用Camera.release()方法释放相机, 这样其它的APP才可以再次使用它.

注意: 不创建相机预览而使用MediaRecorder是可行的. 但是用户通常更希望在拍摄之前可以看到预览.

提示: 如果我们的APP是用来录像的, 设置setRecordingHint(Boolean)为true来提前启动预览. 这个设置可以帮助减少开始录制话费的时间.

配置MediaRecorder:

当使用MediaRecorder类来录像的时候, 我们必须按顺序执行配置步骤, 然后调用MediaRecorder.prepare()方法来检查和实现配置. 下面的代码展示了如何合适的配置和准备MediaRecorder类:

private boolean prepareVideoRecorder(){

mCamera = getCameraInstance();
    mMediaRecorder = new MediaRecorder();

// Step 1:Unlock and set camera to MediaRecorder
    mCamera.unlock();
    mMediaRecorder.setCamera(mCamera);

// Step 2: Setsources
    mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
    mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);

// Step 3: Set aCamcorderProfile (requires API Level 8 or higher)
    mMediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH));

// Step 4: Setoutput file
    mMediaRecorder.setOutputFile(getOutputMediaFile(MEDIA_TYPE_VIDEO).toString());

// Step 5: Setthe preview output
    mMediaRecorder.setPreviewDisplay(mPreview.getHolder().getSurface());

// Step 6:Prepare configured MediaRecorder
    try {
        mMediaRecorder.prepare();
    } catch(IllegalStateException e){
        Log.d(TAG,"IllegalStateException preparing MediaRecorder:"+ e.getMessage());
        releaseMediaRecorder();
        return false;
    } catch(IOException e){
        Log.d(TAG,"IOException preparing MediaRecorder: " + e.getMessage());
        releaseMediaRecorder();
        return false;
    }
    return true;
}

Android2.2版本之前, 我们必须直接设置输出格式和编码格式参数, 而不是使用CamcorderProfile. 这种方法是这样的:

// Step3: Set output format and encoding (for versions prior to API Level 8)
    mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
    mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
    mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);

上面的MediaRecorder的录像参数提供了默认的设置项, 但是如果想要自定义的话, 我们可以使用这些方法:

l  setVideoEncodingBitRate()

l  setVideoSize()

l  setVideoFrameRate()

l  setAudioEncodingBitRate()

l  setAudioChannels()

l  setAudioSamplingRate()

启动和停止MediaRecorder:

当使用MediaRecorder类启动和停止视频录制的时候, 我们必须按顺序执行下面的步骤:

1.      使用Camera.unlock()解锁相机.

2.      像上面栗子中那样配置MediaRecorder.

3.      使用MediaRecorder.start()开始录制.

4.      录制视频.

5.      调用MediaRecorder.stop()方法停止录制.

6.      使用MediaRecorder.release()方法释放媒体recorder.

7.      使用Camera.lock()锁定相机.

下面的栗子展示了如何使用一个button来合适的启动和停止视频录制:

注意, 当完成录制之后不要直接释放相机, 否则预览就会被停止.

private boolean isRecording = false;// Add a listener to the Capture button
Button captureButton = (Button) findViewById(id.button_capture);
captureButton.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if (isRecording) {// stop recording and release cameramMediaRecorder.stop();  // stop the recordingreleaseMediaRecorder(); // release the MediaRecorder objectmCamera.lock();         // take camera access back from MediaRecorder// inform the user that recording has stoppedsetCaptureButtonText("Capture");isRecording = false;} else {// initialize video cameraif (prepareVideoRecorder()) {// Camera is available and unlocked, MediaRecorder is prepared,// now you can start recordingmMediaRecorder.start();// inform the user that recording has startedsetCaptureButtonText("Stop");isRecording = true;} else {// prepare didn't work, release the camerareleaseMediaRecorder();// inform user}}}}
);

上面的代码中, prepareVideoRecorder()方法在”配置MediaRecorder”小节中实现. 该方法负责锁定相机, 配置和准备MediaRecorder实例.

释放相机:

相机是一个可以被设备上的APP共享的资源. 我们的APP可以在获取到一个Camera实例之后开始使用相机, 当使用结束之后, 必须记得释放相机对象, 还有在APP暂停的时候(Activity.onPause)也要记得释放. 如果APP没有合适的释放相机, 所有的接下来的访问相机的请求包括我们自己的APP都将会失败, 可能会导致我们的APP闪退.

想要释放一个Camera对象的实例, 需要使用Camera.release()方法. 栗子:

public class CameraActivity extends Activity {private Camera mCamera;private SurfaceView mPreview;private MediaRecorder mMediaRecorder;...@Overrideprotected void onPause() {super.onPause();releaseMediaRecorder();       // if you are using MediaRecorder, release it firstreleaseCamera();              // release the camera immediately on pause event}private void releaseMediaRecorder(){if (mMediaRecorder != null) {mMediaRecorder.reset();   // clear recorder configurationmMediaRecorder.release(); // release the recorder objectmMediaRecorder = null;mCamera.lock();           // lock camera for later use}}private void releaseCamera(){if (mCamera != null){mCamera.release();        // release the camera for other applicationsmCamera = null;}}
}

保存媒体文件:

用户通过拍照或者录像创建的媒体文件应该被保存在设备的外部存储目录中(SD卡), 这样可以节省系统空间让用户可以在设备之外访问这些文件. 有很多种可以存放媒体文件的目录, 然而作为一个开发者, 我们应该只考虑两个标准的路径:

1.      Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)– 该方法返回标准的共享的和官方推荐的路径用来保存图片和视频. 该目录是共享的(public), 所以其他的APP也可以很容易的发现读取修改和删除保存在这里的文件. 如果我们的APP被用户卸载了, 保存在这里的媒体文件不会被移除. 想要避免干涉用户已经存在的图片和视频, 我们应该为APP创建一个子目录来存放自己的媒体文件. 该方法只能在Android2.2及之后的版本中使用. 更早的API版本可以参考这里.

2.      Context.getExternalFilesDir(Envireonment.DIRECTORY_PICTURES)– 该方法返回一个标准的路径用于保存图片和视频, 该路径是与APP关联的. 如果APP被卸载了, 那么任何保存在该路径下的文件都将被卸载. 该路径中的文件并不是加密的, 其它的APP可以读取修改和删除它们.

下面的栗子展示了如何为一个媒体文件创建一个File或者Uri路径:

public static final int MEDIA_TYPE_IMAGE = 1;
public static final int MEDIA_TYPE_VIDEO = 2;/** Create a file Uri for saving an image or video */
private static Uri getOutputMediaFileUri(int type){return Uri.fromFile(getOutputMediaFile(type));
}/** Create a File for saving an image or video */
private static File getOutputMediaFile(int type){// To be safe, you should check that the SDCard is mounted// using Environment.getExternalStorageState() before doing this.File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "MyCameraApp");// This location works best if you want the created images to be shared// between applications and persist after your app has been uninstalled.// Create the storage directory if it does not existif (! mediaStorageDir.exists()){if (! mediaStorageDir.mkdirs()){Log.d("MyCameraApp", "failed to create directory");return null;}}// Create a media file nameString timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());File mediaFile;if (type == MEDIA_TYPE_IMAGE){mediaFile = new File(mediaStorageDir.getPath() + File.separator +"IMG_"+ timeStamp + ".jpg");} else if(type == MEDIA_TYPE_VIDEO) {mediaFile = new File(mediaStorageDir.getPath() + File.separator +"VID_"+ timeStamp + ".mp4");} else {return null;}return mediaFile;
}

注意: Environment.getExternalStoragePublicDirectory()在Android2.2及更高版本中可用, 如果目标设备使用更早的Android版本, 则要使用Environment.getExternalStorageDirectory()方法代替.

总结:

获取相机对象(PackageManager, Camera); 创建预览窗口(SurfaceView,SurfaceHolder); 关联预览窗口和相机对象; 设置拍摄操作的事件监听器; 拍摄保存; 释放;

参考: https://developer.android.com/guide/topics/media/camera.html

Android 相机 II-实现自己的相机APP相关推荐

  1. Android 11适配指南之系统相机拍照、打开相册,安卓app开发教程

    Android 6 权限适配 Android 7 文件适配 Android 10/11 存储适配 ok,接下来以一个更换头像的小例子来讲解一下. 示例 ======================== ...

  2. 安卓用户又少了一项自由,Android 11不再支持更改默认相机程序

    萧萧 发自 凹非寺 量子位 报道 | 公众号 QbitAI 在微博.微信上想要拍照上传时,想直接用FaceU或美图秀秀作为默认拍照程序,给自拍加个滤镜? 在即将推出的Android 11里,这些第三方 ...

  3. Android11还能自定义相机吗,安卓用户又少了一项自由,Android 11不再支持更改默认相机程序...

    原标题:安卓用户又少了一项自由,Android 11不再支持更改默认相机程序 来源:量子位 关注前沿科技 萧萧 发自 凹非寺 量子位 报道 | 公众号 QbitAI 萧萧 发自 凹非寺 量子位 报道 ...

  4. android编程 自动裁剪图片,Android编程实现调用相册、相机及拍照后直接裁剪的方法...

    本文实例讲述了Android编程实现调用相册.相机及拍照后直接裁剪的方法.分享给大家供大家参考,具体如下: package com.cvte.health.phone; import java.io. ...

  5. Android WebRTC 入门教程(一) -- 使用相机

    前言,最近在搞网页投屏,发现 WebRTC 的Android 版本较少,这里的话,参考了一些优秀的博客,主要是这个大佬的 https://www.jianshu.com/p/eb5fd116e6c8 ...

  6. Android 自定义相机 身份证拍照 自定义身份证相机

    项目中需要用到拍摄身份证,拍完照片后直接拿到和身份证比例一致的图片,做成功的结果如下:    拍完照后直接拿到裁剪好的图本文的核心技术来自: https://yq.aliyun.com/article ...

  7. Android 11 强制用户使用系统相机?

    最近在Android开发者官网中查看Android 11版本变更记录的时候,发现一个有意思的改动 此项变更是说在使用Intent创建拍照,录像隐式请求的时候,只有手机预装的系统相机才会响应.举个例子, ...

  8. Android开发学习之以CameraAPI方式实现相机功能(一)——快速实现相机

    今天无意当中发现在<Android开发学习之基于ZBar实现微信扫一扫>中的一部分代码可以用来以硬件方式实现一个照相机的功能,在<Android开发学习之调用系统相机完成拍照的实现& ...

  9. 【Android笔记67】Android之使用系统中的相机功能(拍照、保存照片、显示拍摄的照片、照片保存到图库等操作)

    这篇文章,主要介绍Android如何使用系统中的相机功能(拍照.保存照片.显示拍摄的照片.照片保存到图库等操作). 目录 一.使用Android相机功能 1.1.如何调用相机功能 1.2.调用相机功能

最新文章

  1. 【中文】Joomla1.7扩展介绍之Googlemaps Plugin
  2. Vue 第一天: 计算属性和观察者
  3. Linux服务器的最大内存和CPU数
  4. Linux 信号可靠性,同步,异步,多线程信号等介绍
  5. 多普勒效应、多普勒效应、网关、路由器
  6. 指针和数组替换和区别
  7. 中国计算机学会通讯下载工具(简易爬虫)
  8. 决策树留一法python代码_ScikitLearn决策树算法类库使用小结
  9. 波及1.95亿辆车,黑莓承认系统存在漏洞,大众、宝马纷纷“躺枪”
  10. bzoj 4895: 项链分赃(增强版)
  11. 45 岁以后的程序员都到哪里去了?
  12. Keras深度学习实战(31)——构建电影推荐系统
  13. adams2015怎么把工具栏打开_怎么合并音乐?教大家3种快速完成音频合并的办法!...
  14. 方正字库的手写字体开始出真GBK了
  15. 主成分分析法(三):计算步骤
  16. docker入门及安装及基本命令
  17. Python爬虫 Selenium实现自动登录163邮箱和Locating Elements介绍
  18. 全国省市区建表sql
  19. 前端学习之CSS第三天
  20. Spring Boot+JSP项目中静态资源配置

热门文章

  1. iphone是android系统吗,iPhone6 Plus的手机系统是什么?能升级安卓4.3吗?
  2. 华为鸿蒙内核linux算国产,都是号称国产操作系统,阿里的YunOS跟华为“鸿蒙”有何不同之处...
  3. 程序流程图,UML各种图总结
  4. “智能制造”拍了拍“工业互联网”,哪些技术领域需要关注?
  5. 医疗器械维修前景如何
  6. List中contains方法使用
  7. ESP8266-Arduino远程控制继电器(开关)
  8. vc 生成html,成功从VC++的XML注释生成静态html文档
  9. 互联网公司纷纷倒在了望京SOHO
  10. 美柚广告推广的模式有几种?美柚广告投放的效果怎么样?