標籤:
Day09 多媒體編程一、對話方塊1. 確定取消對話方塊
建立對話方塊構建器對象,類似原廠模式
AlertDialog.Builder builder = new Builder(this);
設定標題和本文
builder.setTitle("警告");builder.setMessage("若練此功,必先自宮");
設定確定和取消按鈕
builder.setPositiveButton("現在自宮", new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Toast.makeText(MainActivity.this, "恭喜你自宮成功,現在程式退出", 0).show(); }});builder.setNegativeButton("下次再說", new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Toast.makeText(MainActivity.this, "若不自宮,一定不成功", 0).show(); }});
使用構建器建立出對話方塊對象
AlertDialog ad = builder.create();ad.show();
2. 單選對話方塊
建立對話方塊對象
AlertDialog.Builder builder = new Builder(this);builder.setTitle("選擇你的性別");
定義單選選項
final String[] items = new String[]{ "男", "女", "其他"};// -1表示預設選擇builder.setSingleChoiceItems(items, -1, new OnClickListener() { // which表示點擊的是哪一個選項 @Override public void onClick(DialogInterface dialog, int which) { Toast.makeText(MainActivity.this, "您選擇了" + items[which], 0).show(); // 對話方塊消失 dialog.dismiss(); }});builder.show();
3. 多選對話方塊
定義多選的選項,因為可以多選,所以需要一個boolean數組來記錄哪些選項被選了
AlertDialog.Builder builder = new Builder(this);builder.setTitle("請選擇你認為最帥的人");final String[] items = new String[]{ "趙帥哥", "趙師哥", "趙老師", "侃哥"};// true表示對應位置的選項被選了final boolean[] checkedItems = new boolean[]{ true, false, false, false,};builder.setMultiChoiceItems(items, checkedItems, new OnMultiChoiceClickListener() { // 點擊某個選項,如果該選項之前沒被選擇,那麼此時isChecked的值為true @Override public void onClick(DialogInterface dialog, int which, boolean isChecked) { checkedItems[which] = isChecked; }});builder.setPositiveButton("確定", new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { StringBuffer sb = new StringBuffer(); for(int i = 0;i < items.length; i++){ sb.append(checkedItems[i] ? items[i] + " " : ""); } Toast.makeText(MainActivity.this, sb.toString(), 0).show(); }});builder.show();
二、國際化
- 字串國際化:只要在res檔案夾下建立對應語言的values檔案夾就好了
- 美國英文環境:values-en-rUS
- 中文環境為:values-zh
- 大陸地區中文環境: values-zh-cn
三、樣式與主題
- 樣式與主題定義方式一樣
- 樣式用於布局檔案中的組件
- 主題用於Activity
四、多媒體編程4.1 電腦圖片大小的計算
- 圖片大小 = 圖片的總像素 * 每個像素佔用的大小
- 單色圖:每個像素佔用1/8個位元組
- 16色圖:每個像素佔用1/2個位元組
- 256色圖:每個像素佔用1個位元組
- 24位元影像:每個像素佔用3個位元組
4.2 載入大圖片到記憶體
- Android系統以ARGB表示每個像素,每個像素佔用4個位元組,所以很容易記憶體溢出,因此我們需要對圖片進行縮放
4.3 對圖片進行縮放
擷取螢幕寬高
Display dp = getWindowManager().getDefaultDisplay();int screenWidth = dp.getWidth();int screenHeight = dp.getHeight();
擷取圖片寬高
Options opts = new Options();// 請求圖片屬性但不申請記憶體opts.inJustDecodeBounds = true;BitmapFactory.decodeFile("sdcard/dog.jpg", opts);int imageWidth = opts.outWidth;int imageHeight = opts.outHeight;
圖片的寬高除以螢幕寬高,算出寬和高的縮放比例,取較大值作為圖片的縮放比例
int scale = 1;int scaleX = imageWidth / screenWidth;int scaleY = imageHeight / screenHeight;if(scaleX >= scaleY && scaleX > 1){ scale = scaleX;}else if(scaleY > scaleX && scaleY > 1){ scale = scaleY;}
按縮放比例載入圖片
// 設定縮放比例opts.inSampleSize = scale;// 為圖片申請記憶體opts.inJustDecodeBounds = false;Bitmap bm = BitmapFactory.decodeFile("sdcard/dog.jpg", opts);iv.setImageBitmap(bm);
4.4 在記憶體中建立圖片的副本
直接載入的bitmap對象是唯讀,無法修改,要修改圖片只能在記憶體中建立出一個一模一樣的bitmap副本,然後修改副本
// 載入原圖Bitmap srcBm = BitmapFactory.decodeFile("sdcard/photo3.jpg");iv_src.setImageBitmap(srcBm);// 建立與原圖大小一致的空白bitmapBitmap copyBm = Bitmap.createBitmap(srcBm.getWidth(), srcBm.getHeight(), srcBm.getConfig());// 定義畫筆Paint paint = new Paint();// 把紙鋪在畫版上Canvas canvas = new Canvas(copyBm);// 把srcBm的內容繪製在copyBm上canvas.drawBitmap(srcBm, new Matrix(), paint);iv_copy.setImageBitmap(copyBm);
4.5 對圖片進行特效處理
首先定義一個矩陣對象
Matrix mt = new Matrix();
縮放效果
// x軸縮放1倍,y軸縮放0.5倍mt.setScale(1, 0.5f);
旋轉效果
// 以圖片寬高的一半中心點為軸點,順時旋轉30度mt.setRotate(30, copyBm.getWidth() / 2, copyBm.getHeight() / 2);
平移
// x軸座標+10,y軸座標+20mt.setTranslate(10, 20);
鏡面
// 把X座標都變成負數mt.setScale(-1, 1);// 圖片整體向右移mt.postTranslate(copyBm.getWidth(), 0);
倒影
// 把Y座標都變成負數mt.setScale(1, -1);// 圖片整體向下移mt.postTranslate(0, copyBm.getHeight());
五、多媒體案例5.1 畫畫板
功能:記錄使用者觸摸事件的XY座標,繪製直線
給ImageView設定觸摸偵聽,得到使用者的觸摸事件,並獲知使用者觸摸ImageView的座標
iv.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { // 觸控螢幕幕 case MotionEvent.ACTION_DOWN: // 得到觸控螢幕幕時手指的座標 startX = (int) event.getX(); startY = (int) event.getY(); break; // 在螢幕上滑動 case MotionEvent.ACTION_MOVE: // 使用者滑動手指,座標不斷的改變,擷取最新座標 int newX = (int) event.getX(); int newY = (int) event.getY(); // 用上次onTouch方法得到的座標和本次得到的座標繪製直線 canvas.drawLine(startX, startY, newX, newY, paint); iv.setImageBitmap(copyBm); startX = newX; startY = newY; break; } return true; }});
刷子效果,加粗畫筆
paint.setStrokeWidth(8);
調色盤,改變畫筆顏色
paint.setColor(Color.GREEN);
儲存圖片至SD卡
FileOutputStream fos = null;try { fos = new FileOutputStream(new File("sdcard/dazuo.png"));} catch (FileNotFoundException e) { e.printStackTrace();}// 儲存圖片copyBm.compress(CompressFormat.PNG, 100, fos);
- 系統每次收到SD卡就緒廣播時,都會去遍曆sd卡的所有檔案和檔案夾,把遍曆到的所有多媒體檔案都在MediaStore資料庫儲存一個索引,這個索引包含多媒體檔案的檔案名稱、路徑、大小
- 圖庫每次開啟時,並不會去遍曆sd卡擷取圖片,而是通過內容提供者從MediaStore資料庫中擷取圖片的資訊,然後讀取該圖片
系統開機或者點擊載入sd卡按鈕時,系統會發送sd卡就緒廣播,我們也可以手動發送就緒廣播
Intent intent = new Intent();intent.setAction(Intent.ACTION_MEDIA_MOUNTED);intent.setData(Uri.fromFile(Environment.getExternalStorageDirectory()));sendBroadcast(intent);
5.2 撕衣服
原理:把穿內衣和穿外衣的照片重疊顯示,內衣照在下面,使用者滑動螢幕時,觸摸的是外衣照,把手指經過的像素都置為透明,內衣照就顯示出來了
給螢幕設定移動監聽,把指定的像素設定為透明
iv.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_MOVE: int newX = (int) event.getX(); int newY = (int) event.getY(); // 把指定的像素變成透明 copyBm.setPixel(newX, newY, Color.TRANSPARENT); iv.setImageBitmap(copyBm); break; } return true; }});
每次只設定一個像素點太慢,以觸摸的像素為圓心,半徑為5畫圓,圓內的像素全部置為透明
for (int i = -5; i < 6; i++) { for (int j = -5; j < 6; j++) { if(Math.sqrt(i * i + j * j) <= 5) copyBm.setPixel(newX + i, newY + j, Color.TRANSPARENT); }}
5.3 音樂播放器5.3.1 播放服務
- 播放音訊代碼應該運行在服務中,定義一個播放服務MusicService
服務裡定義play、stop、pause、continuePlay等方法
// 播放private void play() { player.reset(); try { player.setDataSource("sdcard/bzj.mp3"); player.prepare(); } catch (Exception e) { e.printStackTrace(); } player.start();}// 暫停private void pause() { player.pause();}// 停止播放private void stop() { player.stop();}// 繼續播放private void continuePlay() { player.start();}
- 把這幾個方法抽取成一個介面MusicInterface
- 定義一個中間人類,繼承Binder,實現MusicInterface
先start啟動MusicService,再bind
Intent intent = new Intent(this, MusicService.class);startService(intent);bindService(intent, conn, BIND_AUTO_CREATE);
5.3.2 根據播放進度設定進度條
擷取當前的播放時間和當前音訊最長時間
int currentPosition = player.getCurrentPosition();int duration = player.getDuration();
- 播放進度需要不停的擷取,不停的重新整理進度條,使用計時器每500毫秒擷取一次播放進度
發訊息至Handler,把播放進度放進Message對象中,在Handler中更新SeekBar的進度
Timer timer = new Timer();timer.schedule(new TimerTask() { @Override public void run() { int currentPosition = player.getCurrentPosition(); int duration = player.getDuration(); Message msg = Message.obtain(); // 把播放進度存入Message中 Bundle data = new Bundle(); data.putInt("currentPosition", currentPosition); data.putInt("duration", duration); msg.setData(data); MainActivity.handler.sendMessage(msg); }}, 5, 500);
在Activity中定義Handler
static Handler handler = new Handler(){ public void handleMessage(android.os.Message msg) { // 取出訊息攜帶的資料 Bundle data = msg.getData(); int currentPosition = data.getInt("currentPosition"); int duration = data.getInt("duration"); // 設定播放進度 sb.setMax(duration); sb.setProgress(currentPosition); };};
5.3.3 拖動進度條改變播放進度
設定拖動監聽,拖動後把進度條設定到拖動停止的位置
// 給sb設定一個拖動偵聽 sb.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { //停止拖動時調用 @Override public void onStopTrackingTouch(SeekBar seekBar) { int progress = seekBar.getProgress(); mi.seekTo(progress); } // 開始拖動時調用 @Override public void onStartTrackingTouch(SeekBar seekBar) { } // 拖動的時候不斷調用 @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { }});
5.4 視頻播放器SurfaceView
- 對畫面的即時更新要求較高
- 雙緩衝技術:記憶體中有兩個畫布,A畫布顯示至螢幕,B畫布在記憶體中繪製下一幀畫面,繪製完畢後B顯示至螢幕,A在記憶體中繼續繪製下一幀畫面
播放視頻也是用MediaPlayer,不過跟音頻不同,要設定顯示在哪個SurfaceView
SurfaceView sv = (SurfaceView) findViewById(R.id.sv);SurfaceHolder sh = sv.getHolder();MediaPlayer player = new MediaPlayer();player.reset();try { player.setDataSource("sdcard/2.3gp"); player.setDisplay(sh); player.prepare();} catch (Exception e) { e.printStackTrace();}player.start();
- SurfaceView是重量級組件,可見時才會建立
給SurfaceHolder設定CallBack,類似於偵聽,可以知道SurfaceView的狀態
sh.addCallback(new Callback() { // SurfaceView銷毀時調用 @Override public void surfaceDestroyed(SurfaceHolder holder) { } // SurfaceView建立時調用 @Override public void surfaceCreated(SurfaceHolder holder) { } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { }});
- SurfaceView一旦不可見,就會被銷毀,一旦可見,就會被建立,銷毀時停止播放,再次建立時再開始播放
5.5 照相機
啟動系統提供的拍照程式
// 隱式啟動系統提供的拍照ActivityIntent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);// 設定照片的儲存路徑File file = new File(Environment.getExternalStorageDirectory(), "haha.jpg"); intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file)); startActivityForResult(intent, 0);
啟動系統提供的攝像程式
Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);File file = new File(Environment.getExternalStorageDirectory(), "haha.3gp"); intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file)); // 設定儲存視頻檔案的品質intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);startActivityForResult(intent, 0);
Android基礎知識:Day09 多媒體編程