职场小白迷上优美句子:

还是电影  《无问西东》中的台词,这句有点感人:

沈光耀的妈妈对沈光耀说:"当初你离家千里,来到这个地方读书,你父亲和我都没有反对过,因为,是我们想你,能享受到人生的乐趣,比如读万卷书行万里路,比如同你喜欢的女孩子结婚生子。注意不是给我增添子孙,而是你自己,能够享受为人父母的乐趣,你一生所要追求的功名利禄,没有什么是你的祖上没经历过的,那些只不过是人生的幻光。我怕,你还没想好怎么过这一生,你的命就没了啊!"

这个是效果图:

推荐一个写的很不错的博客  作者:  新根 - - - Android Camera2 API和拍照与录像过程

另外一个后来发现的:Camera2.0新API下的摄像头预览、原始图像数据获取等_黄政的博客-CSDN博客

权限:

    <uses-permission android:name="android.permission.CAMERA" /><uses-feature android:name="android.hardware.camera" /><uses-feature android:name="android.hardware.camera.autofocus" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.RECORD_AUDIO"/>

------------------------------ App ----------------------------

public class App extends Application {private static App mInstance;@Overridepublic void onCreate() {super.onCreate();mInstance = this;}/*** 获取全局上下文实例*/public static App getInstance() {return mInstance;}}

------------------------------ MainActivity ----------------------------

