多媒体开发
視頻課:https://edu.csdn.net/course/play/7621
學習內容
??使用MediaPlayer播放音樂
??使用SoundPool播放音效
??使用VideoView播放視頻
??使用MediaRecorder錄制音頻
??使用Camera拍照
能力目標
??掌握如何使用MediaPlayer播放音樂
??掌握如何使用SoundPool播放音效
??掌握如何使用VideoView播放視頻
??掌握如何使用MediaRecorder錄制音頻
??了解如何使用Camera拍照
本章簡介
縱觀移動市場上的手機,特別是智能手機,大家一定會發現現在的手機已經不僅僅限接聽電話、收發短信、瀏覽網頁之類的簡單功能了。手機已經發展成一個集照相機、音樂播放器、視頻播放器、網頁瀏覽器等功能于一體的智能設備。因此為手機提供音、視頻的錄制、播放以及照相等功能已經成為軟件開發中必不可少的內容。Android原生提供了對MP3、WAV 、MP4和3GP等音頻、視頻格式支持的組件API,通過這些API和組件我們可以非常容易地實現強大的音頻和視頻功能。在本章節中我們就結合具體的案例針對Android中的多媒體開發相關的內容進行深入講解,這部分內容包括音樂的播放、音效的播放、視頻的播放、音頻的錄制以及拍照等功能的實現。?
核心技能部分?
4.1?音頻播放
4.1.1?Mediaplayer播放音樂
我們經常使用手機邊聽音樂邊瀏覽網頁。可是音樂播放這種功能在Android系統中是如何實現的呢?本小節中我們就學習如何使用android.media.MediaPlayer類播放保存在apk中或SD卡中的音頻文件。
播放apk中音頻文件的步驟包括:
(1)?調用MediaPlayer的create()方法加載指定的MP3文件
(2)?調用MediaPlayer的start()、parse()、stop()等方法完成對播放狀態的控制
播放SD卡上音頻文件的步驟:
(1)?創建MediaPlayer對象,并調用MediaPlayer對象的setDataSource()方法加載指定的MP3文件
(2)?調用MediaPlayer對象的prepare()方法準備音頻
(3)?調用MediaPlayer的start()、parse()、stop()等方法完成對播放狀態的控制?
示例4.1:
使用MediaPlayer播放MP3文件。
本程序最終實現的功能既能播放apk中的音樂文件,又能播放SD卡中指定名稱的文件,而且還要能控制音樂的播放、停止以及暫停。
首先在布局文件中提供提供四個id分別為btnStart1、btnStart2、btnStop和btnPause按鈕,來實現播放apk中的MP3文件、播放SD卡中的MP3文件、停止播放、暫停播放功能。其中btnPause按鈕上面的顯示文字會隨著點擊而顯示不同的文字。顯示效果如圖4.1.1和圖4.1.2所示:
?
圖4.1.1MediaPlayer默認界面
?
圖4.1.2 MediaPlayer播放狀態界面
其次是編寫Activity類,在這個類中,分別為四個按鈕注冊單擊事件、提供事件的處理方法。其中在btnStart1的事件處理方法中,我們使用到了onCompletion事件,這個事件會在音樂播放完時被觸發,此處我們在音樂播放完后釋放了音頻資源,以便其它應用程序可以使用這個資源。詳細代碼如下:
public?class?MediaPlayerActivity extends?Activity {
private?MediaPlayer mediaPlayer?= null;
private?Button btnPause?= null;
private?Button btnStart1?= null;
private?Button btnStart2?= null;
private?Button btnStop?= null;
?
@Override
public?void?onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mediaplayermp3);
?
btnStart1?= (Button) findViewById(R.id.btnStart1);
btnStart2?= (Button) findViewById(R.id.btnStart2);
btnStop?= (Button) findViewById(R.id.btnStop);
btnPause?= (Button) findViewById(R.id.btnPause);
?
btnStart1.setOnClickListener(listener);
btnStart2.setOnClickListener(listener);
btnStop.setOnClickListener(listener);
btnPause.setOnClickListener(listener);
}
?
private?OnClickListener listener?= new?OnClickListener() {
@Override
public?void?onClick(View view) {
switch?(view.getId()) {
case?R.id.btnStart1: // 播放apk中的MP3
// 在創建MediaPlayer對象的同時指定使用res/raw目錄中的MP3資源
mediaPlayer?= MediaPlayer.create(MediaPlayerActivity.this, R.raw.zhubajiebeixifu);
mediaPlayer.setOnCompletionListener(new?OnCompletionListener() {
@Override
public?void?onCompletion(MediaPlayer mp) {
mp.release();
setTitle("資源已經釋放");
}
});
if?(mediaPlayer?!= null)
mediaPlayer.stop();
// 在播放之前,必須執行下列語句做準備工作
try?{
mediaPlayer.prepare();
} catch?(Exception e) {
e.printStackTrace();
}
mediaPlayer.start();// 開始播放
break;
case?R.id.btnStart2: ???// 播放SD卡中的MP3
mediaPlayer?= new?MediaPlayer();
try?{
// 指定mp3文件的路徑
mediaPlayer.setDataSource("/sdcard/aidegongfeng.mp3");
mediaPlayer.prepare();
} catch?(Exception e) {
e.printStackTrace();
}
mediaPlayer.start();
break;
case?R.id.btnStop:??// 停止播放
if?(mediaPlayer?!= null) {
mediaPlayer.stop();
}
break;
case?R.id.btnPause:// 暫停播放
if?(mediaPlayer?!= null) {
if?("播放".equals(btnPause.getText().toString())) {
mediaPlayer.start();
btnPause.setText("暫停");
} else?if?("暫停".equals(btnPause.getText().toString())) {
mediaPlayer.pause();
btnPause.setText("播放");
}
}
}
}
};
}
為了使程序能夠正確運行,我們首先需要在模擬器的SD卡中加入一個名為aidegongfeng的MP3文件。然后運行程序,當我們單擊播放音樂的按鈕時,音樂就會播放起來,當我們單擊停止和暫停按鈕時也會實現相應的功能。
此外,MediaPlayer也支持播放網絡URL指定的音頻文件,基本的代碼如下:
String?url =?"http://........";?// 這里設置音頻URL
MediaPlayer?mediaPlayer =?new?MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(url);
mediaPlayer.prepare();????????// 可能需要很長時間(例如要緩沖)
mediaPlayer.start();
需要注意的是,播放在線網絡音頻文件時,這個在線的媒體文件必須支持漸進下載。所謂漸近下載(Progressive download)是一個術語,它用來描述從服務器向客戶端傳輸媒體文件,通常使用Http協議。用戶可以在媒體文件下載完成之前播放。它和流媒體不同之處在于正在使用的終端用戶設備如何接收來存儲數字媒體數據。
媒體播放器可以漸近下載播放依賴于位于文件頭完整的無信息(meta data)和已經從服務器下載存在于本地緩沖區的數字媒體文件。當一定量的數據對于播放設備來說是可用的了,媒體文件就開始播放。
4.1.2?SoudPool播放音效
因為MediaPlayer具有資源占用較高、延遲時間較長、不支持多個音頻同時播放的特點,對于在游戲開發中出現的需要經常播放密集、短促的音效,使用MediaPlayer就不合適了。我們可以使用SoundPool技術來實現這種短促且對反應速度比較高的情況,比如游戲音效或按鍵聲等。SoundPool除了資源占用低和反應快之外,它還支持自行設置聲音的品質、音量、播放比率等。
使用SoundPool播放聲音的步驟如下:
??調用SoundPool的構造器創建SoundPool對象。
??調用SoundPool對象的load()方法從指定的資源、文件中加載聲音,此時可以使用HashMap<Integer,Integer>對象來管理聲音。
??調用SoundPool的play()方法播放聲音。
示例4.2:
編寫一個程序,實現游戲中的爆炸、射擊、射箭三種動作的聲音播放的效果。
首先在布局文件中提供三個id分別為bomb、shot、arrow的按鈕,當用戶單擊這三個按鈕時分別播放爆炸、射擊、射箭的聲音。程序界面如下圖4.1.3所示:
?
圖4.1.3 ?SoundPool程序界面效果
編寫Activity類,詳細代碼如下:
public?class?SoundPoolActivity extends?Activity implements?OnClickListener {
private?Button bomb?= null;
private?Button shot?= null;
private?Button arrow?= null;
private?SoundPool soundPool?= null;// 定義一個SoundPool
private?HashMap<Integer, Integer> soundMap?= null;
?
@Override
public?void?onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.soundpool);
?
bomb?= (Button) findViewById(R.id.bomb);
shot?= (Button) findViewById(R.id.shot);
arrow?= (Button) findViewById(R.id.arrow);
?
soundMap?= new?HashMap<Integer, Integer>();
// 設置最多可容納13個音頻流,音頻的品質為6
soundPool?= new?SoundPool(13, AudioManager.STREAM_SYSTEM,6);
// load方法加載指定音頻文件,并返回所加載的音頻ID。此處使用HashMap
//來管理這些音頻流
soundMap.put(1, soundPool.load(this, R.raw.bomb, 1));
soundMap.put(2, soundPool.load(this, R.raw.shot, 1));
soundMap.put(3, soundPool.load(this, R.raw.arrow, 1));
bomb.setOnClickListener(this);
shot.setOnClickListener(this);
arrow.setOnClickListener(this);
}
?
// 重寫OnClickListener監聽器接口的方法
@Override
public?void?onClick(View source) {
// 判斷哪個按鈕被單擊
switch?(source.getId()) {
case?R.id.bomb:
soundPool.play(soundMap.get(1), 1, 1, 0, 0, 1);
break;
case?R.id.shot:
soundPool.play(soundMap.get(2), 1, 1, 0, 0, 1);
break;
case?R.id.arrow:
soundPool.play(soundMap.get(3), 1, 1, 0, 0, 1);
break;
}
}
}
在SoundPool的構造方法中可以指定它總共支持多少個聲音、聲音的品質等,該方法的原型如下 :
??SoundPool.SoundPool(int maxStreams, int streamType, int srcQuality)
參數maxStreams指定支持多少個聲音,streamType指定聲音的類型,srcQuality指定聲音的品質。
程序中在加載聲音時用到了load方法,這個方法的原型如下:
??int ?SoundPool.load(Context?context, int resId, int priority)
加載指定的音頻文件,并返回所加載的文件的Id。其中參數priority目前還沒有任何作用,Android建議我們將該參數設為1,以保持和未來版本的兼容性。
4.2?視頻播放
4.2.1?ViedoView播放視頻
現在對視頻播放的支持已經成為智能手機一個必不可少的功能,對高清視頻播放的支持甚至成了許多手機的賣點。所謂的視頻播放指的是在Android設備上播放如3gp格式、rmvb格式、mp4格式的等各種視頻文件。在Android Market上大家也能找到大量的視頻播放軟件,比如UC Player、暴風、QQ影音等。下面我們就使用Android提供的VideoView組件來自己做一個簡單的視頻播放器。
使用VideoView播放視頻的步驟如下:
(1)?在布局文件中定義一個VideoView組件,當然也可以在Java代碼中直接使用new生成。
(2)?使用VideoView的setVideoPath()或setVideo()方法加載對應的視頻。
(3)?調用VideoView的start()、stop()、pause()等方法實現對視頻播放的控制。
示例4.3
使用VideoView編寫一個簡單的視頻播放器。
定義布局文件:
<?xml?version="1.0"?encoding="utf-8"?>
<LinearLayout?xmlns:android="http://schemas.android.com/apk/res/android"
????android:layout_width="fill_parent"
????android:layout_height="fill_parent"
????android:orientation="vertical"?>
?
????<VideoView
????????android:id="@+id/videoView"
????????android:layout_width="fill_parent"
????????android:layout_height="fill_parent"?/>
?
</LinearLayout>
在布局文件中定義了一個VideoView組件,接下來就可以在程序中使用這個組件播放視頻了。一般情況下我們常采用MediaController組件來控制視頻的播放,只需要調用VideoView.?setMediaController方法,即可實現使用指定的MediaControler來控制VideoView中的視頻播放。下面是Activity類的詳細代碼:
public?class?VedioViewActivity extends?Activity {
private?VideoView videoView?= null;
private?MediaController controller?= null;
?
@Override
public?void?onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//設置窗口格式為半透明
getWindow().setFormat(PixelFormat.TRANSLUCENT);
setContentView(R.layout.videoview);
// 獲取界面上VideoView組件
videoView?= (VideoView) findViewById(R.id.videoView);
// 創建MediaController對象
controller?= new?MediaController(this);
File video = new?File("/mnt/sdcard/labixiaoxin009.mp4");
if?(video.e?xists()) {
videoView.setVideoPath(video.getAbsolutePath());
// 設置videoView與mController建立關聯
videoView.setMediaController(controller);
// 設置mController與videoView建立關聯
controller.setMediaPlayer(videoView);
// 讓VideoView獲取焦點
videoView.requestFocus();
}
}
}
在運行程序之前,我們需要先在mnt/sdcard/目錄下放置一個名為labixiaoxin009.mp4視頻文件,如下圖4.1.4所示,最后,整個程序的運行效果如下圖4.1.5所示。
?
圖4.1.4 SD卡中文件圖
?
圖4.1.5 VideoView播放器效果圖
4.3?使用MediaRecord錄制音頻
隨著生活節奏的提高,更多的人選擇用手機中的錄音功能來記錄身邊發生的點點滴滴,而錄音功能相比于傳統的紙筆記事來說更加便捷,場景還原度也更高,因此對很多應用來說,為用戶提供錄音功能就顯得極為重要了。這里所謂的錄音指的是設備通過對模擬信號的采樣、編碼將模擬信號通過數模轉換器轉換為數字信號,并進行一定的壓縮后進行存儲的過程。在Android中要實現錄音非常簡單,只需要借助android.media.MediaRecorder類借助手機內置的麥克風即可實現。
使用MediaRecorder類錄制聲音的步驟和用到的相關方法如下:
??創建MediaRecorder對象。
??調用MediaRecorder對象的setAudioSource(MediaRecorder.AudioSource.MIC)方法設置聲音來源為麥克風。
??調用MediaRecorder對象的setOutputFormat()設置所錄制的音頻文件的格式。
??調用MediaRecorder對象的setAudioencoder()、setAudioEncodingBitRate()、setAudioSamplingRate()設置所錄制的聲音的編碼格式、編碼位率、采樣率等。
??調用MediaRecorder對象的setOutputFile()方法設置錄制的音頻文件的保存位置。
??調用MediaRecorder對象的prepare()方法準備錄制。
??調用MediaRecorde對象r的start()方法開始錄制。
??錄制完成后,調用MediaRecorder對象的stop()方法停止錄制,并調用release()方法釋放資源。
示例4.4
實現一個簡單的錄音機,要求能夠實現對開始錄音、停止錄音、播放錄音的控制,程序界面如下圖4.1.6所示。
?
圖4.1.6 自制錄音機效果圖
?
首先編寫布局文件,在布局文件中提供三個id分別為btnRecord、btnStop、btnPlay的按鈕,分別用來實現開始錄音、停止錄音、播放錄音的功能。
接下來實現功能的Activity類,代碼如下:
public?class?MediaRecorderActivity extends?Activity {
private?File file;
private?MediaRecorder mediaRecorder;
private?MediaPlayer mediaPlayer;
?
@Override
public?void?onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mediarecorder);
?
Button start = (Button) findViewById(R.id.btnRecord);
start.setOnClickListener(listener);
Button stop = (Button) findViewById(R.id.btnStop);
stop.setOnClickListener(listener);
Button play = (Button) findViewById(R.id.btnPlay);
play.setOnClickListener(listener);
?
}
?
//
private?OnClickListener listener?= new?OnClickListener() {
?
public?void?onClick(View view) {
switch?(view.getId()) {
case?R.id.btnRecord:
try?{
//創建保存錄音的音頻文件
file?= File.createTempFile("record", ".mp3");
mediaRecorder?= new?MediaRecorder();
//設置錄音的聲音來源
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
//設置錄制的聲音的輸出格式
??????????????????mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
//設置聲音的編碼格式
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
mediaRecorder.setOutputFile(file.getAbsolutePath());
mediaRecorder.prepare();
mediaRecorder.start();//開始錄音
} catch?(IOException e) {
e.printStackTrace();
}
break;
case?R.id.btnStop:
if?(mediaRecorder?!= null) {
mediaRecorder.stop();//停止錄音
mediaRecorder.release();//釋放資源
mediaRecorder?= null;
}
break;
case?R.id.btnPlay:
try?{
mediaPlayer?= new?MediaPlayer();
mediaPlayer.setDataSource(file.getAbsolutePath());
mediaPlayer.prepare();
mediaPlayer.start();
} catch?(IOException e) {
e.printStackTrace();
}
break;
}
}
?
};
}
本程序需要使用系統的麥克風進行錄音,因此需要向該程序授予錄音的權限,在功能清單文件中添加如下代碼配置錄音權限:
<uses-permission?android:name="android.permission.RECORD_AUDIO"?/>
4.4?使用Camera實現拍照功能
隨著手機配置的提高,很多手機的相機已經可以達到千萬級像素以上,同時還支持光學變焦,這些手機的照相功能在某些方面甚至超過了一些普通的數碼相機。而現在很多應用也需要調用系統的照相功能來完成相應的操作,例如現在比較流行的微博手機客戶端,用戶就可以在客戶端中直接點擊一個按鈕進入到照相功能,照完之后可以直接將相片通過微博客戶端上傳,這里就是一個典型的通過軟件調用照相功能的案例。下面我們就通過一個簡單的例子來演示如何調用系統的照相功能。
Android系統中是通過Camera類對照相提供原始支持,使用Camera類進行拍照的步驟如下:
??調用Camera的open()方法打開相機;
??調用Camera的getParameters()方法獲取拍照參數;
??調用Camera.Parameters對象的相關方法設置相機參數;
??調用Camera的setParameters(Camera.Parameters)方法對相機的拍照參數進行控制;
??調用Camera的setPreviewDisplay()方法設置使用哪個SurfaceView來顯示取景圖片;
??調用Camera的startPreview()方法開始預覽取景 ;
??調用Camera的takePicture()方法進行拍照;
??拍照結束時,可以調用Camera的stopPreview()結束取景預覽,并調用release()方法釋放資源。
示例4.5
實現拍照的功能,要求當拍照成功之后直接把我們所拍的照片保存在SD卡中。
布局文件代碼:
<?xml?version="1.0"?encoding="utf-8"?>
<FrameLayout?xmlns:android="http://schemas.android.com/apk/res/android"
????android:layout_width="fill_parent"
????android:layout_height="fill_parent"?>
?
????<SurfaceView
????????android:id="@+id/surfaceView"
????????android:layout_width="fill_parent"
????????android:layout_height="fill_parent"?/>
?
????<RelativeLayout
????????android:id="@+id/buttonlayout"
????????android:layout_width="fill_parent"
????????android:layout_height="fill_parent"
????????android:visibility="gone"?>
?
????????<Button
????????????android:id="@+id/takepicture"
????????????android:layout_width="wrap_content"
????????????android:layout_height="wrap_content"
????????????android:layout_alignParentBottom="true"
????????????android:layout_alignParentRight="true"
????????????android:layout_marginRight="30dp"
????????????android:text="@string/takebutton"?/>
?
????????<Button
????????????android:id="@+id/foucebutton"
????????????android:layout_width="wrap_content"
????????????android:layout_height="wrap_content"
????????????android:layout_alignTop="@id/takepicture"
????????????android:layout_marginRight="50dp"
????????????android:layout_toLeftOf="@id/takepicture"
????????????android:text="@string/foucebutton"?/>
????</RelativeLayout>
?
</FrameLayout>
Activity類代碼:
public?class?TakePictureActivity extends?Activity {
private?Camera camera?= null;
private?RelativeLayout layout?= null;
?
@Override
public?void?onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.takepicture);
?
layout?= (RelativeLayout) this.findViewById(R.id.buttonlayout);
Button takepictureBtn = (Button) this.findViewById(R.id.takepicture);
Button foucebtn = (Button) this.findViewById(R.id.foucebutton);
?
SurfaceView surfaceView = (SurfaceView) this.findViewById(R.id.surfaceView);
surfaceView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
surfaceView.getHolder().setFixedSize(176, 144);
surfaceView.getHolder().addCallback(new?SurfaceCallback());
?
ButtonClickListener?lisetener = new?ButtonClickListener();
takepictureBtn.setOnClickListener(lisetener);
foucebtn.setOnClickListener(lisetener);
}
?
private?final?class?ButtonClickListener?implements?View.OnClickListener {
public?void?onClick(View v) {
if?(camera?!= null) {
if?(v.getId() == R.id.takepicture) {
camera.takePicture(null, null, new?MyPictureCallback());
layout.setVisibility(ViewGroup.GONE);
} else?{
camera.autoFocus(null);// 對焦
}
}
}
?
private?final?class?MyPictureCallback implements?PictureCallback {
public?void?onPictureTaken(byte[] data, Camera camera) {
try?{
File file = new?File(Environment.getExternalStorageDirectory(), System.currentTimeMillis() + ".jpg");
FileOutputStream outStream = new?FileOutputStream(file);
outStream.write(data);
outStream.close();
camera.startPreview();
} catch?(Exception e) {
e.printStackTrace();
}
}
}
}
?
@Override
public?boolean?onTouchEvent(MotionEvent event) {
if?(event.getAction() == MotionEvent.ACTION_DOWN) {
layout.setVisibility(ViewGroup.VISIBLE);
}
return?super.onTouchEvent(event);
}
?
private?final?class?SurfaceCallback implements?Callback {
?
@Override
public?void?surfaceCreated(SurfaceHolder holder) {
try?{
camera?= Camera.open();
Camera.Parameters parameters = camera.getParameters();
Log.i("MainActivity", parameters.flatten());
parameters.setPreviewSize(800, 480);
parameters.setPreviewFrameRate(5);
parameters.setPictureFormat(ImageFormat.JPEG);
parameters.setPictureSize(640, 480);
camera.setParameters(parameters);
camera.setPreviewDisplay(holder);
camera.startPreview();
} catch?(IOException e) {
e.printStackTrace();
}
?
}
?
@Override
public?void?surfaceChanged(SurfaceHolder holder, int?format, int?width, int?height) {
}
?
@Override
public?void?surfaceDestroyed(SurfaceHolder holder) {
if?(camera?!= null)
camera.release();
camera?= null;
}
?
}
}
功能清單文件中添加權限:
<!-- 在SDCard中創建與刪除文件權限 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<!-- 往SDCard寫入數據權限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.CAMERA"/>
運行程序(建議使用真機進行測試),在SD卡中會生成一張我們新拍的照片,導出照片到電腦就查看我們剛才拍出來的照片了。
任務實訓部分?
1:游戲片段模擬實現
訓練技能點
??Android中繪圖的基本知識
??使用SoundPool播放音效
需求說明
模擬實現極品飛車中汽車開動的動作,要求汽車在沿著某一方向開動的時候,系統能夠自動模擬發動機發出嗡嗡的聲音。
實現步驟
(1)?在屏幕中用Android系統默認提供的基本圖形繪制相關的API繪制一個小汽車。
(2)?給小汽車添加沿某一方向開動的功能。
(3)?給小汽車添加開動時的發動機的嗡鳴效果。?
2:自定義視頻播放器
訓練技能點
??使用VideoView播放視頻
需求說明
模擬UC影音自己編寫一個視頻播放器軟件,要求:
(1)?實現本地視頻播放的功能。
(2)?實現在線視頻播放的功能,至于播放哪個網站的視頻,可以由用戶手工輸入地址簡單實現。
(3)?添加播放記錄的功能。
整個軟件界面參看下圖4.2.1。
?
圖4.2.1 UC影音?
鞏固練習
一、簡答題
1.?簡單描述采用MediaPlayer播放音頻文件的步驟。?
2.?簡單描述使用Camera類進行拍照的步驟。?
二、上機練習
參考4.4節的內容自己實現一個照相機軟件,要求除了能夠的把拍得的照片保存在本地,還能夠上傳到指定的網站,比如郵箱等。?
總結
- 上一篇: OpenGL ES
- 下一篇: ssm整合之七 事务以及404页面处理