05Android學習從零單排之Androidmultimedia(多媒體)

05Android學習從零單排之AndroidMultimedia(多媒體)

讀了那麼多年的書讓我明白一個道理。人要穩重,不要想到啥就做啥。做一行越久即使你不會,幾年之後慢慢的你也會了,加上一點努力你或許你能成爲別人眼中的專家。

注:本章blog主要學習Android下圖片、音頻、視頻的處理。

Android下大圖片的處理

在平時開發中,如果我們加載一個圖片分辨率或者加載批量圖片,超過了手機的屏幕分辨率或者內存空間時,就會導致加載圖片不成功,並會報OOM異常,所以爲了解決這個問題,我們就需要通過一定比例的縮放,方便圖片加載。

圖片資源

大圖片縮放的步驟

  1. 1、獲取當前手機屏幕的寬和高(分辨率)。
  2. 2、獲取圖片的寬和高。
  3. 3、用圖片的分辨率除以當前屏幕的分辨率。
  4. 4、加載縮放後的圖片資源。

圖片資源

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ImageView imageView = (ImageView) findViewById(R.id.iv);
//        Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.dog);//執行到這裏就是OOM了
//        imageView.setImageBitmap(bitmap);
        //獲取當前屏幕的寬高
        WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
        Point point = new Point();
        //TODO 注意測量屏幕的寬高和下面測量圖片的寬高,這種寫法,以前沒遇到過。
        /**
         * 基本順序是 wm.getDefaultDisplay().getSize發現需要Point對象,就去new了
         * 但是new完之後,Point不能直接使用,例如,不能直接point.x。
         * 需要被wm裝載到容器之後,才能執行point.x。不然獲取不到屏幕的寬高。
         */
        wm.getDefaultDisplay().getSize(point);
        int x = point.x;
        int y = point.y;
        Toast.makeText(this, "當前屏幕分辨率"+x+"*"+y, Toast.LENGTH_SHORT).show();
        BitmapFactory.Options options = new BitmapFactory.Options();
        //通過bitmap獲取的資源圖片的信息,而不是加載圖片資源。
        options.inJustDecodeBounds = true;
        //Options
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.dog,options);
        //獲取圖片的寬高,方法同上
        int outWidth = options.outWidth;
        int outHeight = options.outHeight;
        Toast.makeText(this, "獲取到圖片的分辨率:"+outWidth+"*"+outHeight, Toast.LENGTH_SHORT).show();

        //計算縮放比例
        int scale = 1;
        int scaleX = outWidth / x;
        int scaleY = outHeight / y;
        /**
         * scaleX>=scleY    是判斷寬高比例
         * scaleX>scale     是判斷圖片像素除以屏幕像素是否大於1,也就是不能比屏幕小才做縮放。
         */
        if (scaleX>=scaleY&&scaleX>scale) {
            scale = scaleX;
        }else if (scaleY >= scaleX && scaleY>scale) {
            scale = scaleY;
        }
        //inSampleSize 可以把它看成setSampleSize,就是設置縮放後的圖片大小。
        options.inSampleSize = scale;
        //開始真正加載圖片資源了,不是獲取圖片信息了。
        options.inJustDecodeBounds = false;
        Bitmap bit = BitmapFactory.decodeResource(getResources(), R.drawable.dog,options);
        int scaleWidth = options.outWidth;
        int scaleHeight = options.outHeight;
        Toast.makeText(this, "縮放後的圖片分辨率:"+scaleWidth+"*"+scaleHeight, Toast.LENGTH_SHORT).show();
        imageView.setImageBitmap(bit);
    }

Android對圖片的修改操作

Android是不允許對圖片的源文件進行直接修改的,必須要先複製改圖片,才能對圖片進行修改或塗鴉。

創建一個原圖的副本(即複製圖片)

圖片資源


