您的位置:首页 > 移动开发 > Android开发

Android[视频] camera 视频录制

2018-01-03 14:54 615 查看
由于camera已经被舍弃,建议使用替代类camera2。下篇文章会讲到camera2。切入主题。首先是权限,这个比较容易忘记申请。
<!-- 存储 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<!-- 网络权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<!--摄像头-->
<uses-permission android:name="android.permission.CAMERA" />
<!--录音-->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
视频录制需要存储权限、照相机权限、录音权限和网络权限。网络权限在这里并不是主要的。不过一般都顺手加上。毕竟网络是最常用的。
照相机权限和录音权限从Android6.0开始需要动态申请,申请方式Android6.0动态申请权限当然现在框架有很多,可以选择一种自己喜欢的使用。权限申请完成后,下面我们有就要自定义一个预览照相机拍摄画面的展示类了。有两个类可以选择 SurfaceView、SurfaceTexture 这里使用SurfaceView。不管怎么说没有比实现代码实在的东西了。<?xml version="1.0" encoding="utf-8"?><LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:gravity="center_horizontal"><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:layout_weight="1.0"><SurfaceViewandroid:id="@+id/surfaceview"android:layout_width="match_parent"android:layout_height="match_parent"/><Buttonandroid:id="@+id/btn_lamp"android:layout_width="30dp"android:layout_height="30dp"android:background="@drawable/btn_lamp_bg"android:layout_margin="@dimen/margin_10"android:layout_alignParentBottom="true"/><Buttonandroid:id="@+id/btn_lens"android:layout_width="30dp"android:layout_height="30dp"android:background="@drawable/lens"android:layout_margin="@dimen/margin_10"android:layout_alignParentBottom="true"android:layout_alignParentRight="true"/></RelativeLayout><ProgressBarandroid:id="@+id/progressBar"android:layout_width="match_parent"android:layout_height="5dp"style="@android:style/Widget.ProgressBar.Horizontal"android:progressDrawable="@drawable/progressbar_color"/></LinearLayout>
package pers.wtt.ui.video.view;import android.annotation.TargetApi;import android.content.Context;import android.content.Intent;import android.content.pm.FeatureInfo;import android.content.pm.PackageManager;import android.hardware.Camera;import android.media.MediaRecorder;import android.net.Uri;import android.os.Build;import android.os.Environment;import android.os.Handler;import android.os.Message;import android.util.AttributeSet;import android.view.LayoutInflater;import android.view.OrientationEventListener;import android.view.SurfaceHolder;import android.view.SurfaceView;import android.view.View;import android.widget.Button;import android.widget.LinearLayout;import android.widget.ProgressBar;import com.googlecode.mp4parser.util.Matrix;import pers.wtt.R;import pers.wtt.lib_common.utils.LogUtil;import java.io.File;import java.io.IOException;import java.util.Timer;import java.util.TimerTask;/*** Created by 王亭 on 2017/8/3.* 视频录制*/public class MovieRecorderView extends LinearLayout implements MediaRecorder.OnErrorListener,View.OnClickListener {private Context context;private Camera.Parameters parameters;private SurfaceView mSurfaceView;private SurfaceHolder mSurfaceHolder;private ProgressBar mProgressBar;private Button btn_lamp;private Button btn_lens;private MediaRecorder mMediaRecorder;private Camera mCamera;private Timer mTimer;// 计时器private OnRecordFinishListener mOnRecordFinishListener;// 录制完成回调接口private int mWidth;// 视频分辨率宽度private int mHeight;// 视频分辨率高度private boolean isOpenCamera;// 是否一开始就打开摄像头private int mRecordMaxTime;// 一次拍摄最长时间private int mTimeCount;// 时间计数private File mVecordFile = null;// 文件private String ROATE_MP4 = "";private boolean record = false;private boolean isLamp = false;private int mOrientation = 0;private boolean camF = false;private Matrix matrix = Matrix.ROTATE_90;private OrientationEventListener orientationEventListener;public MovieRecorderView(Context context) {this(context, null);}public MovieRecorderView(Context context, AttributeSet attrs) {this(context, attrs, 0);}@TargetApi(Build.VERSION_CODES.HONEYCOMB)public MovieRecorderView(final Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);this.context = context;mWidth=640;mHeight=480;isOpenCamera=true;mRecordMaxTime=1000;LayoutInflater.from(context).inflate(R.layout.moive_recorder_view, this);mSurfaceView = (SurfaceView) findViewById(R.id.surfaceview);mProgressBar = (ProgressBar) findViewById(R.id.progressBar);btn_lamp = (Button) findViewById(R.id.btn_lamp);btn_lens = (Button) findViewById(R.id.btn_lens);btn_lamp.setOnClickListener(this);btn_lens.setOnClickListener(this);orientationEventListener = new OrientationEventListener(context) {@Overridepublic void onOrientationChanged(int orientation) {if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) {return;}//保证只返回四个方向int newOrientation = ((orientation + 45) / 90 * 90) % 360;if (newOrientation != mOrientation) {mOrientation = newOrientation;btn_lamp.setRotation(-mOrientation);btn_lens.setRotation(-mOrientation);LogUtil.e("方向","mOrientation:"+mOrientation);//返回的mOrientation就是手机方向,为0°、90°、180°和270°中的一个}}};if(orientationEventListener.canDetectOrientation()){orientationEventListener.enable();}mProgressBar.setMax(mRecordMaxTime);// 设置进度条最大量mSurfaceHolder = mSurfaceView.getHolder();mSurfaceHolder.addCallback(new CustomCallBack());mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);}private void OpenLightOn()    {PackageManager pm= context.getPackageManager();FeatureInfo[]  features=pm.getSystemAvailableFeatures();for(FeatureInfo f : features){if(PackageManager.FEATURE_CAMERA_FLASH.equals(f.name))   //判断设备是否支持闪光灯{try {initCamera(true, -1);isLamp = true;btn_lamp.setSelected(true);} catch (IOException e) {e.printStackTrace();}}}}private void CloseLightOff()   {if ( mCamera != null ){try {initCamera(false, -1);isLamp = false;btn_lamp.setSelected(false);} catch (IOException e) {e.printStackTrace();}}}public boolean isRecord() {return record;}@Overridepublic void onClick(View v) {switch (v.getId()){ca14563se R.id.btn_lamp:if(isLamp){CloseLightOff();}else{OpenLightOn();}break;case R.id.btn_lens:if(camF){camF = false;}else{camF = true;}changeCamera();break;}}private void changeCamera() {Camera.CameraInfo cameraInfo = new Camera.CameraInfo();int cameraCount = Camera.getNumberOfCameras(); // get cameras numberLogUtil.e("摄像头数量","cameraCount:"+cameraCount + " camF:"+camF);for (int camIdx = 0; camIdx < cameraCount; camIdx++) {Camera.getCameraInfo(camIdx, cameraInfo); // get camerainfoLogUtil.e("摄像头数量","摄像头:"+cameraInfo.facing+"  " + cameraInfo.toString());if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { // 代表摄像头的方位,目前有定义值两个分别为CAMERA_FACING_FRONT前置和CAMERA_FACING_BACK后置if(camF) {try {initCamera(false, camIdx);camF = true;LogUtil.e("摄像头数量", "前置摄像头:" + camIdx);break;} catch (IOException e) {e.printStackTrace();}}} else {if(!camF) {try {initCamera(false, camIdx);camF = false;LogUtil.e("摄像头数量", "后置摄像头:" + camIdx);break;} catch (IOException e) {e.printStackTrace();}}}}}public void init() {if (!isOpenCamera) {// 如果未打开摄像头,则打开try {initCamera(isLamp, -1);} catch (IOException e) {e.printStackTrace();}}}/**** @author wt** @date 2015-2-5*/private class CustomCallBack implements SurfaceHolder.Callback, Camera.PreviewCallback {@Overridepublic void surfaceCreated(SurfaceHolder holder) {if (!isOpenCamera)return;try {initCamera(false, -1);} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {if (!isOpenCamera)return;freeCameraResource();}@Overridepublic void onPreviewFrame(byte[] data, Camera camera) {}}/*** 初始化摄像头** @author lip* @date 2015-3-16* @throws IOException* @param lamp*/private void initCamera(boolean lamp, int camIdx) throws IOException {if (mCamera != null) {freeCameraResource();}try {if(camIdx!=-1){mCamera = Camera.open(camIdx);}else {mCamera = Camera.open();}} catch (Exception e) {e.printStackTrace();freeCameraResource();}if (mCamera == null)return;setCameraParams(lamp);mCamera.setDisplayOrientation(90);mCamera.setPreviewDisplay(mSurfaceHolder);mCamera.startPreview();mCamera.unlock();isOpenCamera = true;}/*** 设置摄像头为竖屏** @author lip* @date 2015-3-16* @param lamp*/private void setCameraParams(boolean lamp) {if (mCamera != null) {parameters = mCamera.getParameters();if(lamp)parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);parameters.set("orientation", "portrait");parameters.setPreviewSize(mWidth, mHeight);mCamera.setParameters(parameters);}}/*** 释放摄像头资源** @author wt* @date 2015-2-5*/private void freeCameraResource() {if (mCamera != null) {mCamera.setPreviewCallback(null);mCamera.stopPreview();mCamera.lock();mCamera.release();mCamera = null;isOpenCamera = false;}}private void createRecordDir() {// File sampleDir = new File(Environment.getExternalStorageDirectory() + File.separator + "im/video/");File vecordDir = new File(Environment.getExternalStorageDirectory() + File.separator+"RecordVideo/");//File sampleDir = new File("/video/");if (!vecordDir.exists()) {vecordDir.mkdirs();}// 创建文件try {mVecordFile = File.createTempFile("recording", ".mp4", vecordDir);//mp4格式//LogUtilUtils.i(mVecordFile.getAbsolutePath());LogUtil.d("Path:",mVecordFile.getAbsolutePath());} catch (IOException e) {}}/*** 初始化** @author lip* @date 2015-3-16* @throws IOException*/private void initRecord() throws IOException {try {mMediaRecorder = new MediaRecorder();mMediaRecorder.reset();if (mCamera != null)mMediaRecorder.setCamera(mCamera);mMediaRecorder.setOnErrorListener(this);mMediaRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);// 视频源mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);// 音频源mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);// 视频输出格式mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);// 音频格式mMediaRecorder.setVideoSize(mWidth, mHeight);// 设置分辨率://            mMediaRecorder.setVideoFrameRate(18);// 帧率mMediaRecorder.setVideoEncodingBitRate(5 * 1024 * 512);// 设置帧频率,然后就清晰了mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);// 视频录制格式// mediaRecorder.setMaxDuration(Constant.MAXVEDIOTIME * 1000);mMediaRecorder.setOutputFile(mVecordFile.getAbsolutePath());if(camF){if(mOrientation==90){matrix = Matrix.ROTATE_180;mMediaRecorder.setOrientationHint(180);// 输出旋转180度,保持竖屏录制}else if(mOrientation==180) {matrix = Matrix.ROTATE_90;mMediaRecorder.setOrientationHint(90);// 输出旋转90度,保持竖屏录制}else if(mOrientation==270) {matrix = Matrix.ROTATE_0;mMediaRecorder.setOrientationHint(0);// 输出旋转0度,保持竖屏录制}else{matrix = Matrix.ROTATE_270;mMediaRecorder.setOrientationHint(270);// 输出旋转270度,保持竖屏录制}}else {if(mOrientation==90){matrix = Matrix.ROTATE_180;mMediaRecorder.setOrientationHint(180);// 输出旋转180度,保持竖屏录制}else if(mOrientation==180) {matrix = Matrix.ROTATE_270;mMediaRecorder.setOrientationHint(270);// 输出旋转270度,保持竖屏录制}else if(mOrientation==270) {matrix = Matrix.ROTATE_0;mMediaRecorder.setOrientationHint(0);// 输出旋转0度,保持竖屏录制}else{matrix = Matrix.ROTATE_90;mMediaRecorder.setOrientationHint(90);// 输出旋转90度,保持竖屏录制}}mMediaRecorder.prepare();mMediaRecorder.start();} catch (IllegalStateException e) {e.printStackTrace();} catch (RuntimeException e) {e.printStackTrace();} catch (Exception e) {e.printStackTrace();}}/*** 开始录制视频** @author wt* @date 2015-2-5//     * @param fileName//     *            视频储存位置* @param onRecordFinishListener*            达到指定时间之后回调接口*/public void record(final OnRecordFinishListener onRecordFinishListener) {this.mOnRecordFinishListener = onRecordFinishListener;createRecordDir();try {if (!isOpenCamera)// 如果未打开摄像头,则打开initCamera(isLamp, -1);initRecord();record = true;btn_lamp.setEnabled(false);btn_lens.setEnabled(false);mTimeCount = 0;// 时间计数器重新赋值mTimer = new Timer();mTimer.schedule(new TimerTask() {@Overridepublic void run() {// TODO Auto-generated method stubmTimeCount++;mProgressBar.setProgress(mTimeCount);// 设置进度条if (mTimeCount == mRecordMaxTime) {// 达到指定时间,停止拍摄stop();if (mOnRecordFinishListener != null) {Intent scanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);scanIntent.setData(Uri.fromFile(mVecordFile));getContext().sendBroadcast(scanIntent);mOnRecordFinishListener.onRecordFinish(mVecordFile.getAbsolutePath());}}}}, 0, 10);} catch (IOException e) {e.printStackTrace();}}private Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);btn_lamp.setEnabled(true);btn_lens.setEnabled(true );mProgressBar.setProgress(1);// 设置进度条}};/*** 停止拍摄** @author wt* @date 2015-2-5*/public void stop() {mOrientation = 0;record = false;stopRecord();releaseRecord();freeCameraResource();handler.sendEmptyMessage(0);//        ROATE_MP4 = DisplayUtil.rotateVideo(mVecordFile.getAbsolutePath(), Matrix.ROTATE_90);}/*** 停止录制** @author wt* @date 2015-2-5*/public void stopRecord() {mProgressBar.setProgress(0);if (mTimer != null)mTimer.cancel();if (mMediaRecorder != null) {// 设置后不会崩mMediaRecorder.setOnInfoListener(null);mMediaRecorder.setOnErrorListener(null);mMediaRecorder.setPreviewDisplay(null);try {mMediaRecorder.stop();} catch (IllegalStateException e) {e.printStackTrace();} catch (RuntimeException e) {e.printStackTrace();} catch (Exception e) {e.printStackTrace();}}}/*** 释放资源** @author wt* @date 2015-2-5*/private void releaseRecord() {if (mMediaRecorder != null) {mMediaRecorder.setOnErrorListener(null);try {mMediaRecorder.release();} catch (IllegalStateException e) {e.printStackTrace();} catch (Exception e) {e.printStackTrace();}}mMediaRecorder = null;}public int getTimeCount() {return mTimeCount;}/*** @return the mVecordFile*/public String getVideoPath() {return mVecordFile.getAbsolutePath();}/*** 录制完成回调接口** @author lip** @date 2015-3-16*/public interface OnRecordFinishListener {public void onRecordFinish(String absolutePath);}@Overridepublic void onError(MediaRecorder mr, int what, int extra) {try {if (mr != null)mr.reset();} catch (IllegalStateException e) {e.printStackTrace();} catch (Exception e) {e.printStackTrace();}}}
有一点需要注意,录制相关设置的顺序会影响程序运行。如果有异常出现,请检查顺序是否和本例相同。
<?xml version="1.0" encoding="utf-8"?><RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><per.wtt.ui.video.view.MovieRecorderViewandroid:id="@+id/moive_rv"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_centerHorizontal="true"android:layout_weight="1.0"></pers.wtt.ui.video.view.MovieRecorderView><LinearLayoutandroid:id="@+id/record_rl"android:layout_width="match_parent"android:layout_height="120dp"android:layout_below="@id/moive_rv"android:gravity="center_horizontal|center_vertical"android:orientation="horizontal"><Viewandroid:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1.0"></View><LinearLayoutandroid:id="@+id/ll_photo"android:layout_width="60dp"android:layout_height="wrap_content"android:gravity="center_horizontal"android:orientation="vertical"android:visibility="invisible"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="@dimen/margin_5"android:text="相册"android:textColor="#9f9f9e"android:textSize="@dimen/text_12sp"android:visibility="invisible"/><ImageViewandroid:id="@+id/iv_cvit_icon"android:layout_width="40dp"android:layout_height="40dp"android:background="@drawable/ic_album"/><TextViewandroid:id="@+id/tv_cvit_value"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textColor="@color/text_white"android:textSize="@dimen/text_16sp"android:visibility="gone"/><TextViewandroid:id="@+id/tv_cvit_name"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="@dimen/margin_5"android:text="相册"android:textColor="#9f9f9e"android:textSize="@dimen/text_12sp"/></LinearLayout><Viewandroid:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1.0"></View><Buttonandroid:id="@+id/start_btn"android:layout_width="80dp"android:layout_height="80dp"android:background="@drawable/btn_rec_bg"/><Viewandroid:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1.0"></View><Viewandroid:layout_width="60dp"android:layout_height="60dp"android:background="@drawable/ic_btn_rec"android:visibility="invisible"/><Viewandroid:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1.0"></View></LinearLayout></LinearLayout><ImageViewandroid:id="@+id/iv_close"android:layout_width="30dp"android:layout_height="30dp"android:src="@mipmap/close_w"android:layout_margin="@dimen/margin_10"/></RelativeLayout>
package pers.wtt.ui.video.fragment;import android.content.Intent;import android.net.Uri;import android.view.View;import android.widget.Button;import android.widget.ImageView;import android.widget.LinearLayout;import pers.wtt.R;import pers.wtt.base.BaseFragmentNew;import pers.wtt.ui.video.activity.VideoCoverActivity;import pers.wtt.ui.video.view.MovieRecorderView;import pers.wtt.lib_common.utils.LogUtil;import java.io.File;/*** Created by 王亭 on 2017/4/13.*/public class VideoRECFragment extends BaseFragmentNew implements View.OnClickListener{private MovieRecorderView movieRV;private Button startBtn;private LinearLayout ll_photo;private ImageView iv_close;public static VideoRECFragment getInstance(String title) {VideoRECFragment videoRECFragment = new VideoRECFragment();return videoRECFragment;}@Overridepublic int getLayoutID() {return R.layout.activity_video_rec;}@Overridepublic void initView() {initViews();initEvents();}/*    @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);*//*set it to be no title*//*requestWindowFeature(Window.FEATURE_NO_TITLE);*//*set it to be full screen*//*getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);setContentView(R.layout.activity_video_rec);initViews();initEvents();}*/private void initViews(){movieRV = (MovieRecorderView) view.findViewById(R.id.moive_rv);//录制startBtn = (Button)view.findViewById(R.id.start_btn);ll_photo = (LinearLayout) view.findViewById(R.id.ll_photo);iv_close = (ImageView) view.findViewById(R.id.iv_close);}private void initEvents(){//开始录制startBtn.setOnClickListener(this);ll_photo.setOnClickListener(this);iv_close.setOnClickListener(this);}public boolean isRec(){return movieRV.isRecord();}public void initRec(){movieRV.init();}public void stopRec(){LogUtil.e("stopRec");if(movieRV.isRecord()) {startBtn.setSelected(false);movieRV.stop();}}@Overridepublic void onPause() {super.onPause();LogUtil.e("onPause");if(movieRV.isRecord()) {movieRV.stop();}}@Overridepublic void onClick(View v) {switch (v.getId()){case R.id.start_btn:if(!movieRV.isRecord()) {startBtn.setSelected(true);movieRV.record(new MovieRecorderView.OnRecordFinishListener() {@Overridepublic void onRecordFinish(String absolutePath) {Intent intent = new Intent();intent.putExtra("filePath", absolutePath);toTarget(VideoCoverActivity.class, intent, true);getActivity().runOnUiThread(new Runnable() {@Overridepublic void run() {startBtn.setSelected(false);}});}});}else{startBtn.setSelected(false);movieRV.stop();String vecordFile = movieRV.getVideoPath();Intent scanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);scanIntent.setData(Uri.fromFile(new File(vecordFile)));getContext().sendBroadcast(scanIntent);if(vecordFile!=null){Intent intent = new Intent();intent.putExtra("filePath", vecordFile);toTarget(VideoCoverActivity.class, intent, true);}}break;case R.id.ll_photo:break;case R.id.iv_close:showInfoDialog("操作确认", "作品尚未保存,确认要取消吗?", new CallBack() {@Overridepublic void cancel() {}@Overridepublic void confirm() {getActivity().finish();}});break;}}}
视频录制后跳转到获取封面Activity
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center"><include layout="@layout/bar_title_back_rt" ></include><ImageViewandroid:id="@+id/iv_video_cover"android:layout_margin="@dimen/margin_10"android:layout_width="320dp"android:layout_height="240dp"android:layout_weight="1.0"android:scaleType="fitXY"/><android.support.v7.widget.RecyclerViewandroid:id="@+id/rv_video_cover"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginBottom="@dimen/margin_10"></android.support.v7.widget.RecyclerView></LinearLayout>
package pers.wtt.ui.video.activity;import android.content.Context;import android.content.Intent;import android.graphics.Bitmap;import android.graphics.ColorMatrix;import android.graphics.ColorMatrixColorFilter;import android.media.MediaMetadataRetriever;import android.os.Bundle;import android.support.annotation.Nullable;import android.support.v7.widget.LinearLayoutManager;import android.support.v7.widget.RecyclerView;import android.text.TextUtils;import android.view.KeyEvent;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.ImageView;import android.widget.TextView;import pers.wtt.R;import pers.wtt.base.BaseActivity;import pers.wtt.ui.interfaces.IRecyclerViewItemClick;import pers.wtt.utils.DisplayUtil;import java.util.ArrayList;import java.util.List;/*** Created by Administrator on 2017/4/18.*/public class VideoCoverActivity extends BaseActivity implements IRecyclerViewItemClick,View.OnClickListener {private ImageView iv_back;private TextView tv_title;private TextView tv_btn_name;private ImageView iv_video_cover;private RecyclerView rv_video_cover;private LinearLayoutManager linearLayoutManager;private VideoCoverAdapter videoCoverAdapter;private String filePath = "";@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_video_cover);iv_back = (ImageView) findViewById(R.id.iv_back);tv_title = (TextView) findViewById(R.id.tv_title);tv_btn_name = (TextView) findViewById(R.id.tv_btn_name);tv_btn_name.setVisibility(View.VISIBLE);tv_btn_name.setText(getResources().getString(R.string.next));tv_title.setText(getResources().getString(R.string.cover));iv_back.setOnClickListener(this);tv_btn_name.setOnClickListener(this);iv_video_cover = (ImageView) findViewById(R.id.iv_video_cover);rv_video_cover = (RecyclerView) findViewById(R.id.rv_video_cover);linearLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);rv_video_cover.setLayoutManager(linearLayoutManager);videoCoverAdapter = new VideoCoverAdapter(this);videoCoverAdapter.setItemClickListener(this);rv_video_cover.setAdapter(videoCoverAdapter);filePath = getIntent().getStringExtra("filePath");new Thread(){@Overridepublic void run() {super.run();MediaMetadataRetriever mmr = new MediaMetadataRetriever();mmr.setDataSource(filePath);String durationStr = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION); // 播放时长单位为毫秒int duration = 10 * 1000 * 1000;int current = duration/10;if(!TextUtils.isEmpty(durationStr)){duration = Integer.valueOf(durationStr)*1000;current = duration/10;}for (int i=0; i<10; i++){try {//                        final Bitmap bitmap = DisplayUtil.toGrayScale(mmr.getFrameAtTime(i*current, MediaMetadataRetriever.OPTION_CLOSEST));final Bitmap bitmap = mmr.getFrameAtTime(i*current, MediaMetadataRetriever.OPTION_CLOSEST);if(bitmap!=null) {if (i == 0) {runOnUiThread(new Runnable() {@Overridepublic void run() {if (iv_video_cover != null) {iv_video_cover.setImageBitmap(bitmap);iv_video_cover.setTag(bitmap);}}});}try {runOnUiThread(new Runnable() {@Overridepublic void run() {if (videoCoverAdapter != null) {videoCoverAdapter.addData(bitmap);videoCoverAdapter.notifyDataSetChanged();}}});Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}}} catch (Exception e) {e.printStackTrace();showToast("获取封面失败");finish();}}/*   Bitmap bitmap = mmr.getFrameAtTime();iv_video_cover.setImageBitmap(bitmap);System.out.println(mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DATE));System.out.println(mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_MIMETYPE));*/mmr.release();}}.start();}@Overridepublic void onItemClick(View view, Object obj) {Bitmap bitmap = (Bitmap)obj;iv_video_cover.setImageBitmap(bitmap);iv_video_cover.setTag(bitmap);}@Overridepublic void onDeleteClick(int position, Object obj) {}@Overridepublic void onClick(View v) {switch (v.getId()){case R.id.iv_back:showInfoDialog("操作确认", "作品尚未保存,确认要取消吗?", new CallBack() {@Overridepublic void cancel() {}@Overridepublic void confirm() {finish();}});break;case R.id.tv_btn_name:Bitmap bitmap = (Bitmap) iv_video_cover.getTag();String coverPath = DisplayUtil.saveBitmap(this, bitmap);if(!TextUtils.isEmpty(coverPath)) {Intent intent = new Intent();intent.putExtra("filePath", filePath);intent.putExtra("coverPath", coverPath);toTarget(VideoShareActivity.class, intent, true);}else{showToast("保存封面图片失败");}break;}}class VideoCoverAdapter extends RecyclerView.Adapter{private List<Bitmap> coverLst = null;private IRecyclerViewItemClick itemClickListener;private Context context;private int currentSel = 0;public VideoCoverAdapter(Context context) {super();this.context = context;this.coverLst = new ArrayList<Bitmap>();}public void setDatas(List<Bitmap> lst){coverLst = lst;}@Overridepublic RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {View view = LayoutInflater.from(context).inflate(R.layout.item_cover, parent, false);ConverViewHolder converViewHolder = new ConverViewHolder(view);return converViewHolder;}@Overridepublic void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {ConverViewHolder converViewHolder = (ConverViewHolder)holder;converViewHolder.itemView.setTag(coverLst.get(position));converViewHolder.iv_conver.setImageBitmap(coverLst.get(position));if(position==getCurrentSel()) {ColorMatrix cm = new ColorMatrix();cm.setSaturation(1); // 设置饱和度ColorMatrixColorFilter grayColorFilter = new ColorMatrixColorFilter(cm);converViewHolder.iv_conver.setColorFilter(grayColorFilter); // 如果想恢复彩色显示,设置为null即可int padding = DisplayUtil.dip2px(context, 2);converViewHolder.iv_conver.setPadding(padding,padding,padding,padding);converViewHolder.iv_status.setVisibility(View.VISIBLE);}else{converViewHolder.iv_conver.setPadding(0,0,0,0);converViewHolder.iv_status.setVisibility(View.GONE);converViewHolder.iv_conver.setColorFilter(null); // 如果想恢复彩色显示,设置为null即可}converViewHolder.itemView.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {setCurrentSel(position);notifyDataSetChanged();if(itemClickListener!=null){itemClickListener.onItemClick(v, v.getTag());}}});}@Overridepublic int getItemCount() {if(coverLst!=null){return coverLst.size();}return 0;}public void setItemClickListener(IRecyclerViewItemClick itemClickListener) {this.itemClickListener = itemClickListener;}public int getCurrentSel() {return currentSel;}public void setCurrentSel(int currentSel) {this.currentSel = currentSel;}public void addData(Bitmap bitmap) {coverLst.add(bitmap);}}class ConverViewHolder extends RecyclerView.ViewHolder{private ImageView iv_conver;private ImageView iv_status;public ConverViewHolder(View itemView) {super(itemView);iv_conver = (ImageView) itemView.findViewById(R.id.iv_cover);iv_status = (ImageView) itemView.findViewById(R.id.iv_status);}}@Overridepublic boolean onKeyDown(int keyCode, KeyEvent event) {if(keyCode==KeyEvent.KEYCODE_BACK){showInfoDialog("操作确认", "作品尚未保存,确认要取消吗?", new CallBack() {@Overridepublic void cancel() {}@Overridepublic void confirm() {finish();}});return true;}return super.onKeyDown(keyCode, event);}}
最新布局,上面是ImageView 下面按钮是自定义View。录制按钮 ,实现。
package pers.wtt.videocorder.view;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.RectF;import android.os.Handler;import android.os.Looper;import android.os.Message;import android.support.annotation.Nullable;import android.support.annotation.Px;import android.util.AttributeSet;import android.view.View;import com.orhanobut.logger.Logger;import java.util.Timer;import java.util.TimerTask;import pers.wtt.videocorder.interfaces.OnCustomCirleListener;/*** Created by 王亭 on 2017/12/18.*/public class CustomCircleButton extends View implements View.OnClickListener{private int mWidth;private int mHeight;//中心圆画笔private Paint mCirclePaint;//默认灰色描边/进度画笔private Paint mDefStrokePaint;//描边/进度画笔private Paint mStrokePaint;//按钮状态private Status currentStatus = Status.NORMAL;//总进度private int maxProgress = 360;//当前进度private int currentProgress = 0;//完成时间private int mComplete = 10;//定时器private Timer timer;//定时任务private TimerTask timerTask;//中心圆颜色private int mCircleColor = Color.parseColor("#129aee");//边框颜色private int mStrokeColor = Color.parseColor("#eeeeee");//进度颜色private int mProgressColor = Color.parseColor("#129aee");//进度完成接口private OnCustomCirleListener onCustomCirleListener;//更新UIprivate Handler handler = new Handler(Looper.getMainLooper()){@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);invalidate();}};public void setOnCustomCirleListener(OnCustomCirleListener onCustomCirleListener) {this.onCustomCirleListener = onCustomCirleListener;}public int getmCircleColor() {return mCircleColor;}public void setmCircleColor(int mCircleColor) {this.mCircleColor = mCircleColor;}public int getmStrokeColor() {return mStrokeColor;}public void setmStrokeColor(int mStrokeColor) {this.mStrokeColor = mStrokeColor;}public int getmProgressColor() {return mProgressColor;}public void setmProgressColor(int mProgressColor) {this.mProgressColor = mProgressColor;}public int getmComplete() {return mComplete;}/*** 设置进度完成时间* @param mComplete 单位秒*/public void setmComplete(int mComplete) {this.mComplete = mComplete;}/*** 10秒为例 360度/10+1秒=36度每秒 1000毫秒/36度每秒≈28毫秒每度* 从0s开始的所以需要+1秒否则会少一秒* @return*/private int getSpeed(){return Math.round(1000.00f/(360.00f/(mComplete+1)));}private void startTimer(){stopTimer();timer = new Timer();timerTask = new TimerTask() {@Overridepublic void run() {if(currentProgress>maxProgress){currentStatus = Status.NORMAL;currentProgress = 0;stopTimer();if(onCustomCirleListener!=null)onCustomCirleListener.complete();handler.sendEmptyMessage(0);}else{currentProgress+=1;handler.sendEmptyMessage(0);}}};timer.schedule(timerTask, 0,getSpeed());}private void stopTimer(){if(timer!=null){timer.cancel();timer =null;}if(timerTask!=null){timerTask.cancel();timerTask=null;}}public enum Status{START,//开始STOP,//暂停NORMAL//停止}public CustomCircleButton(Context context) {this(context, null);}public CustomCircleButton(Context context, @Nullable AttributeSet attrs) {this(context, attrs, 0);}public CustomCircleButton(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);mCirclePaint = new Paint();mCirclePaint.setAntiAlias(true);//消除锯齿mCirclePaint.setColor(mCircleColor);mDefStrokePaint = new Paint();mDefStrokePaint.setStyle(Paint.Style.STROKE);mDefStrokePaint.setAntiAlias(true);//消除锯齿mDefStrokePaint.setColor(mStrokeColor);mDefStrokePaint.setStrokeWidth(20);mStrokePaint = new Paint();mStrokePaint.setStyle(Paint.Style.STROKE);mStrokePaint.setAntiAlias(true);//消除锯齿mStrokePaint.setColor(mProgressColor);mStrokePaint.setStrokeWidth(10);setOnClickListener(this);}@Overridepublic void layout(@Px int l, @Px int t, @Px int r, @Px int b) {super.layout(l, t, r, b);}@Overrideprotected void onDraw(Canvas canvas) {int r = getWidth() / 2;//也可以是getMeasuredHeight()/2,本例中我们已经将宽高设置相等了//圆心的横坐标为当前的View的左边起始位置+半径int centerX = r;//圆心的纵坐标为当前的View的顶部起始位置+半径int centerY = r;if(currentStatus==Status.NORMAL) {canvas.drawCircle(centerX, centerY, r - mDefStrokePaint.getStrokeWidth(), mCirclePaint);canvas.drawCircle(centerX, centerY, r - mDefStrokePaint.getStrokeWidth(), mDefStrokePaint);}else{canvas.drawCircle(centerX, centerY, r - mDefStrokePaint.getStrokeWidth(), mCirclePaint);canvas.drawCircle(centerX, centerY, r - mDefStrokePaint.getStrokeWidth(), mDefStrokePaint);RectF oval  = new RectF();oval.set(15,15,r*2 - 15,r * 2 - 15);canvas.drawArc(oval, -90, ((float) currentProgress / maxProgress) * 360, false, mStrokePaint);}super.onDraw(canvas);}@Overrideprotected void onLayout(boolean changed, int left, int top, int right, int bottom) {super.onLayout(changed, left, top, right, bottom);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {setMeasuredDimension(mWidth, mHeight);} else if (widthSpecMode == MeasureSpec.AT_MOST) {setMeasuredDimension(mWidth, heightSpecSize);} else if (heightSpecMode == MeasureSpec.AT_MOST) {setMeasuredDimension(widthSpecSize, mHeight);} else {super.onMeasure(widthMeasureSpec, heightMeasureSpec);}}@Overridepublic void onClick(View v) {Logger.e("CustomCircleButton Click!");if(currentStatus==Status.START) {currentStatus = Status.STOP;stopTimer();}else if(currentStatus==Status.STOP){currentStatus = Status.START;startTimer();}else{currentStatus = Status.START;startTimer();}invalidate();onCustomCirleListener.onClick(v);}}

                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息