第13章 繪圖與多媒體 13-1 顯示圖檔-行動相簿 13-2 音樂播放-音樂播放器 13-3 影片播放-視訊播放器 13-4 聲音處理-錄音程式 13-5 2D繪圖-井字遊戲
13-1 顯示圖檔-行動相簿(說明) 行動相簿是一個在手持行動裝置瀏覽照片的Android程式,筆者準備用來說明如何在Android應用程式使用GridView和ImageView元件來顯示圖檔。在實務上,我們可以將活動、旅遊、人或寵物照片收集建立成相簿程式,隨時隨地分享照片給其他人。 這個範例是在主活動使用GridView元件顯示照片的縮圖目錄,選擇照片縮圖後,可以開啟另一活動使用ImageView元件顯示較大照片。
13-1 顯示圖檔-行動相簿 步驟一:開啟和執行Android Studio專案 請啟動Android Studio開啟專案Ch13_1,程式包含3個Java類別檔、2個版面配置檔和一些照片的PNG格式圖檔,其執行結果如下圖所示:
13-1 顯示圖檔-行動相簿 步驟二:建立照片的圖形資源 行動相簿的主要目的是顯示照片,我們需要建立照片的圖形資源,所有照片的PNG圖檔是位在專案實際路徑「app/src/main/res/drawable」目錄下的同名圖檔(「Project」視窗如果選【Android】檢視,顯示的專案路徑和實際路徑並不相同),如下圖所示:
13-1 顯示圖檔-行動相簿 步驟三:縮圖目錄的GridView元件 在縮圖目錄MainActivity類別的版面配置刪除TextView元件後,新增【Containers】區段的GridView元件,為了方便說明,筆者列出XML標籤,如下所示: <GridView android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/gridView" ...... android:stretchMode="columnWidth" android:columnWidth="90dp" android:numColumns="auto_fit" />
13-1 顯示圖檔-行動相簿 步驟四:建立縮圖目錄的MainActivity類別-1 行動相簿的縮圖目錄是使用GridView元件來建立,在MainActivity類別開頭的Integer[]陣列是圖庫的圖形資源索引,如下所示: public class MainActivity extends ActionBarActivity { private Integer[] imgThumbIds = { R.drawable.image01, R.drawable.image02, R.drawable.image03, R.drawable.image04, R.drawable.image05, R.drawable.image06, R.drawable.image07 }; …... }
13-1 顯示圖檔-行動相簿 步驟四:建立縮圖目錄的MainActivity類別-2 onCreate()方法 protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); GridView gridview = (GridView) findViewById(R.id.gridView); gridview.setAdapter(new GridAdapter(this, imgThumbIds)); gridview.setOnItemClickListener( new AdapterView.OnItemClickListener() { public void onItemClick(AdapterView<?> parent, View v, int position, long id) { Toast.makeText(MainActivity.this, "" + position, Toast.LENGTH_SHORT).show(); Intent intent = new Intent(MainActivity.this, ImageActivity.class); intent.putExtra("IMAGEID", imgThumbIds[position]); startActivity(intent); } });
13-1 顯示圖檔-行動相簿 步驟五:GridAdapter類別-1 GridAdapter類別是繼承BaseAdapter的自訂結合器類別,在此類別需要覆寫getCount()、getItem()、getItemId()和getView()方法,如下所示: public class GridAdapter extends BaseAdapter { private Context context; private Integer[] imgThumbIds; public GridAdapter(Context c, Integer[] thumbIds) { context = c; imgThumbIds = thumbIds; }
13-1 顯示圖檔-行動相簿 步驟五:GridAdapter類別-2 在之後依序覆寫getCount()、getItem()和getItemId()方法,可以傳回照片數,即陣列尺寸,和傳回照片物件和哪一張照片,如下所示: @Override public int getCount() { return imgThumbIds.length; } public Object getItem(int position) { return null; public long getItemId(int position) { return position;
13-1 顯示圖檔-行動相簿 步驟五:GridAdapter類別-3 最後覆寫getView()方法,可以傳回ImageView物件,如下所示: @Override public View getView(int position, View convertView, ViewGroup parent) { ImageView imageView; if (convertView == null) { // 是否需初始ImageView元件 imageView = new ImageView(context); imageView.setLayoutParams( new GridView.LayoutParams(220, 180)); imageView.setScaleType( ImageView.ScaleType.CENTER_CROP); imageView.setPadding(8, 8, 8, 8); } else { imageView = (ImageView) convertView; } imageView.setImageResource(imgThumbIds[position]); return imageView;
13-1 顯示圖檔-行動相簿 步驟六:顯示照片的版面配置檔 在ImageActivity活動的版面配置檔是activity_image.xml,在刪除TextView元件後,新增一個ImageView元件imageView。
13-1 顯示圖檔-行動相簿 步驟七:顯示照片的ImageActivity活動-1 public class ImageActivity extends ActionBarActivity { …... } 上述ImageActivity類別的成員方法的說明,如下所示:
13-1 顯示圖檔-行動相簿 步驟七:顯示照片的ImageActivity活動-2 onCreate()方法 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_image); ActionBar actionBar = getSupportActionBar(); actionBar.setDisplayHomeAsUpEnabled(true); Intent intent = getIntent(); int imageID = intent.getIntExtra("IMAGEID", 0); ImageView imageview = (ImageView)findViewById(R.id.imageView); imageview.setImageResource(imageID); }
13-1 顯示圖檔-行動相簿 步驟八:在AndroidManifest.xml註冊活動 ImageActivity活動需要在AndroidManifest.xml檔註冊,和指定返回的父活動是.MainActivity,如下所示: <activity android:name=".ImageActivity" android:label="@string/title_activity_image" android:parentActivityName=".MainActivity"> <meta-data android:name="android.support.PARENT_ACTIVITY" android:value=".MainActivity"/> </activity>
13-2 音樂播放-音樂播放器(說明) Android應用程式可以使用MediaPlayer物件播放音樂,為了不中斷音樂的播放,專案是建立服務來播放儲存在SD卡的MP3音樂,即Windows作業系統範例音樂Kalimba.mp3。
13-2 音樂播放-音樂播放器 步驟一:開啟和執行Android Studio專案 請啟動Android Studio開啟專案Ch13_2,內含2個Java類別檔的活動與服務,和版面配置檔activity_main.xml。首先需要將MP3音樂檔複製至Android模擬器的SD卡後執行此專案,其執行結果如下圖所示:
13-2 音樂播放-音樂播放器 步驟二:建立播放器介面的版面配置 音樂播放器的使用介面是定義在activity_main.xml版面配置檔,在TextView元件(lblOutput)下使用LinearLayout水平編排3個Button元件,如下圖所示:
13-2 音樂播放-音樂播放器 步驟三:建立Activity活動類別的事件處理方法-1 在MainActivity活動類別的開頭宣告成員的TextView物件變數,如下所示: public class MainActivity extends ActionBarActivity { private TextView output; … }
13-2 音樂播放-音樂播放器 步驟三:建立Activity活動類別的事件處理方法-2 onCreate()方法 在覆寫的onCreate()方法顯示播放器介面的版面配置和取得TextView元件,如下所示: @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); output = (TextView) findViewById(R.id.output); }
13-2 音樂播放-音樂播放器 步驟三:建立Activity活動類別的事件處理方法-3 start_Click()事件處理方法 在start_Click()方法使用Intent物件呼叫startService()方法來啟動MusicService服務,附件ISPAUSE是布林值false,表示不是暫停播放,如下所示: public void start_Click(View view) { Intent intent = new Intent(this, MusicService.class); intent.putExtra("ISPAUSE", false); startService(intent); output.setText("音樂播放中..."); }
13-2 音樂播放-音樂播放器 步驟三:建立Activity活動類別的事件處理方法-4 pause_Click()事件處理方法 在pause_Click()方法也是使用Intent物件呼叫startService()方法來啟動MusicService服務,如下所示: public void pause_Click(View view) { Intent intent = new Intent(this, MusicService.class); intent.putExtra("ISPAUSE", true); startService(intent); output.setText("音樂暫停中..."); }
13-2 音樂播放-音樂播放器 步驟三:建立Activity活動類別的事件處理方法-5 stop_Click()事件處理方法 在stop_Click()方法是使用Intent物件呼叫stopService()方法來停止服務,也就是停止音樂的播放,如下所示: public void stop_Click(View view) { Intent intent = new Intent(this, MusicService.class); stopService(intent); output.setText("音樂已經停止播放..."); }
13-2 音樂播放-音樂播放器 步驟四:建立MusicService服務類別播放音樂-1 播放音樂是在MusicService服務類別,使用MediaPlayer物件的相關方法來播放音樂。類別是繼承自Service類別,在開頭宣告成員的MediaPlayer物件,如下所示: public class MusicService extends Service { private MediaPlayer player; private String musicFile = "/sdcard/Kalimba.mp3"; ...... }
13-2 音樂播放-音樂播放器 步驟四:建立MusicService服務類別播放音樂-2 onCreate()方法 在覆寫onCreate()方法建立MediaPlayer物件,try/catch例外處理使用setDataSource()方法指定音樂檔案路徑,setOnCompletionListener()方法指定播放完畢的傾聽者物件,最後呼叫prepare()方法進入準備狀態,如下所示: @Override public void onCreate() { player = new MediaPlayer(); try { player.setDataSource(musicFile); player.setOnCompletionListener(listener); player.prepare(); // 準備 } catch (Exception ex) { Log.d("Ch13_2", "onCreate: " + ex.getMessage()); }
13-2 音樂播放-音樂播放器 步驟四:建立MusicService服務類別播放音樂-3 OnCompletionListener傾聽者物件 使用匿名內層類別建立OnCompletionListener傾聽者物件,類別需要覆寫onCompletion()方法,如下所示: private OnCompletionListener listener = new OnCompletionListener() { @Override public void onCompletion(MediaPlayer nouse) { try { player.stop(); // 停止 player.prepare(); // 準備 } catch (Exception ex){ Log.d("Ch13_2", "Listener: " + ex.getMessage()); } };
13-2 音樂播放-音樂播放器 步驟四:建立MusicService服務類別播放音樂-4 onStartCommand()方法 @Override public int onStartCommand(Intent intent, int flags, int startId) { Boolean isPause = intent.getBooleanExtra("ISPAUSE", true); try { if (isPause == true) { if (player.isPlaying() == true) player.pause(); // 暫停 } else { player.start(); // 播放 } } catch (Exception ex) { Log.d("Ch13_2", "onStart(): " + ex.getMessage()); return START_STICKY;
13-2 音樂播放-音樂播放器 步驟四:建立MusicService服務類別播放音樂-5 onDestroy()方法 在覆寫的onDestroy()方法是呼叫stop()方法停止音樂播放,如下所示: @Override public void onDestroy() { try { player.stop(); // 停止 player.prepare(); // 準備 } catch (Exception ex) { Log.d("Ch13_2", "onDestroy(): " + ex.getMessage()); }
13-2 音樂播放-音樂播放器 步驟四:建立MusicService服務類別播放音樂-6 onBind()抽象方法 實作的onBind()抽象方法並沒有使用,所以傳回null,如下所示: @Override public IBinder onBind(Intent intent) { return null; }
13-2 音樂播放-音樂播放器 步驟五:在AndroidManifest.xml註冊服務 MusicService服務需要在AndroidManifest.xml檔註冊,如下所示: <service android:name=".MusicService" android:enabled="true" android:exported="true" > </service>
13-3 影片播放-視訊播放器(說明) 在Android應用程式播放影片也可以使用MediaPlayer,不過,我們需要建立Surface物件繪出影片內容,在第16-3節使用相機時,就會使用此方式來動態顯示從相機硬體取得的預覽串流畫面。 另一種比較簡單的方式是使用VideoView元件和MediaController物件來控制影片播放,換句話說,我們可以輕鬆在活動類別建立一個視訊播放器程式。
13-3 影片播放-視訊播放器 步驟一:開啟和執行Android Studio專案 請啟動Android Studio開啟專案Ch13_3,內含1個Java類別檔的活動和一個版面配置檔activity_main.xml,如同音效檔,我們需要將影片檔Wildlife.mp4先複製至Android模擬器的SD卡,其執行結果如右圖所示:
13-3 影片播放-視訊播放器 步驟二:建立版面配置的VideoView元件 視訊播放器的使用介面是定義在activity_main.xml版面配置檔,在TextView元件下新增VideoView元件,如下圖所示:
13-3 影片播放-視訊播放器 步驟三:建立Activity活動類別播放影片-1 在MainActivity活動類別開頭宣告成員VideoView物件變數,和指定影片檔名,如下所示: public class MainActivity extends ActionBarActivity { private VideoView video; private String videoFile = "Wildlife.mp4"; …... }
13-3 影片播放-視訊播放器 步驟三:建立Activity活動類別播放影片-2 onCreate()方法 在覆寫的onCreate()方法顯示VideoView元件的版面配置後,就可以取得VideoView物件和呼叫相關方法來播放影片,如下所示: @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); video = (VideoView)findViewById(R.id.video1); video.setVideoURI( Uri.parse("file:///sdcard/"+ videoFile)); video.setMediaController(new MediaController(this)); video.start(); }
13-3 影片播放-視訊播放器 步驟三:建立Activity活動類別播放影片-3 onPause()方法 在覆寫的onPause()方法是呼叫stopPlayback()方法停止影片的播放,如下所示: @Override public void onPause() { super.onStop(); video.stopPlayback(); }
13-4 聲音處理-錄音程式(說明) 一般來說,行動裝置的麥克風除了通話外,我們也可以建立錄音程式,使用麥克風裝置來錄製聲音,在Android是使用MediaRecorder物件來錄音和將它存入檔案。 MediaRecorder物件在使用上只需指定錄音格式和儲存檔案的路徑,就可以開始進行聲音錄製。
13-4 聲音處理-錄音程式 步驟一:開啟和執行Android Studio專案 請啟動Android Studio開啟專案Ch13_4,內含1個Java類別檔、一個版面配置檔activity_main.xml。建議使用實機來測試專案的執行 ,其執行結果如下圖所示:
13-4 聲音處理-錄音程式 步驟二:建立錄音程式的版面配置 錄音程式的使用介面是定義在activity_main.xml版面配置檔,在TextView元件(lblOutput)之下是2個Button元件,如下圖所示:
13-4 聲音處理-錄音程式 步驟三:建立Activity活動類別的事件處理方法-1 在MainActivity活動類別的開頭宣告成員的MediaRecorder、Button、TextView和File物件變數,如下所示: public class MainActivity extends ActionBarActivity { private MediaRecorder recorder; private Button start, stop; private TextView output; File path; … }
13-4 聲音處理-錄音程式 步驟三:建立Activity活動類別的事件處理方法-2 onCreate()方法 在覆寫的onCreate()方法建立MediaRecorder物件和錄音檔路徑的File物件,如下所示: @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); start = (Button) findViewById(R.id.start); stop = (Button) findViewById(R.id.stop); output = (TextView) findViewById(R.id.output); recorder = new MediaRecorder(); path = new File(Environment. getExternalStorageDirectory(), "myrecord.3gp"); resetRecorder(); }
13-4 聲音處理-錄音程式 步驟三:建立Activity活動類別的事件處理方法-3 onDestroy()方法 在覆寫的onDestroy()方法呼叫release()方法釋放MediaRecorder物件的資源,如下所示: @Override protected void onDestroy() { super.onDestroy(); recorder.release(); }
13-4 聲音處理-錄音程式 步驟三:建立Activity活動類別的事件處理方法-4 resetRecorder()方法 在自訂的resetRecorder()方法重設錄音機的MediaRecorder物件,如下所示: private void resetRecorder() { recorder.setAudioSource( MediaRecorder.AudioSource.MIC); recorder.setOutputFormat( MediaRecorder.OutputFormat.THREE_GPP); recorder.setAudioEncoder( MediaRecorder.AudioEncoder.DEFAULT); recorder.setOutputFile(path.getAbsolutePath()); try { recorder.prepare(); output.setText("錄音程序準備完成...."); start.setEnabled(true); } catch (Exception ex) { Log.d("Ch13_4", "resetRecorder: " + ex.getMessage()); }
13-4 聲音處理-錄音程式 步驟三:建立Activity活動類別的事件處理方法-5 start_Click()事件處理方法 在start_Click()方法呼叫MediaRecorder物件的start()方法開始錄音,然後使用setEnabled()方法設定按鈕狀態是否可用,因為已經開始錄音,所以將【停止錄音】鈕設為可用,可以按下此按鈕來結束錄音,如下所示: public void start_Click(View view) { output.setText("開始錄音...."); try { recorder.start(); start.setEnabled(false); stop.setEnabled(true); } catch (Exception ex) { Log.d("Ch13_4", "start_Click " + ex.getMessage()); }
13-4 聲音處理-錄音程式 步驟三:建立Activity活動類別的事件處理方法-6 stop_Click()事件處理方法 在stop_Click()方法呼叫MediaRecorder物件的stop()方法停止錄音,如下所示: public void stop_Click(View view) { output.setText("停止錄音...."); recorder.stop(); start.setEnabled(true); stop.setEnabled(false); }
13-4 聲音處理-錄音程式 步驟四:在AndroidManifest.xml新增錄音權限 錄音程式因為執行錄音和將錄音檔寫入外部儲存裝置,所以在AndroidManifest.xml檔需要新增RECORD_AUDIO和WRITE_EXTERNAL_STORAGE權限,如下所示: <uses-permission android:name= "android.permission.RECORD_AUDIO"/> "android.permission.WRITE_EXTERNAL_STORAGE"/>
13-5 2D繪圖-井字遊戲 13-5-1 2D繪圖的基礎 13-5-2 井字遊戲
13-5-1 2D繪圖的基礎-說明 Android支援2D繪圖,可以在繼承View類別覆寫的onDraw()方法繪圖,如下所示: public class Draw2D extends View { public Draw2D(Context context) { super(context); } @Override protected void onDraw(Canvas canvas) { // 在此繪圖
13-5-1 2D繪圖的基礎-建立畫布 在繪圖前,我們需要先建立畫筆的Paint物件,如下所示: Paint paint = new Paint(); paint.setStyle(Paint.Style.FILL); 上述setStyle()方法指定畫筆的樣式,然後將整個Canvas物件填滿白色的背景色彩,如下所示: paint.setColor(Color.WHITE); canvas.drawPaint(paint);
13-5-1 2D繪圖的基礎-畫圓 在畫布畫圓是使用Canvas物件的drawCircle()方法,首先使用setAntiAlias()方法指定畫筆線條沒有鋸齒狀,和色彩為紅色,如下所示: paint.setAntiAlias(true); paint.setColor(Color.RED); canvas.drawCircle(80, 30, 25, paint);
13-5-1 2D繪圖的基礎-畫長方形 畫長方形是使用drawRect(),參數為兩組(x, y),分別是左上角和右下角座標,畫筆是藍色,如下所示: paint.setColor(Color.BLUE); canvas.drawRect(20, 15, 50, 100, paint);
13-5-1 2D繪圖的基礎-畫出資源圖形 在畫布上也可以直接繪出圖形資源的圖檔,首先取得資源的Resources物件,如下所示: Resources res = this.getResources(); Bitmap bitmap = BitmapFactory.decodeResource( res, R.drawable.icon); canvas.drawBitmap(bitmap, 50 ,200 , paint);
13-5-1 2D繪圖的基礎-畫出文字內容 在畫布上也可以畫出一段文字內容,首先指定畫筆的色彩、樣式、線條和字型尺寸,如下所示: paint.setColor(Color.GREEN); paint.setStyle(Paint.Style.FILL); paint.setAntiAlias(true); paint.setTextSize(30); 上述setTextSize()方法指定字型尺寸,然後呼叫drawText()方法畫出一段文字內容,如下所示: canvas.drawText("我的畫布!", 50, 180, paint);
13-5-1 2D繪圖的基礎-畫出旋轉文字 我們還可以在畫布上繪出一段旋轉文字。首先指定畫筆色彩、字型尺寸和繪出的字串,如下所示: paint.setColor(Color.BLACK); paint.setTextSize(25); String str = "旋轉的文字!"; canvas.rotate(-45, 200, 200); paint.setStyle(Paint.Style.FILL); canvas.drawText(str, 200, 200, paint); 取消旋轉是呼叫restore()方法,如下所示: canvas.restore();
13-5-2 井字遊戲-說明 井字遊戲是2D繪圖的應用,筆者使用2D繪圖方法來繪出井字形的遊戲板和在指定儲存格繪出O和X的圖形。
13-5-2 井字遊戲-步驟 步驟一:開啟和執行Android專案 步驟二:建立Activity活動執行井字遊戲 步驟三:建立井字形遊戲板的MainBoard類別 步驟四:建立邏輯儲存格的CellBoard類別
End