@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        btnRotate.setOnClickListener(this);
        btnMove.setOnClickListener(this);
        btnOverturn.setOnClickListener(this);
        btnReflection.setOnClickListener(this);
        resource = BitmapFactory.decodeResource(getResources(), R.drawable.meinv);
        iv1.setImageBitmap(resource);
        //給第二個ImageView設置和第一個一樣的圖片
        bitmap = Bitmap.createBitmap(resource.getWidth()*2, resource.getHeight()*2, resource.getConfig());
        //準備畫布
        canvas = new Canvas(bitmap);
        //開始作畫,參照第一張圖片 Paint可以理解爲筆
        matrix = new Matrix();
//        matrix.setRotate(30, bitmap.getWidth()/2, bitmap.getHeight()/2);
        canvas.drawBitmap(resource, matrix, new Paint());
        iv2.setImageBitmap(bitmap);
    }

對創建的圖片進行“旋轉”、“移動”等操作。

  • 1、旋轉
    matrix.setRotate(30);
  • 2、平移
    matrix.setTranslate(20, 0);
  • 3、縮放
    matrix.setScale(0.5f, 0.5f);
  • 4、倒影
    //對圖片進行倒影效果
    matrix.setScale(1.0f, -1.0f);
    matrix.postTranslate(0, copyBitmap.getHeight());
  • 5、翻轉
    //對圖片進行鏡面
    matrix.setScale(-1.0f, 1.0f);
    matrix.postTranslate(copyBitmap.getWidth(), 0);

圖片資源

public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_rotate:
                matrix.setRotate(30,bitmap.getWidth()/2,bitmap.getHeight()/2);
                canvas.drawBitmap(resource, matrix, new Paint());
                iv2.setImageBitmap(bitmap);
                break;
            case R.id.btn_move:
                matrix.setTranslate(20,20);
                canvas.drawBitmap(resource, matrix, new Paint());
                iv2.setImageBitmap(bitmap);
                break;
            case R.id.btn_overturn:
                //對圖片進行鏡面
                matrix.setScale(-1.0f, 1.0f);
                matrix.postTranslate(bitmap.getWidth(), 0);
                canvas.drawBitmap(resource, matrix, new Paint());
                iv2.setImageBitmap(bitmap);
                break;
            case R.id.btn_reflection:
                //對圖片進行倒影效果
                matrix.setScale(1.0f, -1.0f);
                matrix.postTranslate(0, bitmap.getHeight());
                canvas.drawBitmap(resource, matrix, new Paint());
                iv2.setImageBitmap(bitmap);
                break;
        }

Android版塗鴉板

通過Bitmap複製一張圖片作爲背景,然後獲取到觸摸事件,不斷的在這張圖上作畫。
最後發送一條廣播,通知圖庫更新。

圖片資源


protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        iv = (ImageView) findViewById(R.id.iv);
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.bb);
//        iv.setImageBitmap(bitmap);
        //複製了一直帶有屬性的畫板
        newBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), bitmap.getConfig());
        //準備畫布
        final Canvas canvas = new Canvas(newBitmap);
        final Paint paint = new Paint();
        canvas.drawBitmap(bitmap,new Matrix(),paint);
        //在畫布上畫一條線
