java制作仿微信錄制小視頻控件
本文為用 MediaRecorder 錄制小視頻的控件,可以設(shè)置視頻錄制的時(shí)間、空間大小、初始時(shí)是否打開攝像頭等。 此控件為組合控件,繼承自 LinearLayout ,為防止出錯(cuò),需實(shí)現(xiàn) android.media.MediaRecorder.OnErrorListener 接口。
小視頻錄制界面

MovieRecorderView.java
import java.io.File;
import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;
import android.content.Context;
import android.content.res.TypedArray;
import android.hardware.Camera;
import android.hardware.Camera.Parameters;
import android.media.MediaRecorder;
import android.media.MediaRecorder.AudioEncoder;
import android.media.MediaRecorder.AudioSource;
import android.media.MediaRecorder.OnErrorListener;
import android.media.MediaRecorder.OutputFormat;
import android.media.MediaRecorder.VideoEncoder;
import android.media.MediaRecorder.VideoSource;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import com.contron.dgyj.R;
import com.contron.dgyj.common.Globals;
import com.contron.dgyj.im.ImGlobal;
import com.lidroid.xutils.util.LogUtils;
/**
* 視頻播放控件
*
* @author liuyinjun
*
* @date 2015-2-5
*/
public class MovieRecorderView extends LinearLayout implements OnErrorListener {
private SurfaceView mSurfaceView;
private SurfaceHolder mSurfaceHolder;
private ProgressBar mProgressBar;
private MediaRecorder mMediaRecorder;
private Camera mCamera;
private Timer mTimer;// 計(jì)時(shí)器
private OnRecordFinishListener mOnRecordFinishListener;// 錄制完成回調(diào)接口
private int mWidth;// 視頻分辨率寬度
private int mHeight;// 視頻分辨率高度
private boolean isOpenCamera;// 是否一開始就打開攝像頭
private int mRecordMaxTime;// 一次拍攝最長時(shí)間
private int mTimeCount;// 時(shí)間計(jì)數(shù)
private File mVecordFile = null;// 文件
public MovieRecorderView(Context context) {
this(context, null);
}
public MovieRecorderView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MovieRecorderView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MovieRecorderView, defStyle, 0);
mWidth = a.getInteger(R.styleable.MovieRecorderView_width, 320);// 默認(rèn)320
mHeight = a.getInteger(R.styleable.MovieRecorderView_height, 240);// 默認(rèn)240
isOpenCamera = a.getBoolean(R.styleable.MovieRecorderView_is_open_camera, true);// 默認(rèn)打開
mRecordMaxTime = a.getInteger(R.styleable.MovieRecorderView_record_max_time, 10);// 默認(rèn)為10
LayoutInflater.from(context).inflate(R.layout.movie_recorder_view, this);
mSurfaceView = (SurfaceView) findViewById(R.id.surfaceview);
mProgressBar = (ProgressBar) findViewById(R.id.progressBar);
mProgressBar.setMax(mRecordMaxTime);// 設(shè)置進(jìn)度條最大量
mSurfaceHolder = mSurfaceView.getHolder();
mSurfaceHolder.addCallback(new CustomCallBack());
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
a.recycle();
}
/**
*
* @author liuyinjun
*
* @date 2015-2-5
*/
private class CustomCallBack implements Callback {
@Override
public void surfaceCreated(SurfaceHolder holder) {
if (!isOpenCamera)
return;
try {
initCamera();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (!isOpenCamera)
return;
freeCameraResource();
}
}
/**
* 初始化攝像頭
*
* @author liuyinjun
* @date 2015-2-5
* @throws IOException
*/
private void initCamera() throws IOException {
if (mCamera != null) {
freeCameraResource();
}
try {
mCamera = Camera.open();
} catch (Exception e) {
e.printStackTrace();
freeCameraResource();
}
if (mCamera == null)
return;
setCameraParams();
mCamera.setDisplayOrientation(90);
mCamera.setPreviewDisplay(mSurfaceHolder);
mCamera.startPreview();
mCamera.unlock();
}
/**
* 設(shè)置攝像頭為豎屏
*
* @author liuyinjun
* @date 2015-2-5
*/
private void setCameraParams() {
if (mCamera != null) {
Parameters params = mCamera.getParameters();
params.set("orientation", "portrait");
mCamera.setParameters(params);
}
}
/**
* 釋放攝像頭資源
*
* @author liuyinjun
* @date 2015-2-5
*/
private void freeCameraResource() {
if (mCamera != null) {
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
mCamera.lock();
mCamera.release();
mCamera = null;
}
}
private void createRecordDir() {
File sampleDir = new File(Environment.getExternalStorageDirectory() + File.separator + "im/video/");
if (!sampleDir.exists()) {
sampleDir.mkdirs();
}
File vecordDir = sampleDir;
// 創(chuàng)建文件
try {
mVecordFile = File.createTempFile("recording", ".mp4", vecordDir);//mp4格式
LogUtils.i(mVecordFile.getAbsolutePath());
} catch (IOException e) {
}
}
/**
* 初始化
*
* @author liuyinjun
* @date 2015-2-5
* @throws IOException
*/
private void initRecord() throws IOException {
mMediaRecorder = new MediaRecorder();
mMediaRecorder.reset();
if (mCamera != null)
mMediaRecorder.setCamera(mCamera);
mMediaRecorder.setOnErrorListener(this);
mMediaRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
mMediaRecorder.setVideoSource(VideoSource.CAMERA);// 視頻源
mMediaRecorder.setAudioSource(AudioSource.MIC);// 音頻源
mMediaRecorder.setOutputFormat(OutputFormat.MPEG_4);// 視頻輸出格式
mMediaRecorder.setAudioEncoder(AudioEncoder.AMR_NB);// 音頻格式
mMediaRecorder.setVideoSize(mWidth, mHeight);// 設(shè)置分辨率:
// mMediaRecorder.setVideoFrameRate(16);// 這個(gè)我把它去掉了,感覺沒什么用
mMediaRecorder.setVideoEncodingBitRate(1 * 1024 * 512);// 設(shè)置幀頻率,然后就清晰了
mMediaRecorder.setOrientationHint(90);// 輸出旋轉(zhuǎn)90度,保持豎屏錄制
mMediaRecorder.setVideoEncoder(VideoEncoder.MPEG_4_SP);// 視頻錄制格式
// mediaRecorder.setMaxDuration(Constant.MAXVEDIOTIME * 1000);
mMediaRecorder.setOutputFile(mVecordFile.getAbsolutePath());
mMediaRecorder.prepare();
try {
mMediaRecorder.start();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (RuntimeException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 開始錄制視頻
*
* @author liuyinjun
* @date 2015-2-5
* @param fileName
* 視頻儲存位置
* @param onRecordFinishListener
* 達(dá)到指定時(shí)間之后回調(diào)接口
*/
public void record(final OnRecordFinishListener onRecordFinishListener) {
this.mOnRecordFinishListener = onRecordFinishListener;
createRecordDir();
try {
if (!isOpenCamera)// 如果未打開攝像頭,則打開
initCamera();
initRecord();
mTimeCount = 0;// 時(shí)間計(jì)數(shù)器重新賦值
mTimer = new Timer();
mTimer.schedule(new TimerTask() {
@Override
public void run() {
// TODO Auto-generated method stub
mTimeCount++;
mProgressBar.setProgress(mTimeCount);// 設(shè)置進(jìn)度條
if (mTimeCount == mRecordMaxTime) {// 達(dá)到指定時(shí)間,停止拍攝
stop();
if (mOnRecordFinishListener != null)
mOnRecordFinishListener.onRecordFinish();
}
}
}, 0, 1000);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 停止拍攝
*
* @author liuyinjun
* @date 2015-2-5
*/
public void stop() {
stopRecord();
releaseRecord();
freeCameraResource();
}
/**
* 停止錄制
*
* @author liuyinjun
* @date 2015-2-5
*/
public void stopRecord() {
mProgressBar.setProgress(0);
if (mTimer != null)
mTimer.cancel();
if (mMediaRecorder != null) {
// 設(shè)置后不會(huì)崩
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 liuyinjun
* @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 File getmVecordFile() {
return mVecordFile;
}
/**
* 錄制完成回調(diào)接口
*
* @author liuyinjun
*
* @date 2015-2-5
*/
public interface OnRecordFinishListener {
public void onRecordFinish();
}
@Override
public void onError(MediaRecorder mr, int what, int extra) {
try {
if (mr != null)
mr.reset();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
movie_recorder_view.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:background="@android:color/background_dark"
android:orientation="vertical">
<SurfaceView
android:id="@+id/surfaceview"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1"
/>
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="2dp"
/>
</LinearLayout>
attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:background="@android:color/background_dark"
android:orientation="vertical">
<SurfaceView
android:id="@+id/surfaceview"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1"
/>
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="2dp"
/>
</LinearLayout>
以上所述就是本文的全部內(nèi)容了,希望大家能夠喜歡。
相關(guān)文章
MQ的分類組成優(yōu)缺點(diǎn)測試點(diǎn)入門教程
這篇文章主要為大家介紹了MQ的分類組成優(yōu)缺點(diǎn)測試點(diǎn)入門教程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05
Java多線程并發(fā)編程(互斥鎖Reentrant Lock)
這篇文章主要介紹了ReentrantLock 互斥鎖,在同一時(shí)間只能被一個(gè)線程所占有,在被持有后并未釋放之前,其他線程若想獲得該鎖只能等待或放棄,需要的朋友可以參考下2017-05-05
SpringCloud環(huán)境搭建過程之Rest使用小結(jié)
這篇文章主要介紹了SpringCloud環(huán)境搭建之Rest使用,本文通過實(shí)例代碼圖文相結(jié)合給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-08-08
SpringBoot用實(shí)體接收Get請求傳遞過來的多個(gè)參數(shù)的兩種方式
本文主要介紹SpringBoot用實(shí)體接收Get請求傳遞過來的多個(gè)參數(shù),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04
使用IDEA啟動(dòng)項(xiàng)目遇見ClassNotFoundException的解決方案
這篇文章主要介紹了使用IDEA啟動(dòng)項(xiàng)目遇見ClassNotFoundException的正確解決方案,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-06-06
MultipartResolver實(shí)現(xiàn)文件上傳功能
這篇文章主要為大家詳細(xì)介紹了MultipartResolver實(shí)現(xiàn)文件上傳功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-06-06

