android筆記-多媒體編程

多媒體概念(了解)

  • 文字、圖片、音頻、視頻

計算機圖片大小的計算(掌握)

圖片大小 = 圖片的總像素 * 每個像素占用的大小

  • 單色圖:每個像素占用1/8個字節
  • 16色圖:每個像素占用1/2個字節
  • 256色圖:每個像素占用1個字節
  • 24位圖:每個像素占用3個字節

加載大圖片到內存(掌握)

Android系統以ARGB表示每個像素,所以每個像素占用4個字節,很容易內存溢出

對圖片進行縮放(掌握)

  • 獲取屏幕寬高

    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);
    

在內存中創建圖片的副本(掌握)

直接加載的bitmap對象是只讀的,無法修改,要修改圖片只能在內存中創建出一個一模一樣的bitmap副本,然后修改副本

    //加載原圖
    Bitmap srcBm = BitmapFactory.decodeFile("sdcard/photo3.jpg");
    iv_src.setImageBitmap(srcBm);
    
    //創建與原圖大小一致的空白bitmap
    Bitmap 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);

對圖片進行特效處理(熟悉)

  • 首先定義一個矩陣對象

      Matrix mt = new Matrix();
    
  • 縮放效果

      //x軸縮放1倍,y軸縮放0.5倍
      mt.setScale(1, 0.5f);
    
  • 旋轉效果

      //以copyBm.getWidth() / 2, copyBm.getHeight() / 2點為軸點,順時旋轉30度
      mt.setRotate(30, copyBm.getWidth() / 2, copyBm.getHeight() / 2);
    
  • 平移

      //x軸坐標+10,y軸坐標+20
      mt.setTranslate(10, 20);
    
  • 鏡面

      //把X坐標都變成負數
      mt.setScale(-1, 1);
      //圖片整體向右移
      mt.postTranslate(copyBm.getWidth(), 0);
    
  • 倒影

      //把Y坐標都變成負數
      mt.setScale(1, -1);
      //圖片整體向下移
      mt.postTranslate(0, copyBm.getHeight());
    

畫畫板(掌握)

記錄用戶觸摸事件的XY坐標,繪制直線

  • 給ImageView設置觸摸偵聽,得到用戶的觸摸事件,并獲知用戶觸摸ImageView的坐標

      iv.setOnTouchListener(new OnTouchListener() {
          
          @Override
          public boolean onTouch(View v, MotionEvent event) {
              // TODO Auto-generated method stub
              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) {
          // TODO Auto-generated catch block
          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);
    

撕衣服(掌握)

  • 原理:把穿內衣和穿外衣的照片重疊顯示,內衣照在下面,用戶滑動屏幕時,觸摸的是外衣照,把手指經過的像素都置為透明,內衣照就顯示出來了

       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);
          }
      }
    

音樂播放器

播放服務(掌握)

  • 播放音頻的代碼應該運行在服務中,定義一個播放服務MusicService

  • 服務里定義play、stop、pause、continuePlay等方法

          private void play() {
              // TODO Auto-generated method stub
              player.reset();
              try {
                  player.setDataSource("sdcard/bzj.mp3");
                  player.prepare();
              } catch (Exception e) {
                  // TODO Auto-generated catch block
                  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);
    

根據播放進度設置進度條(掌握)

  • 獲取當前的播放時間和當前音頻的最長時間

      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);
          };
      };
    

拖動進度條改變播放進度

     //給sb設置一個拖動偵聽
     sb.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
        //停止拖動時調用
        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {
            // TODO Auto-generated method stub
            int progress = seekBar.getProgress();
            mi.seekTo(progress);
        }
        //開始拖動時調用            
        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {
            // TODO Auto-generated method stub
            
        }
        //拖動的時候不斷調用            
        @Override
        public void onProgressChanged(SeekBar seekBar, int progress,
                boolean fromUser) {
            // TODO Auto-generated method stub
            
        }
    });    

視頻播放器(熟悉)

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) {
              // TODO Auto-generated method stub
              
          }
          //SurfaceView創建時調用
          @Override
          public void surfaceCreated(SurfaceHolder holder) {
              // TODO Auto-generated method stub
              
          }
          
          @Override
          public void surfaceChanged(SurfaceHolder holder, int format, int width,
                  int height) {
              // TODO Auto-generated method stub
              
          }
      });
    
  • SurfaceView一旦不可見,就會被銷毀,一旦可見,就會被創建,銷毀時停止播放,再次創建時再開始播放


攝像頭(熟悉)

  • 啟動系統提供的拍照程序

      //隱式啟動系統提供的拍照Activity
      Intent 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);
    

使用Camera類完成照相(熟悉)

  • 查閱文檔
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,117評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,860評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,128評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,291評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,025評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,421評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,477評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,642評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,177評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,970評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,157評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,717評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,410評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,821評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,053評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,896評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,157評論 2 375

推薦閱讀更多精彩內容