//        canvas.drawLine(10,10,200,100,paint);
        iv.setOnTouchListener(new View.OnTouchListener() {
            int downX = 0;
            int downY = 0;
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        downX = (int) event.getX();
                        downY = (int) event.getY();
//                        Toast.makeText(MainActivity.this, "當前按下的座標X:"+downX+"Y:"+downY, Toast.LENGTH_SHORT).show();
                        break;
                    case MotionEvent.ACTION_MOVE:
                        int moveX = (int) event.getX();
                        int moveY = (int) event.getY();
                        paint.setStrokeWidth(3);
//                        System.out.println("當前移動座標X:" + moveX + "Y:" + moveY);
                        canvas.drawLine(downX,downY,moveX,moveY,paint);
                        iv.setImageBitmap(newBitmap);
                        //重新計算座標
                        downX = (int) event.getX();
                        downY = (int) event.getY();
                        break;
                    case MotionEvent.ACTION_UP:

                        break;
                }
                return true;
            }
        });
        iv.setImageBitmap(newBitmap);

    }
    public void saveImage(View view){
        String path = Environment.getExternalStorageDirectory().getPath() + "/Download/" + "哈哈.png";
//        String path = getFilesDir().getPath() + "/哈哈.png";
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(path);
            boolean compress = newBitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
            if (compress) {
                //發送一條廣播,更新圖庫(已失效。)
//                Intent intent = new Intent();
//                intent.setAction(Intent.ACTION_MEDIA_MOUNTED);
//                intent.setData(Uri.parse("file://"+getFilesDir()));
//                sendBroadcast(intent);
                //發送廣播,通知圖庫更新(掃描文件)。
                sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + Environment.getExternalStorageDirectory().getPath() + "/Download/")));
                fos.close();
                Toast.makeText(this, "塗鴉保存成功", Toast.LENGTH_SHORT).show();
            }else {
                Toast.makeText(this, "保存失敗", Toast.LENGTH_SHORT).show();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

Android音頻播放

相對來說Android下的音視頻播放還是比較簡單的,都被封裝好在MediaPlay裏面了,簡單的Demo,幾行代碼就可以了。

public void startOpen(View view){
        //初始化MediaPlay,根據官網copy一份,改下path就行了
        MediaPlayer mediaPlayer = new MediaPlayer();
        mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
        try {
            mediaPlayer.setDataSource("http://10.0.2.2/xpg.mp3");
            mediaPlayer.prepare();
            mediaPlayer.start();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

複雜點的,帶服務後臺播放加顯示播放進度的Mp3播放器

圖片資源

  • 混合方式開啓音樂服務(就是爲了調用服務裏面的方法)
public class MainActivity extends AppCompatActivity {

    private static final String TAG = "Hsia";
    private Iservices iservices;
    private static SeekBar mSB;
    private MyConn myConn;
    public static Handler handler = new Handler(){

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            Bundle data = msg.getData();
            //總大小
            duration = data.getInt("duration");
            //當前位置
            currentPosition = data.getInt("currentPosition");

            mSB.setMax(duration);
            mSB.setProgress(currentPosition);
        }
    };
    private static int currentPosition;
    private static int duration;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d(TAG, "onCreate: ");
        mSB = ((SeekBar) findViewById(R.id.sb));
//        mSB.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
//            //當SeekBar進度改變時
//            @Override
//            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
//                int position = seekBar.getProgress();  //獲取當前播放的進度
//                //調用服務裏面的方法更新更新到制定位置
//                iservices.callsetMusicProgress(position);
//            }
//
//            @Override
//            public void onStartTrackingTouch(SeekBar seekBar) {
//
//            }
//
//            @Override
//            public void onStopTrackingTouch(SeekBar seekBar) {
//
//            }
//        });

        mSB.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            //停止拖動回調
            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                int position = seekBar.getProgress();  //獲取當前播放的進度
                iservices.callsetMusicProgress(position);

            }
            //開始拖動
            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }
            //進度發生了改變
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress,
                                          boolean fromUser) {

            }
        });

        Intent intent = new Intent(this,MusicServer.class);
        startService(intent);
        myConn = new MyConn();
        bindService(intent, myConn,BIND_AUTO_CREATE);
    }
    class MyConn implements ServiceConnection{

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.d(TAG, "onServiceConnected: ");
            iservices = (Iservices)service;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    }

    //開始
    public void startMusic(View view) {
        iservices.callStartMusic();
    }

    //暫停
    public void stopMusic(View view) {
        iservices.callStopMusic();
    }

    //重新播放
    public void restartMusic(View view) {
        iservices.callRestartMusic();
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(myConn);
    }
}
  • 在服務裏面初始化MediaPlay(Iservices接口就不貼出來了)
public class MusicServer extends Service {