public class MainActivity extends AppCompatActivity implements View.OnClickListener {private String[] permissions = new String[]{Manifest.permission.CAMERA,Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.RECORD_AUDIO};private AutoFitTextureView mTextureview;private Button mTakePictureBtn;//拍照private Button mVideoRecodeBtn;//开始录像private LinearLayout mVerticalLinear;private Button mTakePictureBtn2;//拍照 横,竖屏状态分别设置了一个拍照,录像的按钮private Button mVideoRecodeBtn2;//开始录像private LinearLayout mHorizontalLinear;private TextView mVHScreenBtn;private CameraController mCameraController;private boolean mIsRecordingVideo; //开始停止录像public static String BASE_PATH = Environment.getExternalStorageDirectory() + "/AAA";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);getSupportActionBar().hide();// 去掉标题栏getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);// 设置全屏setContentView(R.layout.activity_main);//Activity对象PermissionsUtils.getInstance().checkPermissions(permissions, permissionsResult);}/*** 创建监听权限的接口对象*/private PermissionsUtils.IPermissionsResult permissionsResult = new PermissionsUtils.IPermissionsResult() {@Overridepublic void requestPermissions(String[] permissions, int requestCode) {ActivityCompat.requestPermissions(MainActivity.this, permissions, requestCode);}@Overridepublic void passPermissons() {//授权后的操作//获取相机管理类的实例mCameraController = CameraController.getInstance();mCameraController.setFolderPath(BASE_PATH);initView();//判断当前横竖屏状态if (getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {mVHScreenBtn.setText("竖屏");} else {mVHScreenBtn.setText("横屏");}}@Overridepublic void forbitPermissons() {//未授权,请手动授权}@Overridepublic void positiveClick(Intent intent) {startActivity(intent);finish();}};@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[]permissions, @NonNull int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);//就多一个参数thisPermissionsUtils.getInstance().onRequestPermissionsResult(this.getPackageName(), requestCode,permissions, grantResults);}@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)private void initView() {mTextureview = (AutoFitTextureView) findViewById(R.id.textureview);mTakePictureBtn = (Button) findViewById(R.id.take_picture_btn);mTakePictureBtn.setOnClickListener(this);mVideoRecodeBtn = (Button) findViewById(R.id.video_recode_btn);mVideoRecodeBtn.setOnClickListener(this);mVerticalLinear = (LinearLayout) findViewById(R.id.vertical_linear);mTakePictureBtn2 = (Button) findViewById(R.id.take_picture_btn2);mTakePictureBtn2.setOnClickListener(this);mVideoRecodeBtn2 = (Button) findViewById(R.id.video_recode_btn2);mVideoRecodeBtn2.setOnClickListener(this);mHorizontalLinear = (LinearLayout) findViewById(R.id.horizontal_linear);mVHScreenBtn = (TextView) findViewById(R.id.v_h_screen_btn);mVHScreenBtn.setOnClickListener(this);//判断当前屏幕方向if (getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {//竖屏时mHorizontalLinear.setVisibility(View.VISIBLE);mVerticalLinear.setVisibility(View.GONE);} else {//横屏时mVerticalLinear.setVisibility(View.VISIBLE);mHorizontalLinear.setVisibility(View.GONE);}WindowManager windowManager = getWindowManager();int orientation = getResources().getConfiguration().orientation;mCameraController.initCamera(mTextureview, windowManager, orientation);}@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.take_picture_btn:mCameraController.takePicture();break;case R.id.video_recode_btn:recordingVideo();break;case R.id.take_picture_btn2:mCameraController.takePicture();break;case R.id.video_recode_btn2:recordingVideo();break;case R.id.v_h_screen_btn://判断当前屏幕方向if (getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {//切换竖屏setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);} else {//切换横屏setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);}break;default:break;}}/*** 视频录像*/private void recordingVideo(){if (mIsRecordingVideo) {mCameraController.stopRecordingVideo();mVideoRecodeBtn.setText("开始录像");mVideoRecodeBtn2.setText("开始录像");Toast.makeText(this, "录像结束", Toast.LENGTH_SHORT).show();} else {mVideoRecodeBtn.setText("停止录像");mVideoRecodeBtn2.setText("停止录像");mCameraController.startRecordingVideo();Toast.makeText(this, "录像开始", Toast.LENGTH_SHORT).show();}mIsRecordingVideo = !mIsRecordingVideo;}@Overrideprotected void onDestroy() {super.onDestroy();if (mCameraController != null) {mCameraController.closeCamera();}}}

------------------------- activity_main.xml ---------------------------

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context=".MainActivity"><com.test.camera2demo.AutoFitTextureViewandroid:id="@+id/textureview"android:layout_width="match_parent"android:layout_height="match_parent" /><LinearLayoutandroid:id="@+id/vertical_linear"android:layout_width="match_parent"android:layout_height="80dp"android:layout_alignParentBottom="true"android:gravity="center"android:orientation="horizontal"><Buttonandroid:id="@+id/take_picture_btn"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="拍照" /><Buttonandroid:id="@+id/video_recode_btn"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="20dp"android:text="开始录像" /><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:visibility="invisible" /></LinearLayout><LinearLayoutandroid:id="@+id/horizontal_linear"android:layout_width="80dp"android:layout_height="match_parent"android:layout_alignParentRight="true"android:gravity="center"android:orientation="vertical"><Buttonandroid:id="@+id/take_picture_btn2"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="拍照" /><Buttonandroid:id="@+id/video_recode_btn2"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="20dp"android:text="开始录像" /></LinearLayout><Buttonandroid:id="@+id/v_h_screen_btn"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentRight="true"android:layout_alignParentBottom="true"android:layout_marginBottom="15dp"android:text="横屏" /></RelativeLayout>

------------------------ PermissionsUtils ----------------------

public class PermissionsUtils {private final int mRequestCode = 100;//权限请求码public static boolean showSystemSetting = true;private PermissionsUtils() {}private static PermissionsUtils permissionsUtils;private IPermissionsResult mPermissionsResult;public static PermissionsUtils getInstance() {if (permissionsUtils == null) {permissionsUtils = new PermissionsUtils();}return permissionsUtils;}public void checkPermissions(String[] permissions, @NonNull IPermissionsResult permissionsResult) {mPermissionsResult = permissionsResult;if (Build.VERSION.SDK_INT < 23) {//6.0才用动态权限permissionsResult.passPermissons();return;}//创建一个mPermissionList,逐个判断哪些权限未授予,未授予的权限存储到mPerrrmissionList中List<String> mPermissionList = new ArrayList<>();//逐个判断你要的权限是否已经通过for (int i = 0; i < permissions.length; i++) {if (ContextCompat.checkSelfPermission(App.getInstance().getApplicationContext(), permissions[i]) != PackageManager.PERMISSION_GRANTED) {mPermissionList.add(permissions[i]);//添加还未授予的权限}}//申请权限if (mPermissionList.size() > 0) {//有权限没有通过,需要申请permissionsResult.requestPermissions(permissions, mRequestCode);} else {//说明权限都已经通过,可以做你想做的事情去permissionsResult.passPermissons();return;}}//请求权限后回调的方法//参数: requestCode  是我们自己定义的权限请求码//参数: permissions  是我们请求的权限名称数组//参数: grantResults 是我们在弹出页面后是否允许权限的标识数组,数组的长度对应的是权限名称数组的长度,数组的数据0表示允许权限,-1表示我们点击了禁止权限public void onRequestPermissionsResult(String packageName, int requestCode, @NonNull String[] permissions,@NonNull int[] grantResults) {boolean hasPermissionDismiss = false;//有权限没有通过if (mRequestCode == requestCode) {for (int i = 0; i < grantResults.length; i++) {if (grantResults[i] == -1) {hasPermissionDismiss = true;}}//如果有权限没有被允许if (hasPermissionDismiss) {if (showSystemSetting) {showSystemPermissionsSettingDialog(packageName);//跳转到系统设置权限页面,或者直接关闭页面,不让他继续访问} else {mPermissionsResult.forbitPermissons();}} else {//全部权限通过,可以进行下一步操作。。。mPermissionsResult.passPermissons();}}}/*** 不再提示权限时的展示对话框*/private AlertDialog mPermissionDialog;private void showSystemPermissionsSettingDialog(final String packageName) {if (mPermissionDialog == null) {mPermissionDialog = new AlertDialog.Builder(App.getInstance().getApplicationContext()).setMessage("已禁用权限,请手动授予").setPositiveButton("设置", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {cancelPermissionDialog();Uri packageURI = Uri.parse("package:" + packageName);Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, packageURI);mPermissionsResult.positiveClick(intent);}}).setNegativeButton("取消", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {//关闭页面或者做其他操作cancelPermissionDialog();//mContext.finish();mPermissionsResult.forbitPermissons();}}).create();}mPermissionDialog.show();}//关闭对话框private void cancelPermissionDialog() {if (mPermissionDialog != null) {mPermissionDialog.cancel();mPermissionDialog = null;}}public interface IPermissionsResult {void requestPermissions(String[] permissions, int requestCode);void passPermissons();void forbitPermissons();void positiveClick(Intent intent);}}

------------------------ AutoFitTextureView ----------------------

public class AutoFitTextureView extends TextureView {private int mRatioWidth = 0;private int mRatioHeight = 0;public AutoFitTextureView(Context context) {this(context, null);}public AutoFitTextureView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public AutoFitTextureView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);}/*** @param width  Relative horizontal size* @param height Relative vertical size*/public void setAspectRatio(int width, int height) {if (width < 0 || height < 0) {throw new IllegalArgumentException("Size cannot be negative.");}mRatioWidth = width;mRatioHeight = height;requestLayout();}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//        int width = MeasureSpec.getSize(widthMeasureSpec);
//        int height = MeasureSpec.getSize(heightMeasureSpec);
//        if (0 == mRatioWidth || 0 == mRatioHeight) {
//            setMeasuredDimension(width, height);
//        } else {
//            if (width < height * mRatioWidth / mRatioHeight) {
//                setMeasuredDimension(width, width * mRatioHeight / mRatioWidth);
//            } else {
//                setMeasuredDimension(height * mRatioWidth / mRatioHeight, height);
//            }
//        }}}

------------------------ CameraController--------------------------

public class CameraController {private static final String TAG = "CameraControllerTag";//保存视频,图片的文件夹路径private String mFolderPath;private ImageReader mImageReader;private HandlerThread mBackgroundThread;private Handler mBackgroundHandler;private final int START_MEDIA_RECORDER_MSG = 1001;private Handler mainHandler = new Handler(Looper.getMainLooper()) {@Overridepublic void handleMessage(@NonNull Message msg) {super.handleMessage(msg);switch (msg.what) {case START_MEDIA_RECORDER_MSG://开启录像mMediaRecorder.start();break;}}};private AutoFitTextureView mTextureView;private WindowManager mWindowManager;private int mOrientation = -1;//一个信号量以防止应用程序在关闭相机之前退出。private Semaphore mCameraOpenCloseLock = new Semaphore(1);//当前相机的IDprivate String mCameraId;private CameraDevice mCameraDevice;private Size mPreviewSize;private CaptureRequest.Builder mPreviewRequestBuilder;private CameraCaptureSession mCaptureSession;private CaptureRequest mPreviewRequest;//拍照储存文件private File mFile;private Integer mSensorOrientation;private CameraCaptureSession mPreviewSession;private CaptureRequest.Builder mPreviewBuilder;//Camera2 API保证的最大预览宽度private static final int MAX_PREVIEW_WIDTH = 1920;//Camera2 API保证的最大预览高度private static final int MAX_PREVIEW_HEIGHT = 1080;private boolean mFlashSupported;private int mState = STATE_PREVIEW;private static final SparseIntArray ORIENTATIONS = new SparseIntArray();static {ORIENTATIONS.append(Surface.ROTATION_0, 90);ORIENTATIONS.append(Surface.ROTATION_90, 0);ORIENTATIONS.append(Surface.ROTATION_180, 270);ORIENTATIONS.append(Surface.ROTATION_270, 180);}private static final int SENSOR_ORIENTATION_DEFAULT_DEGREES = 90;private static final int SENSOR_ORIENTATION_INVERSE_DEGREES = 270;//相机状态:显示相机预览private static final int STATE_PREVIEW = 0;//相机状态:等待焦点被锁定private static final int STATE_WAITING_LOCK = 1;//等待曝光被Precapture状态private static final int STATE_WAITING_PRECAPTURE = 2;//相机状态:等待曝光的状态是不是Precaptureprivate static final int STATE_WAITING_NON_PRECAPTURE = 3;//相机状态:拍照private static final int STATE_PICTURE_TAKEN = 4;private MediaRecorder mMediaRecorder;private String mNextVideoAbsolutePath;private CameraController() {}private static class ClassHolder {static CameraController mInstance = new CameraController();}public static CameraController getInstance() {return ClassHolder.mInstance;}public void initCamera(AutoFitTextureView textureView, WindowManager windowManager, int orientation) {this.mTextureView = textureView;this.mWindowManager = windowManager;this.mOrientation = orientation;startBackgroundThread();if (mTextureView.isAvailable()) {openCamera(mTextureView.getWidth(), mTextureView.getHeight());} else {mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);}}/*** 设置需要保存文件的文件夹路径** @param path*/public void setFolderPath(String path) {this.mFolderPath = path;File mFolder = new File(path);if (!mFolder.exists()) {mFolder.mkdirs();Log.d(TAG, "文件夹不存在去创建");} else {Log.d(TAG, "文件夹已创建");}}public String getFolderPath() {return mFolderPath;}/*** 拍照*/public void takePicture() {lockFocus();}/*** 开始录像*/@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)public void startRecordingVideo() {if (null == mCameraDevice || !mTextureView.isAvailable() || null == mPreviewSize) {return;}try {closePreviewSession();setUpMediaRecorder();SurfaceTexture texture = mTextureView.getSurfaceTexture();assert texture != null;texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);List<Surface> surfaces = new ArrayList<>();//为相机预览设置曲面Surface previewSurface = new Surface(texture);surfaces.add(previewSurface);mPreviewBuilder.addTarget(previewSurface);//设置MediaRecorder的表面Surface recorderSurface = mMediaRecorder.getSurface();surfaces.add(recorderSurface);mPreviewBuilder.addTarget(recorderSurface);// 启动捕获会话// 一旦会话开始,我们就可以更新UI并开始录制mCameraDevice.createCaptureSession(surfaces, new CameraCaptureSession.StateCallback() {@Overridepublic void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {mPreviewSession = cameraCaptureSession;updatePreview();mainHandler.sendEmptyMessage(START_MEDIA_RECORDER_MSG);}@Overridepublic void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {}}, mBackgroundHandler);} catch (CameraAccessException | IOException e) {e.printStackTrace();}}/*** 停止录像*/public void stopRecordingVideo() {mMediaRecorder.stop();mMediaRecorder.reset();mNextVideoAbsolutePath = null;startPreview();}private void closePreviewSession() {if (mPreviewSession != null) {mPreviewSession.close();mPreviewSession = null;}}private void setUpMediaRecorder() throws IOException {//设置用于录制的音源mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);//开始捕捉和编码数据到setOutputFile(指定的文件)mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);//设置在录制过程中产生的输出文件的格式mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);if (mNextVideoAbsolutePath == null || mNextVideoAbsolutePath.isEmpty()) {mNextVideoAbsolutePath = getVideoFilePath();}//设置输出文件的路径mMediaRecorder.setOutputFile(mNextVideoAbsolutePath);//设置录制的视频编码比特率mMediaRecorder.setVideoEncodingBitRate(10000000);//设置要捕获的视频帧速率mMediaRecorder.setVideoFrameRate(25);//设置要捕获的视频的宽度和高度mMediaRecorder.setVideoSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());//设置视频编码器,用于录制mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);//设置audio的编码格式mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);int rotation = mWindowManager.getDefaultDisplay().getRotation();Log.d(TAG, "setUpMediaRecorder-rotation: " + rotation);switch (mSensorOrientation) {case SENSOR_ORIENTATION_DEFAULT_DEGREES:mMediaRecorder.setOrientationHint(ORIENTATIONS.get(rotation));break;case SENSOR_ORIENTATION_INVERSE_DEGREES:mMediaRecorder.setOrientationHint(ORIENTATIONS.get(rotation));break;}mMediaRecorder.prepare();}private String getVideoFilePath() {return getFolderPath() + "/" + System.currentTimeMillis() + ".mp4";}private void updatePreview() {if (null == mCameraDevice) {return;}try {mPreviewBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);HandlerThread thread = new HandlerThread("CameraPreview");thread.start();mPreviewSession.setRepeatingRequest(mPreviewBuilder.build(), null, mBackgroundHandler);} catch (CameraAccessException e) {e.printStackTrace();}}private void lockFocus() {//创建文件mFile = new File(getFolderPath() + "/" + getNowDate() + ".jpg");try {mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,CameraMetadata.CONTROL_AF_TRIGGER_START);mState = STATE_WAITING_LOCK;mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback,mBackgroundHandler);} catch (CameraAccessException e) {e.printStackTrace();Log.d(TAG, "lockFocus: e:" + e.getMessage());}}/*** 获取当前时间,用来给文件夹命名*/private String getNowDate() {SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");return simpleDateFormat.format(new Date());}private TextureView.SurfaceTextureListener mSurfaceTextureListener= new TextureView.SurfaceTextureListener() {@Overridepublic void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {openCamera(width, height);}@Overridepublic void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {}@Overridepublic boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {return false;}@Overridepublic void onSurfaceTextureUpdated(SurfaceTexture surface) {}};private void startBackgroundThread() {mBackgroundThread = new HandlerThread("CameraBackground");mBackgroundThread.start();mBackgroundHandler = new Handler(mBackgroundThread.getLooper());}/*** 开启相机*/private void openCamera(int width, int height) {//设置相机输出setUpCameraOutputs(width, height);//配置变换configureTransform(width, height);CameraManager manager = (CameraManager) App.getInstance().getApplicationContext().getSystemService(Context.CAMERA_SERVICE);try {if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {throw new RuntimeException("Time out waiting to lock camera opening.");}if (ActivityCompat.checkSelfPermission(App.getInstance().getApplicationContext(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {return;}//打开相机预览manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler);} catch (CameraAccessException e) {e.printStackTrace();} catch (InterruptedException e) {throw new RuntimeException("Interrupted while trying to lock camera opening.", e);}}/*** CameraDevice状态更改时被调用。*/private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {@Overridepublic void onOpened(@NonNull CameraDevice cameraDevice) {// 打开相机时调用此方法。 在这里开始相机预览。mCameraOpenCloseLock.release();mCameraDevice = cameraDevice;//创建CameraPreviewSessionstartPreview();}@Overridepublic void onDisconnected(@NonNull CameraDevice cameraDevice) {mCameraOpenCloseLock.release();cameraDevice.close();mCameraDevice = null;}@Overridepublic void onError(@NonNull CameraDevice cameraDevice, int error) {mCameraOpenCloseLock.release();cameraDevice.close();mCameraDevice = null;}};/*** 为相机预览创建新的CameraCaptureSession*/private void startPreview() {try {SurfaceTexture texture = mTextureView.getSurfaceTexture();assert texture != null;// 将默认缓冲区的大小配置为我们想要的相机预览的大小。 设置分辨率texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());// 预览的输出Surface。Surface surface = new Surface(texture);//设置了一个具有输出Surface的CaptureRequest.Builder。mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);mPreviewRequestBuilder.addTarget(surface);//创建一个CameraCaptureSession来进行相机预览。mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()),new CameraCaptureSession.StateCallback() {@Overridepublic void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {// 相机已经关闭if (null == mCameraDevice) {return;}//会话准备好后,我们开始显示预览mCaptureSession = cameraCaptureSession;// 自动对焦mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);// 最终开启相机预览并添加事件mPreviewRequest = mPreviewRequestBuilder.build();try {mCaptureSession.setRepeatingRequest(mPreviewRequest,mCaptureCallback, mBackgroundHandler);} catch (CameraAccessException e) {e.printStackTrace();}}@Overridepublic void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {}}, null);} catch (CameraAccessException e) {e.printStackTrace();}}/*** 处理与JPEG捕获有关的事件*/private CameraCaptureSession.CaptureCallback mCaptureCallback = new CameraCaptureSession.CaptureCallback() {//处理private void process(CaptureResult result) {switch (mState) {case STATE_PREVIEW: {Log.d(TAG, "process --- STATE_PREVIEW");//预览状态break;}case STATE_WAITING_LOCK: {//等待对焦 afState==1:连续对焦状态;afState==2:已对焦完成。afState==4:对焦正确,并已锁定。afState==4:未能成功对焦并锁定Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);Log.d(TAG, "process --- STATE_WAITING_LOCK, afState:" + afState);if (afState == null) {captureStillPicture();} else if (CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState ||CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState) {// 某些设备上的控制状态可以为空Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);if (aeState == null || aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) {mState = STATE_PICTURE_TAKEN;captureStillPicture();} else {runPrecaptureSequence();}}break;}case STATE_WAITING_PRECAPTURE: {Log.d(TAG, "process --- STATE_WAITING_PRECAPTURE");//某些设备上的控制状态可以为空Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);if (aeState == null ||aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE ||aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED) {mState = STATE_WAITING_NON_PRECAPTURE;}break;}case STATE_WAITING_NON_PRECAPTURE: {Log.d(TAG, "process --- STATE_WAITING_NON_PRECAPTURE");// CONTROL_AE_STATE can be null on some devicesInteger aeState = result.get(CaptureResult.CONTROL_AE_STATE);if (aeState == null || aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) {mState = STATE_PICTURE_TAKEN;captureStillPicture();}break;}}}@Overridepublic void onCaptureProgressed(@NonNull CameraCaptureSession session,@NonNull CaptureRequest request,@NonNull CaptureResult partialResult) {process(partialResult);}@Overridepublic void onCaptureCompleted(@NonNull CameraCaptureSession session,@NonNull CaptureRequest request,@NonNull TotalCaptureResult result) {process(result);}};/*** 拍摄静态图片。*/private void captureStillPicture() {try {if (null == mCameraDevice) {return;}// 这是用来拍摄照片的CaptureRequest.Builder。final CaptureRequest.Builder captureBuilder =mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);captureBuilder.addTarget(mImageReader.getSurface());// 使用相同的AE和AF模式作为预览。captureBuilder.set(CaptureRequest.CONTROL_AF_MODE,CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);setAutoFlash(captureBuilder);// 方向int rotation = mWindowManager.getDefaultDisplay().getRotation();captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, getOrientation(rotation));CameraCaptureSession.CaptureCallback CaptureCallback= new CameraCaptureSession.CaptureCallback() {@Overridepublic void onCaptureCompleted(@NonNull CameraCaptureSession session,@NonNull CaptureRequest request,@NonNull TotalCaptureResult result) {ToastUtils.showToast("图片地址: " + mFile);Log.d(TAG, mFile.toString());unlockFocus();}};//停止连续取景mCaptureSession.stopRepeating();//捕获图片mCaptureSession.capture(captureBuilder.build(), CaptureCallback, null);} catch (CameraAccessException e) {e.printStackTrace();Log.d(TAG, "captureStillPicture: e:" + e.getMessage());}}private int getOrientation(int rotation) {return (ORIENTATIONS.get(rotation) + mSensorOrientation + 270) % 360;}private void setAutoFlash(CaptureRequest.Builder requestBuilder) {if (mFlashSupported) {requestBuilder.set(CaptureRequest.CONTROL_AE_MODE,CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);}}/*** 解锁焦点*/private void unlockFocus() {try {//重置自动对焦mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,CameraMetadata.CONTROL_AF_TRIGGER_CANCEL);setAutoFlash(mPreviewRequestBuilder);mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback,mBackgroundHandler);//将相机恢复正常的预览状态。mState = STATE_PREVIEW;//打开连续取景模式mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback,mBackgroundHandler);} catch (CameraAccessException e) {e.printStackTrace();Log.d(TAG, "unlockFocus: e:" + e.getMessage());}}/*** 运行捕获静止图像的预捕获序列。 当我们从{@link #()}的{@link #mCaptureCallback}中得到响应时,应该调用此方法。*/private void runPrecaptureSequence() {try {//这是如何告诉相机触发的。mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);//告诉 mCaptureCallback 等待preapture序列被设置.mState = STATE_WAITING_PRECAPTURE;mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback,mBackgroundHandler);} catch (CameraAccessException e) {e.printStackTrace();}}private ImageReader.OnImageAvailableListener mOnImageAvailableListener= new ImageReader.OnImageAvailableListener() {@Overridepublic void onImageAvailable(ImageReader reader) {//等图片可以得到的时候获取图片并保存mBackgroundHandler.post(new ImageSaver(reader.acquireNextImage(), mFile));}};/*** 设置与相机相关的成员变量。** @param width  相机预览的可用尺寸宽度* @param height 相机预览的可用尺寸的高度*/private void setUpCameraOutputs(int width, int height) {CameraManager manager = (CameraManager) App.getInstance().getApplicationContext().getSystemService(Context.CAMERA_SERVICE);//获取摄像头列表try {for (String cameraId : manager.getCameraIdList()) {CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);//不使用前置摄像Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT) {continue;//不结束循环,只跳出本次循环}StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);if (map == null) {continue;}//对于静态图像拍照, 使用最大的可用尺寸Size largest = Collections.max(Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)),new CompareSizesByArea());mImageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(), ImageFormat.JPEG, 2);mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mBackgroundHandler);//获取手机旋转的角度以调整图片的方向int displayRotation = mWindowManager.getDefaultDisplay().getRotation();mSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);boolean swappedDimensions = false;switch (displayRotation) {case Surface.ROTATION_0:case Surface.ROTATION_180://横屏if (mSensorOrientation == 90 || mSensorOrientation == 270) {swappedDimensions = true;}break;case Surface.ROTATION_90:case Surface.ROTATION_270://竖屏if (mSensorOrientation == 0 || mSensorOrientation == 180) {swappedDimensions = true;}break;default:Log.e(TAG, "Display rotation is invalid: " + displayRotation);}Point displaySize = new Point();mWindowManager.getDefaultDisplay().getSize(displaySize);int rotatedPreviewWidth = width;int rotatedPreviewHeight = height;int maxPreviewWidth = displaySize.x;int maxPreviewHeight = displaySize.y;if (swappedDimensions) {rotatedPreviewWidth = height;rotatedPreviewHeight = width;maxPreviewWidth = displaySize.y;maxPreviewHeight = displaySize.x;}if (maxPreviewWidth > MAX_PREVIEW_WIDTH) {maxPreviewWidth = MAX_PREVIEW_WIDTH;}if (maxPreviewHeight > MAX_PREVIEW_HEIGHT) {maxPreviewHeight = MAX_PREVIEW_HEIGHT;}WindowManager wm = (WindowManager) App.getInstance().getApplicationContext().getSystemService(Context.WINDOW_SERVICE);DisplayMetrics displayMetrics = new DisplayMetrics();wm.getDefaultDisplay().getMetrics(displayMetrics);int widthPixels = displayMetrics.widthPixels;int heightPixels = displayMetrics.heightPixels;Log.d(TAG, "widthPixels: " + widthPixels + "____heightPixels:" + heightPixels);//尝试使用太大的预览大小可能会超出摄像头的带宽限制mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class),rotatedPreviewWidth, rotatedPreviewHeight, maxPreviewWidth,maxPreviewHeight, largest);//我们将TextureView的宽高比与我们选择的预览大小相匹配。这样设置不会拉伸,但是不能全屏展示if (mOrientation == Configuration.ORIENTATION_LANDSCAPE) {//横屏mTextureView.setAspectRatio(mPreviewSize.getWidth(), mPreviewSize.getHeight());Log.d(TAG, "横屏: " + "width:" + mPreviewSize.getWidth() + "____height:" + mPreviewSize.getHeight());} else {// 竖屏mTextureView.setAspectRatio(widthPixels, heightPixels);Log.d(TAG, "竖屏: " + "____height:" + mPreviewSize.getHeight() + "width:" + mPreviewSize.getWidth());}mMediaRecorder = new MediaRecorder();mCameraId = cameraId;return;}} catch (CameraAccessException e) {Log.d(TAG, "setUpCameraOutputs-CameraAccessException: " + e.getMessage());}}/*** 根据他们的区域比较两个Size*/static class CompareSizesByArea implements Comparator<Size> {@Overridepublic int compare(Size lhs, Size rhs) {// 我们在这里投放,以确保乘法不会溢出return Long.signum((long) lhs.getWidth() * lhs.getHeight() -(long) rhs.getWidth() * rhs.getHeight());}}private static Size chooseOptimalSize(Size[] choices, int textureViewWidth,int textureViewHeight, int maxWidth, int maxHeight, Size aspectRatio) {// 收集支持的分辨率,这些分辨率至少与预览图面一样大List<Size> bigEnough = new ArrayList<>();// 收集小于预览表面的支持分辨率List<Size> notBigEnough = new ArrayList<>();int w = aspectRatio.getWidth();int h = aspectRatio.getHeight();for (Size option : choices) {if (option.getWidth() <= maxWidth && option.getHeight() <= maxHeight &&option.getHeight() == option.getWidth() * h / w) {if (option.getWidth() >= textureViewWidth &&option.getHeight() >= textureViewHeight) {bigEnough.add(option);} else {notBigEnough.add(option);}}}//挑一个足够大的最小的。如果没有一个足够大的,就挑一个不够大的。if (bigEnough.size() > 0) {return Collections.min(bigEnough, new CompareSizesByArea());} else if (notBigEnough.size() > 0) {return Collections.max(notBigEnough, new CompareSizesByArea());} else {Log.d(TAG, "Couldn't find any suitable preview size");return choices[0];}}private void configureTransform(int viewWidth, int viewHeight) {if (null == mTextureView || null == mPreviewSize) {return;}int rotation = mWindowManager.getDefaultDisplay().getRotation();Matrix matrix = new Matrix();RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);RectF bufferRect = new RectF(0, 0, mPreviewSize.getHeight(), mPreviewSize.getWidth());float centerX = viewRect.centerX();float centerY = viewRect.centerY();if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);float scale = Math.max((float) viewHeight / mPreviewSize.getHeight(),(float) viewWidth / mPreviewSize.getWidth());matrix.postScale(scale, scale, centerX, centerY);matrix.postRotate(90 * (rotation - 2), centerX, centerY);} else if (Surface.ROTATION_180 == rotation) {matrix.postRotate(180, centerX, centerY);}mTextureView.setTransform(matrix);}/*** 将JPG保存到指定的文件中。*/private static class ImageSaver implements Runnable {/*** JPEG图像*/private final Image mImage;/*** 保存图像的文件*/private final File mFile;public ImageSaver(Image image, File file) {mImage = image;mFile = file;}@Overridepublic void run() {ByteBuffer buffer = mImage.getPlanes()[0].getBuffer();byte[] bytes = new byte[buffer.remaining()];buffer.get(bytes);FileOutputStream output = null;try {output = new FileOutputStream(mFile);output.write(bytes);} catch (IOException e) {e.printStackTrace();} finally {mImage.close();if (null != output) {try {output.close();} catch (IOException e) {e.printStackTrace();}}}}}public void closeCamera() {if (mWindowManager != null) {mWindowManager = null;}if (mTextureView != null) {mTextureView.setSurfaceTextureListener(null);mTextureView = null;}}}

------------------------------ ToastUtils----------------------------

public class ToastUtils {private static Toast mToast;/*** 子线程主线程都支持弹框提示* Context 为 application** @param msg*/public static void showToast(String msg) {//主线程if (Thread.currentThread() == Looper.getMainLooper().getThread()) {if (mToast == null) {mToast = Toast.makeText(App.getInstance().getApplicationContext(), "", Toast.LENGTH_SHORT);}mToast.setText(msg);mToast.show();} else {mainHandler.sendMessage(mainHandler.obtainMessage(999, msg));}}/*** 切换到主线程*/private static Handler mainHandler = new Handler(Looper.getMainLooper()) {@Overridepublic void handleMessage(@NonNull Message msg) {super.handleMessage(msg);switch (msg.what) {case 999:if (mToast == null) {mToast = Toast.makeText(App.getInstance().getApplicationContext(), "", Toast.LENGTH_LONG);}mToast.setText((String) msg.obj);mToast.show();break;}}};
}

码云源码https://gitee.com/baipenggui/camera2-demo.git

有博友发现码云下载源码没有权限了,这是github地址:

github源码https://github.com/Gui779/Camera2Demo.git

Android 使用Camera2 实现拍照录像的功能相关推荐

