編輯:關於Android編程
實現類似微信的小視頻功能,可以錄制一個視頻然後播放該視頻。
視頻錄制,使用一個自定義控件。
/**
* 視頻錄制控件
*
* @author lip
*
* @date 2015-3-16
*/
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;// 計時器
private OnRecordFinishListener mOnRecordFinishListener;// 錄制完成回調接口
private int mWidth;// 視頻分辨率寬度
private int mHeight;// 視頻分辨率高度
private boolean isOpenCamera;// 是否一開始就打開攝像頭
private int mRecordMaxTime;// 一次拍攝最長時間
private int mTimeCount;// 時間計數
private File mVecordFile = null;// 文件
public MovieRecorderView(Context context) {
this(context, null);
}
public MovieRecorderView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public MovieRecorderView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mWidth=320;
mHeight=240;
isOpenCamera=true;
mRecordMaxTime=10;
LayoutInflater.from(context).inflate(R.layout.moive_recorder_view, this);
mSurfaceView = (SurfaceView) findViewById(R.id.surfaceview);
mProgressBar = (ProgressBar) findViewById(R.id.progressBar);
mProgressBar.setMax(mRecordMaxTime);// 設置進度條最大量
mSurfaceHolder = mSurfaceView.getHolder();
mSurfaceHolder.addCallback(new CustomCallBack());
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
/**
*
* @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 lip
* @date 2015-3-16
* @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();
}
/**
* 設置攝像頭為豎屏
*
* @author lip
* @date 2015-3-16
*/
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/");
File sampleDir = new File(Environment.getExternalStorageDirectory() + File.separator+"RecordVideo/");
//File sampleDir = new File("/video/");
if (!sampleDir.exists()) {
sampleDir.mkdirs();
}
File vecordDir = sampleDir;
// 創建文件
try {
mVecordFile = File.createTempFile("recording", ".mp4", vecordDir);//mp4格式
//LogUtils.i(mVecordFile.getAbsolutePath());
Log.d("Path:",mVecordFile.getAbsolutePath());
} catch (IOException e) {
}
}
/**
* 初始化
*
* @author lip
* @date 2015-3-16
* @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);// 設置分辨率:
// mMediaRecorder.setVideoFrameRate(16);// 這個我把它去掉了,感覺沒什麼用
mMediaRecorder.setVideoEncodingBitRate(1 * 1024 * 512);// 設置幀頻率,然後就清晰了
mMediaRecorder.setOrientationHint(90);// 輸出旋轉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
* 達到指定時間之後回調接口
*/
public void record(final OnRecordFinishListener onRecordFinishListener) {
this.mOnRecordFinishListener = onRecordFinishListener;
createRecordDir();
try {
if (!isOpenCamera)// 如果未打開攝像頭,則打開
initCamera();
initRecord();
mTimeCount = 0;// 時間計數器重新賦值
mTimer = new Timer();
mTimer.schedule(new TimerTask() {
@Override
public void run() {
// TODO Auto-generated method stub
mTimeCount++;
mProgressBar.setProgress(mTimeCount);// 設置進度條
if (mTimeCount == mRecordMaxTime) {// 達到指定時間,停止拍攝
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) {
// 設置後不會崩
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;
}
/**
* 錄制完成回調接口
*
* @author lip
*
* @date 2015-3-16
*/
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();
}
}
}
視頻播放:
public class MainActivity extends ActionBarActivity {
private MovieRecorderView movieRV;
private Button startBtn;
private Button stopBtn;
private Button playBtn;
private Button pauseBtn;
private SurfaceView playView;
private MediaPlayer player;
int position;
public MainActivity() {
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
initEvents();
init();
}
private void init()
{
player=new MediaPlayer();
playView=(SurfaceView) this.findViewById(R.id.play_surfaceV);
//設置SurfaceView自己不管理的緩沖區
playView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
playView.getHolder().addCallback(new Callback() {
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
if (position>0) {
try {
//開始播放
play();
//並直接從指定位置開始播放
player.seekTo(position);
position=0;
} catch (Exception e) {
// TODO: handle exception
}
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
});
}
private void initViews()
{
movieRV=(MovieRecorderView)findViewById(R.id.moive_rv);
//錄制
startBtn=(Button)findViewById(R.id.start_btn);
stopBtn=(Button)findViewById(R.id.stop_btn);
//播放
playBtn=(Button)findViewById(R.id.play_btn);
pauseBtn=(Button)findViewById(R.id.pause_btn);
}
private void initEvents()
{
//開始錄制
startBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
movieRV.record(new MovieRecorderView.OnRecordFinishListener() {
@Override
public void onRecordFinish() {
}
});
}
});
//停止錄制
stopBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
movieRV.stop();
}
});
//播放已錄制視頻
playBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
play();
}
});
//暫停
pauseBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(player.isPlaying())
{
player.pause();
}
else
{
player.start();
}
}
});
}
@Override
protected void onPause() {
//先判斷是否正在播放
if (player.isPlaying()) {
//如果正在播放我們就先保存這個播放位置
position=player.getCurrentPosition()
;
player.stop();
}
super.onPause();
}
private void play()
{
try {
Log.d("play:","");
player.reset();
player.setAudioStreamType(AudioManager.STREAM_MUSIC);
//設置需要播放的視頻
String path=movieRV.getmVecordFile().getAbsolutePath();
player.setDataSource(path);
Log.d("play:",path);
//把視頻畫面輸出到SurfaceView
player.setDisplay(playView.getHolder());
player.prepare();
//播放
player.start();
} catch (Exception e) {
// TODO: handle exception
}
}
}
布局文件:
需要的系統權限:
基本的視頻錄制功能就可以實現了。
下載地址:android仿微信視頻錄制播放
Android最佳性能實踐(一)——合理管理內存
有不少朋友都問過我,怎樣才能寫出高性能的應用程序,如何避免程序出現OOM,或者當程序內存占用過高的時候該怎麼樣去排查。確實,一個優秀的應用程序,不僅僅要功能完成得好,性能
Android UI體驗之全屏沉浸式透明狀態欄樣式
前言: Android 4.4之後谷歌提供了沉浸式全屏體驗, 在沉浸式全屏模式下, 狀態欄、 虛擬按鍵動態隱藏, 應用可以使用完整的屏幕空間, 按
Android開發--滑動側邊欄的實現
在Android應用開發中,滑動側邊欄經常使用,今天我也試著自己進行了一個簡單的實踐,雖然功能還不是很強大,但是可以保留下來為以後的開發使用,有需要時在進行簡單的修改。實
android-----事件分發機制測試系列
先來說說我遇到的問題,這次測試使用的布局文件是: 也就是說布局圖是醬紫的:具體就是我在MyRelativeLayo