    private static final String TAG = "Hsia";
    private MediaPlayer mediaPlayer;

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new MyBind();
    }

    @Override
    public void onCreate() {
        super.onCreate();
        //初始化MediaPlay,根據官網copy一份,改下path就行了
        mediaPlayer = new MediaPlayer();
        mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
        try {
            mediaPlayer.setDataSource(Environment.getExternalStorageDirectory().getPath()+"/Download/"+"zxxpg.mp3");
            mediaPlayer.prepareAsync();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    private void startMusicService() {
        mediaPlayer.start();
        mediaPlayer.setLooping(false);
        //更新進度條
        updateSeekBar();
    }

    private void updateSeekBar() {
        //獲取音頻的總文件長度
        final int duration = mediaPlayer.getDuration();
        //添加一個定時器,不斷的更新SeekBar
        Timer timer = new Timer();
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                //獲取當前播放進度
                int currentPosition = mediaPlayer.getCurrentPosition();
                //把數據發送出去
                Message message = new Message();
                Bundle bundle = new Bundle();
                bundle.putInt("duration",duration);
                bundle.putInt("currentPosition",currentPosition);
                message.setData(bundle);
                MainActivity.handler.sendMessage(message);
            }
        };
        timer.schedule(timerTask,10,1000);
    }

    private void stopMusicService() {
        mediaPlayer.pause();
    }

    private void restartMusicService() {
        mediaPlayer.start();
    }

    private void setMusicProgress(int progress){
        mediaPlayer.seekTo(progress);
    }

    //定義一箇中間人對象,把想要暴露出去的方法暴露出去
    class MyBind extends Binder implements Iservices {

        @Override
        public void callStartMusic() {
            startMusicService();
        }

        @Override
        public void callStopMusic() {
            stopMusicService();
        }

        @Override
        public void callRestartMusic() {
            restartMusicService();
        }

        @Override
        public void callsetMusicProgress(int progress) {
            setMusicProgress(progress);
        }

    }

}

Android視頻播放

VideoView實現(封裝好的簡單幾行代碼)

圖片資源

VideoView mVideo = (VideoView) findViewById(R.id.vv);
        mVideo.setVideoPath("http://10.0.2.2/vd.3gp");
        //添加一個進度條
        mVideo.setMediaController(new MediaController(this));
        mVideo.start();

SurfaceView實現(VideoView是基於SurfaceView的封裝)

SurfaceView mSV = (SurfaceView) findViewById(R.id.sv);
        final SurfaceHolder holder = mSV.getHolder();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(100);
                    final MediaPlayer mediaPlayer = new MediaPlayer();
                    //設置播放數據源
                    mediaPlayer.setDataSource("http://10.0.2.2/vd.mp4");
                    //準備播放
                    mediaPlayer.prepareAsync();
                    mediaPlayer.setDisplay(holder);
                    mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
                        @Override
                        public void onPrepared(MediaPlayer mp) {
                        mediaPlayer.start();
                        }
                    });
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });

Android萬能播放器

是通過使用vitamio第三方工程來實現的。官網
www.vitamio.org
- 1、導入vitamio庫工程到AndroidStudio中。
AndroidStudio導入第三方庫工程
- 2、vitamio基本用法和VideoView差不多。

Android拍照和攝像

打開照相機

 //打開照相機
    public void openC(View view) {
        //創建意圖對象
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(Environment.getExternalStorageDirectory().getPath()+"/Download", "paizhao.png"))); // set the image file name
        //開啓意圖   獲取結果
        startActivityForResult(intent, 0);
    }

打開攝像機

//打開攝像機
    public void openS(View view) {
//創建意圖對象
        Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(Environment.getExternalStorageDirectory().getPath()+"/Download", "luxiang.3gp"))); // set the image file name
        //開啓意圖   獲取結果
        startActivityForResult(intent, 0);
    }

關於作者
- 個人網站:北京互聯科技
- Email:[email protected]
- 項目地址:https://github.com/swordman20/Hsia05AndroidMultimedia.git

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章