  1. 【Android -- 相机】Camera2 实现拍照 预览功能

    前言 上篇文章,我们已经用 Camera1 实现了预览和拍照的功能,但也说到,在API21的时候,Camera1已经被标注为弃用,因为它的API功能和灵活性满足不了现在日益复杂的相机开发了,所以在 A ...

  2. buildroot+qt+qcamrea+opencv打开摄像头进行拍照录像保存功能

    相关参考 如何用FFmpeg API采集摄像头视频和麦克风音频,并实现录制文件的功能_爱写代码的自由人-CSDN博客_ffmpeg 摄像头 红胖子(红模仿)的博文大全:开发技术集合(包含Qt实用技术. ...

  3. Android自定义照相机实现 拍照 录像

    文档中的Camera 要使用Camera,首先我们先看一下文档(http://androiddoc.qiniudn.com/reference/android/hardware/Camera.html ...

  4. Android拍照录像框架Camera2使用简介

    Android拍照录像框架Camera2使用简介 在Android 21时Google更新了以往的摄像头类库Camera,取而代之的是Camera2.作为更新版本,Camera2具有更强功能的同时也增 ...

  5. Android开发 Camera2开发_1_拍照功能开发

    Android开发 Camera2开发_1_拍照功能开发 介绍 google已经在Android5.1之后取消了对Camera1的更新,转而提供了功能更加强大的Camera2.虽然新版本依然可以使用C ...

  6. android 相机和照片一起_Android相机开发(三): 实现拍照录像和查看

    Android Camera Develop: capture photo and video 概述 上篇完成了相机的偏好设置,本篇就要实现相机的核心功能--拍照和录像了.直觉上拍照和录像应该差别不大 ...

  7. android 拍照插件,拍照录像,android原生插件

    更新记录 1.0.1(2021-03-31) 拍照录像模式,轻触拍照,长按录像:只录像模式,轻触即可录像 支持录像,设置视频的比特率,最长/最短录制时间等 支持录像时,输出视频第一帧图片 支持自定义图 ...

  8. 十分钟实现 Android Camera2 相机拍照

    1. 前言 因为工作中要使用Android Camera2 API,但因为Camera2比较复杂,网上资料也比较乱,有一定入门门槛,所以花了几天时间系统研究了下,并在CSDN上记录了下,希望能帮助到更 ...

  9. Java乔晓松-android中调用系统拍照功能并显示拍照的图片

    android中调用系统拍照功能并显示拍照的图片 如果你是拍照完,利用onActivityResult获取data数据,把data数据转换成Bitmap数据,这样获取到的图片,是拍照的照片的缩略图 代 ...

最新文章

  1. Go开发者路线图2019,请收下这份指南
  2. java多线程测试框架(含入参和返回值)
  3. python内积 卷积
  4. 服务器架构有哪些方式?—Vecloud微云
  5. Java数据结构和算法:字符串、数组和广义表
  6. CTFshow php特性 web102
  7. 如何保护 SpringBoot 配置文件中的敏感信息
  8. 【随笔】深度学习之美——杨家有女初长成,养在深闺人未识
  9. 序列化的作用_Java 序列化的高级认识
  10. OpenCV与图像处理学习八——图像边缘提取(Canny检测代码)
  11. MyBatisPlus_删除篇_入门试炼_04
  12. mysql使用占位费付_美国大学硕士占位费怎么支付?三种支付方式任选!
  13. 7-12 两个数的简单计算器 (10 分)
  14. windows 安装pip 和 pychar 安装pymysql
  15. MatConvnet工具箱文档翻译理解五
  16. Qt::QWidget 无默认标题栏边框的拖拽修改大小方式
  17. vray渲染里服务器信息,VRay分布式渲染详细介绍
  18. kindle电子书去drm_如何将无DRM的电子书加载到Kindle
  19. 炫酷文字消失动画网站404页面源码
  20. mysql导入access2016_将 Access 2.0 和 Access 95 数据库导入到当前版本

热门文章

  1. 排序算法3----插入法
  2. Xshell远程连接华为云服务器
  3. UMEditor配置
  4. 企业管理软件中的积分制管理软件添加成员操作介绍
  5. Project 学习使用
  6. 《写给大家看的设计书》- UI设计必看
  7. js实现图片预览功能
  8. 【STM32H7教程】第94章 STM32H7的SPI总线应用之双机通信(DMA方式)
  9. Java iText生成PDF文档
  10. 计算机网络知识的PPT,计算机网络基础理论知识.ppt