Android學習筆記——簡單實現照相、錄音和錄影功能
阿新 • • 發佈:2019-02-05
Android菜鳥第一次寫原創部落格,大神請輕噴,共同進步。
最近剛接觸到如何實現一個簡單的照相機功能,然後又將錄音和錄影功能加了進去。ps:錄影功能相對複雜,自己實現起來比較困難,我就直接在程式裡呼叫系統的錄影功能了。以後在慢慢學習:)好,言歸正傳。
1、首先新建一個照相機專案;
2、我先新建了一個BaseActivity,用於設定相機螢幕全屏和獲取螢幕寬高畫素等功能;
程式碼:
3、新建一個照相的Activity(名叫PhotographActivity)繼承了BaseActivity,同時實現了OnClickListener, SurfaceHolder.Callback, PictureCallback三個介面。這時eclipse會自動提示必須實現onClick(View v)、surfaceCreated(SurfaceHolder holder)、surfaceChanged(SurfaceHolder holder, int format, int width, int height)、surfaceDestroyed(SurfaceHolder holder)、onPictureTaken(byte[] data, Camera camera)這四個方法,具體功能請看程式碼註釋;package xw.phonemanager.activity; import android.app.Activity; import android.os.Bundle; import android.util.DisplayMetrics; import android.view.Window; import android.view.WindowManager; public class BaseActivity extends Activity { public static float screenWidth, screenHeigth; // private ProgressDialog mDialog; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setFullScreen(); // 設定全屏 getScreen(); //獲取螢幕寬高值 } // 設定全屏 private void setFullScreen() { requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); } //獲取螢幕寬高值 private void getScreen() { DisplayMetrics metrics = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(metrics); screenWidth = metrics.widthPixels; screenHeigth = metrics.heightPixels; } // public void showProgressDialog(String message){ // } }
4、新建一個佈局檔案photograph.xml,用於顯示開啟相機後預覽狀態螢幕上顯示的東西,和常見的相機介面差不多,如圖:
xml程式碼:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <SurfaceView android:id="@+id/camera" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" /> <SeekBar android:id="@+id/seekbar_focal" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_marginLeft="20dp" android:layout_alignParentRight="true" android:layout_marginRight="60dp" android:layout_alignParentTop="true" android:layout_marginTop="15dp" /> <ImageView android:id="@+id/back_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_marginBottom="10dp" android:layout_alignParentRight="true" android:layout_marginRight="10dp" android:src="@drawable/back" /> <ImageView android:id="@+id/flash_switch" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_marginLeft="15dp" android:layout_alignParentBottom="true" android:layout_marginBottom="10dp" android:src="@drawable/light_auto" /> <ImageView android:id="@+id/switch_camera" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignLeft="@+id/shot_btn" android:layout_alignTop="@+id/seekbar_focal" android:src="@drawable/showall" android:visibility="invisible"/> <ImageView android:id="@+id/shot_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_marginRight="10dp" android:layout_centerVertical="true" android:src="@drawable/photo" /> <Button android:id="@+id/record_start_btn" style="?android:attr/buttonStyleSmall" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_marginBottom="10dp" android:layout_toRightOf="@id/flash_switch" android:layout_marginLeft="120dp" android:text="@string/record_start" /> <Button android:id="@+id/record_stop_btn" style="?android:attr/buttonStyleSmall" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_marginBottom="10dp" android:layout_toRightOf="@id/flash_switch" android:layout_marginLeft="120dp" android:text="@string/record_stop" android:visibility="invisible" /> <Button android:id="@+id/video_btn" style="?android:attr/buttonStyleSmall" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_marginBottom="10dp" android:layout_marginLeft="73dp" android:layout_toLeftOf="@id/back_btn" android:layout_marginRight="100dp" android:text="@string/video" /> </RelativeLayout>
5、開始實現相機、錄音和錄影功能,請看程式碼和註釋:
package xw.activity.photograph;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Calendar;
import java.util.Locale;
import xw.phonemanager.activity.BaseActivity;
import xw.phonemanager.activity.MenuActivity;
import xw.phonemanager.activity.R;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.PictureCallback;
import android.media.MediaRecorder;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.text.format.DateFormat;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.Toast;
public class PhotographActivity extends BaseActivity implements
OnClickListener, SurfaceHolder.Callback, PictureCallback {
SurfaceView cameraPrew;// 預覽圖物件
ImageView shot;
ImageView flash;
ImageView back;
ImageView switchCamera;
Button recordStart;
Button recordStop;
Button VCR;
SeekBar focus;
Camera camera;// 相機物件
Camera.Parameters parameters;// 相機引數物件
MediaRecorder mRecorder; // MediaRecorder物件,用於實現錄音和錄影
private int cameraNum; // 攝像頭的數量
private int cameraID;// 攝像頭的編號
private boolean canSwitchCam = false;
private boolean noSD;
private int maxZoom; // 最大變焦
private String recFileName; // 錄音音訊檔名稱
// 設定了兩個判斷標識,當處於錄音狀態時不允許拍照或錄影,均置為false
private boolean isPhoto = true; // 是否可以照相
private boolean isVideo = true; // 是否可以錄影
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.photograph);
Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);// 螢幕高亮
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);// 強制橫屏
getWindow().setFormat(PixelFormat.TRANSPARENT);// 螢幕半透
init();// 初始化
}
private void init() {
checkSD();// 首先檢測SD卡是否存在或可用,不可用則提示使用者,避免浪費表情的尷尬:D
cameraInit();
prewInit();
buttonInit();
zoomInit();
seekBarInit();
}
/**
* 檢查是否有SD卡
*/
private void checkSD() {
String SDtatus = Environment.getExternalStorageState();
if (SDtatus == Environment.MEDIA_MOUNTED) {
noSD = true;
Toast.makeText(PhotographActivity.this, "沒有SD卡或SD卡不可用",
Toast.LENGTH_LONG).show();
} else {
noSD = false;
}
}
/**
* 相機初始化
*/
private void cameraInit() {
cameraNum = Camera.getNumberOfCameras();// 攝像頭的個數
if (camera != null) { // 若不為空,釋放資源重新獲取
camera.release();
}
camera = Camera.open(); // 開啟攝像頭
// 檢測手機的攝像頭個數,若不止一個則顯示切換攝像頭的圖示,canSwitchCam置為true
switchCamera = (ImageView) findViewById(R.id.switch_camera);
if (cameraNum > 1) {
switchCamera.setVisibility(View.VISIBLE);
canSwitchCam = true;
cameraID = 0;// 當前的攝像頭ID設為0
}
// 相機例項獲得相機引數集
parameters = camera.getParameters();
}
/**
* 預覽初始化
*/
private void prewInit() {
cameraPrew = (SurfaceView) findViewById(R.id.camera);// 繫結SurfaceView並例項化
cameraPrew.getHolder().setFixedSize((int) BaseActivity.screenWidth,
(int) BaseActivity.screenHeigth);
cameraPrew.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
cameraPrew.getHolder().addCallback(this);
// 給整個螢幕的SurfaceView設定一個觸控監聽,實現全屏手動對焦
cameraPrew.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
camera.autoFocus(null);// 點選螢幕即可對焦
return false;
}
});
}
/**
* 按鍵初始化,設定按鍵監聽
*/
private void buttonInit() {
shot = (ImageView) findViewById(R.id.shot_btn);// 拍照鍵
flash = (ImageView) findViewById(R.id.flash_switch);// 切換閃光燈鍵
back = (ImageView) findViewById(R.id.back_btn);// 返回鍵
recordStart = (Button) findViewById(R.id.record_start_btn);// 開始錄音鍵
recordStop = (Button) findViewById(R.id.record_stop_btn);// 結束錄音鍵
VCR = (Button) findViewById(R.id.video_btn);// 開始錄影鍵
// 設定監聽
shot.setOnClickListener(this);
flash.setOnClickListener(this);
back.setOnClickListener(this);
switchCamera.setOnClickListener(this);
recordStart.setOnClickListener(this);
recordStop.setOnClickListener(this);
VCR.setOnClickListener(this);
}
/**
* 滑動條實現變焦,初始化
*/
private void seekBarInit() {
focus = (SeekBar) findViewById(R.id.seekbar_focal);
focus.setProgress(0);
focus.setMax(maxZoom);// 滑動條的最大值設為獲得的相機可變焦值的最大值
focus.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
zoomChanged(progress);// 傳遞當前進度,變焦
}
});
}
private void zoomInit() {
maxZoom = parameters.getMaxZoom();// 獲得相機最大變焦範圍
}
/**
* 按鍵監聽
*/
@Override
public void onClick(View v) {
int id = v.getId();
switch (id) {
case R.id.shot_btn:
if (isPhoto == true) {
takPicture();
} else {
Toast.makeText(this, "無響應,正在錄音", Toast.LENGTH_SHORT).show();
}
break;
case R.id.flash_switch:
flashSwitch();
break;
case R.id.back_btn:
startActivity(new Intent(PhotographActivity.this,
MenuActivity.class));
finish();
break;
// 切換攝像頭
case R.id.switch_camera:
if (canSwitchCam == true) {// 至少有兩個才能切換
if (camera != null) {
camera.stopPreview();
camera.release();
Camera.open(++cameraID % cameraNum);// 在id:0~cameraNum之間切換
}
}
break;
case R.id.record_start_btn:
if (noSD == true) { // 沒有SD卡
Toast.makeText(PhotographActivity.this, "沒有SD卡或SD卡不可用",
Toast.LENGTH_LONG).show();
return;
} else {
startRec();// 開始錄音
}
break;
case R.id.record_stop_btn:
stopRec();// 結束錄音
break;
case R.id.video_btn:
if (isVideo == true) {
startVideo();// 開始錄影
} else {
Toast.makeText(this, "無響應,正在錄音", Toast.LENGTH_SHORT).show();
}
break;
}
}
/**
* 切換閃光燈狀態,如果當前是開狀態,點選切換為關狀態, 再點選切換為自動狀態,再點選又到開狀態
*/
private void flashSwitch() {
if (parameters.getFlashMode() == Parameters.FLASH_MODE_AUTO) {
flash.setImageResource(R.drawable.light_on);
parameters.setFlashMode(Parameters.FLASH_MODE_ON);
camera.setParameters(parameters);
} else if (parameters.getFlashMode() == Parameters.FLASH_MODE_ON) {
flash.setImageResource(R.drawable.light_off);
parameters.setFlashMode(Parameters.FLASH_MODE_OFF);
camera.setParameters(parameters);
} else {
flash.setImageResource(R.drawable.light_auto);
parameters.setFlashMode(Parameters.FLASH_MODE_AUTO);
camera.setParameters(parameters);
}
}
// 變焦
private void zoomChanged(int zoom) {
parameters.setZoom(zoom);
camera.setParameters(parameters);
}
// 照像
public void takPicture() {
camera.autoFocus(null);
camera.takePicture(null, null, this);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
try {
camera.setPreviewDisplay(cameraPrew.getHolder());// 獲取相機預覽
} catch (IOException e) {
e.printStackTrace();
}
camera.getParameters();// 獲取相機引數例項
camera.startPreview();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
/**
* 退出相機介面後,釋放相機相關資源
*/
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (camera != null) {
camera.stopPreview();
camera.release();
camera = null;
}
}
/**
* 拍照並儲存,自動跳轉到檢視介面
*/
@Override
public void onPictureTaken(byte[] data, Camera camera) {
if (noSD == true) { // 沒有SD卡
Toast.makeText(PhotographActivity.this, "沒有SD卡或SD卡不可用",
Toast.LENGTH_LONG).show();
return;
} else {
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
String name = DateFormat.format("yyyyMMdd_hhmmss",
Calendar.getInstance(Locale.CHINA))
+ ".jpg";// 自定義照片名字和格式
File file = new File("sdcard/TianTian/photo/");
file.mkdirs(); // 建立目錄
String fileName = "/mnt/sdcard/TianTian/photo/" + name;// 照片檔案的絕對路徑
BufferedOutputStream bos = null;
try {
bos = new BufferedOutputStream(new FileOutputStream(fileName));
bitmap.compress(Bitmap.CompressFormat.JPEG, 80, bos);// 通過緩衝流存入
// 拍完之後跳轉到檢視剛拍的照片的一個Activity(ConfirmActivity),使用者可以選擇是否保留圖片
ConfirmActivity.photoName = fileName;
Uri imgUri = Uri.fromFile(new File(fileName));
Intent intent = new Intent();
intent.setClass(PhotographActivity.this, ConfirmActivity.class);
intent.setData(imgUri);
this.camera.stopPreview();// 停止預覽
startActivity(intent);
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
try {
bos.close();// 關閉流
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
// 開始錄音
private void startRec() {
// 錄音時不能拍照和錄影
isPhoto = false;
isVideo = false;
mRecorder = new MediaRecorder();
mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);// 設定錄音員,從MIC獲得聲音
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);// 設定音訊輸出格式
mRecorder.setAudioEncoder(MediaRecorder.VideoEncoder.DEFAULT);// 設定編碼格式
String path = Environment.getExternalStorageDirectory().getPath();
String name = DateFormat.format("yyyyMMdd_hhmmss",
Calendar.getInstance(Locale.CHINA))
+ ".3gp";
File file = new File(path);
file.mkdirs();
recFileName = path + "/" + name;
file = new File(recFileName);
if (!file.exists()) {
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
mRecorder.setOutputFile(recFileName);
try {
mRecorder.prepare();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
mRecorder.start(); // 錄音開始
Toast.makeText(this, "錄音開始", Toast.LENGTH_SHORT).show();
// “開始錄音”後,將按鈕換成“停止錄音”獲得焦點
recordStart.setVisibility(View.GONE);
recordStop.setVisibility(View.VISIBLE);
}
// 停止錄音
private void stopRec() {
isPhoto = true;
isVideo = true;
mRecorder.stop();
mRecorder.release();
mRecorder = null;
Toast.makeText(this, "錄音結束", Toast.LENGTH_SHORT).show();
// “停止錄音”後,將按鈕還原“開始錄音”獲得焦點
recordStop.setVisibility(View.GONE);
recordStart.setVisibility(View.VISIBLE);
}
/**
* 呼叫系統錄影機錄影
*/
private void startVideo() {
camera.stopPreview();
camera.release();
camera = null;
Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 0);
startActivityForResult(intent, RESULT_FIRST_USER);
finish();
}
/**
* 重寫onKeyDown方法,當在相機介面按返回鍵退到主選單而不是直接退出程式
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
startActivity(new Intent(PhotographActivity.this,
MenuActivity.class));
finish();
}
return false;
}
}
6、增加一個拍完照後可以檢視所拍的照片的功能,並且可以選擇保留或者刪除當前的照片,選擇後均會回到相機介面。新建一個ConfirmActivity,繼承BaseActivity。程式碼:
package xw.activity.photograph;
import java.io.File;
import xw.phonemanager.activity.BaseActivity;
import xw.phonemanager.activity.R;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.net.Uri;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;
import android.widget.Toast;
public class ConfirmActivity extends BaseActivity {
public static String photoName;
ImageView photo;// 顯示所拍的照
ImageView save;// 保留
ImageView delete;// 刪除
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.confirm);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);// 強制橫屏
photo = (ImageView) findViewById(R.id.photo_img);
save = (ImageView) findViewById(R.id.photo_save);
// 設定監聽,點選後會自動回到相機介面
save.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(ConfirmActivity.this, "照片已儲存", Toast.LENGTH_LONG)
.show();
startActivity(new Intent(ConfirmActivity.this,
PhotographActivity.class));
finish();
}
});
delete = (ImageView) findViewById(R.id.photo_delete);
// 設定監聽,點選後會自動回到相機介面
delete.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
File file = new File(photoName);
if (file.exists()) {
file.delete();// 刪除照片
Toast.makeText(ConfirmActivity.this, "照片已刪除",
Toast.LENGTH_LONG).show();
startActivity(new Intent(ConfirmActivity.this,
PhotographActivity.class));
finish();
}
}
});
showPic();// 顯示所拍的照片
}
private void showPic() {
Intent intent = getIntent();
Uri originUri = intent.getData();
photo.setImageURI(originUri);
}
/**
* 重寫onKeyDown方法,當在相機介面按返回鍵退到主選單而不是直接退出程式
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
startActivity(new Intent(ConfirmActivity.this,
PhotographActivity.class));
finish();
}
return false;
}
}
與之對應的xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ImageView
android:id="@+id/photo_img"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<ImageView
android:id="@+id/photo_delete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignTop="@+id/photo_save"
android:layout_marginRight="74dp"
android:src="@drawable/photo_del" />
<ImageView
android:id="@+id/photo_save"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_marginBottom="32dp"
android:layout_marginLeft="68dp"
android:src="@drawable/photo_save" />
</RelativeLayout>
至此,簡單的照相、錄音和錄影功能基本實現,照相介面效果圖:
學識鄙陋,未免貽笑大方,望見諒:)
謝謝。