Android Day 14 多媒体学习笔记

day14 Android多媒体

1. Android中图片的处理

1.1 加载分辨率过大的图片以及OOM异常

1.1.1 OOM产生的原因 – 内存溢出:当我们加载的图片的分辨率过大,就会出现OOM内存溢出错误。 这是因为安卓系统vm的最大内存申请极限是16M。而我们在处理图片的时候是根据图片的分辨率大小来创建内存的byte数组,如果数组过大,超过了内存的极限,就会报OOM异常。

1.1.2 获取bitmap需要BitmapFactory,调用里面的Bitmap.decodeFile()方法使用decodeFile这种方式是把图片文件所有的像素点都加载入内存。如果图片分辨率过大 虚拟机承受不了负担就会报OOM异常
Bitmap bitmap = BitmapFactory.decodeFile("/mnt/sdcard/hh.jpg")

解决的方法:只要图片的分辨率高于手机分辨率或者相同 就不会看出有任何不同的地方 所以首先要获取手机的分辨率 再跟图片的分辨率进行比例压缩就可以完美解决.

具体步骤:

  1. 获取手机屏幕api – getSystemService(WINDOW_SERVICE) 返回 WindowManager 对象 使用manager对象获取手机分辨率
  2. 获取图片分辨率api – BitmapFactory。decodeFile(“file”,opts) Option对象是安卓定义好的bitmap选项类 这个类中我们可以设置图片只读不解析加载到内存– bitmap.InJustDecodeBounds=true 这个属性设置为True 之后Decoder就会返回null 而不会返回位图 这样就可以不去真实的解析 bitmap 但可以查询宽跟高
    设置完这个参数之后decodeFile()文件后返回值就为null了
  3. 利用获取到的图片与屏幕宽和高 得出要缩放的比例 将Option选项中的opts.inSampleSize设置为缩放比例 就可以缩放文件了
  4. 最后还要真正解析图片 将前面设置好的opts传入decodeFile()方法中

    //加载分辨率过大图片方法
    public void load(View v){
    BitmapFactory.Options opts = new BitmapFactory.Options();
    //设置只获取图片的大小 而不需要直接加载图片进入内存
    opts.inJustDecodeBounds = true;
    //获取原图片宽高
    BitmapFactory.decodeFile("/mnt/sdcard/dog.jpg",opts);
    int imgWidth = opts.outWidth;
    int imgHeight = opts.outHeight;
    
    Log.i("MainActivity","imgWidth"+imgWidth+"~~~imgHeight"+imgHeight);
    
    //获取模拟器窗口信息
    WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
    
    Point outSize = new Point();
    wm.getDefaultDisplay().getSize(outSize);
    
    int screenWidth = outSize.x;
    int screenHeight = outSize.y;
    
    Log.i("MainActivity","screenWidth"+screenWidth+"~~~screenHeight"+screenHeight);
    
    //获取图片的宽 高缩放比例  通过比较比例 选取大的作为缩放比
    int scaleX = imgWidth/screenWidth;
    int scaleY = imgHeight/screenHeight;
    int scale = 1;
    
    if(scaleX>scaleY && scaleX>1){
    scale = scaleX;
    }else if(scaleX<scaleY && scaleY>1){
    scale = scaleY;
    }
    //设置图片缩放比
    opts.inSampleSize = scale;
    //重要:设置完成后 要将opts.inJustDecodeBounds属性改为false就可以加载图片了
    opts.inJustDecodeBounds = false;
    Bitmap bitmap = BitmapFactory.decodeFile("/mnt/sdcard/dog.jpg", opts);
    
    iv.setImageBitmap(bitmap);
    }
    

2. 创建图片的副本

先熟悉一下Android里面关于Canvas API的描述:To draw something, you need 4 basic components: A Bitmap to hold the pixels. a Canvas to host the draw calls(Writing into the bitmap).

简单可以理解为:Bitmap是用来存储像素的, Canvas是用来接收draw的调用(Draw的结果就是即将像素给画到Bitmap中)。 由此Bitmap其实就是用来直接展示在窗口上的一个显示对象,他是一个最终的产品。
那么怎么理解Canvas呢?Canvas有画布的意思,刚开始接触的时候 容易理解为画图应该直接在画布上完成即可。但是在Android中Canvas其实是一个媒介,是通过Canvas来调用drawLine,drawCircle方法,最终展示的都是像素,而只有Bitmap可以保存像素,canvas并不能。所以创建Canvas的时候一定要传递一个Bitmap对象,用来承载在Canvas上画的内容。 可以更好的理解为 Bitmap是一张不可以直接画图的图像,只有在这张图像上放Canvas这个画布,然后就可以在画布上开始作图,最后掀开画布,这个图对应的像素点就存储到Bitmap中了。 而不是很多人说的白纸 这样会有很多人误解为 作图应该直接在纸上化成即可。

重点步骤

  1. 获取要拷贝的原图 获取原图的代码如下。
    Bitmap bitmap = BitmapFactory.decodeResource(Resource res, int id):这行代码作用是获取到资源文件下的图片。
  2. 创建一个Bitmap副本,这里要用到一个Bitmap类中的静态方法createBitmap() : 根据原图的尺寸配置创建副本。 注意: 这里获取到的newBitmap只是一个位图的配置对象,并不是拷贝好的图片。
    Bitmap newBitmap = Bitmap.createBitmap(int width,int height,Config config)

    width = bitmap.getWidth()

    height = bitmap.getHeight()

    config = bitmap.getConfig()

  3. 创建画板跟画笔对象。 Google提供了一个Canvas类 目的就是为了作图
    Canvas canvas = new Canvas(newBitmap)
    Paint paint = new Paint()
    paint.setColor(Color.BLACK)
  4. 作图用的是canvas中的drawBitmap()方法
    canvas.drawBitmap(bitmap,Matrix matrix,Paint paint)
    Matrix是Google定义的矩阵类,他的作用主要是来放大或者缩小图片
    Matrix matrix = new Matrix()
    matrix.setScale(2,2) 就是将图片的放大两倍

Matrix介绍:这里不得不提到Matrix矩阵的概念。相信大家很多搞程序的都不知道或者学过也也早已经还给老师了。矩阵就是由方程组的系数以及常数所构成的方阵,用在解线性方程组上既方便又直观。 其实就是解线性方程的组的一系列理论。高数很重要,还在上大学的朋友们一定且行且珍惜。 Android中Google已经帮我们定义好了矩阵这个类处理图像,所以直接可以使用。其实原理就是处理每一个像素点的坐标。所以Goolge用Matrix矩阵封装了关于图片处理的方法:

matrix.setRotate(degrees,px,py) – 设置旋转(旋转度数,x轴中心点,y轴中心点)

matrix.setScale() – 设置放大缩小

matrix.setTranslate(dx,dy) – 设置位移

//将图片放大
public void turnBig(View v) {

    Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.tomcat);

    Matrix matrix = new Matrix();
    matrix.setScale(2,2);
    Paint paint = new Paint();
    paint.setColor(Color.BLUE);

    Bitmap newBitmap = Bitmap.createBitmap(bitmap.getWidth() * 2, bitmap.getHeight() * 2, bitmap.getConfig());
    Canvas canvas = new Canvas(newBitmap);

    canvas.drawBitmap(bitmap,matrix,paint);

    iv.setImageBitmap(newBitmap);

}
//图片的左右移动
public void turnRight(View v){
    Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.tomcat);

    Bitmap newBitmap = bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), bitmap.getConfig());

    dx++;
    Canvas canvas = new Canvas(newBitmap);
    Paint paint = new Paint();
    paint.setColor(Color.BLACK);

    Matrix matrix = new Matrix();
    matrix.setTranslate(dx,0);

    canvas.drawBitmap(bitmap,matrix,paint);


    iv.setImageBitmap(newBitmap);
}
//图片的旋转
int degrees = 1;
public void rotate(View v){
    Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.tomcat);
    Bitmap newBitmap = bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), bitmap.getConfig());

    Canvas canvas = new Canvas(newBitmap);

    Matrix matrix = new Matrix();
    Paint paint = new Paint();

    degrees--;
    matrix.setRotate(degrees,bitmap.getWidth()/2,bitmap.getHeight()/2);

    canvas.drawBitmap(bitmap,matrix,paint);

    iv.setImageBitmap(newBitmap);
}

小知识点:

1. 当我们要获取到res目录下的文件的时候,我们就可以使用decodeResource(Resource res, int id)方法了 参数res我们直接使用API getResource()方法.谷歌封装好的调用系统资源方法,可以获取到所有资源文件 eg 图片 layout string… id就是要找的资源R.id….

2. 由于我们要修改图片时,原图是无法修改的。,所以必须要创建一个原图的copy副本。在副本上面才可以修改 使用bitmap.createBitmap来创建 API: createBitmap(int width,int height, Config config) 这个方法config参数指的是文件的配置文件 – eg 是多少色图/位图… 由于是创建副本 使用bitmap.getConfig();

3.综合案例练习

3.1 画画板

要求:画图 将图片保存 设置画笔颜色 public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    ImageView iv;
    float startX;
    float startY;
    float endX;
    float endY;

    private Bitmap bitmap;
    private Paint paint;
    private Canvas canvas;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        iv = (ImageView) findViewById(R.id.iv);

        WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);

        int width = wm.getDefaultDisplay().getWidth();
        int height = wm.getDefaultDisplay().getHeight();

        bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);

        canvas = new Canvas(bitmap);

        paint = new Paint();
        paint.setColor(Color.BLACK);

        TextView tv_red = (TextView) findViewById(R.id.tv_red);
        TextView tv_green = (TextView) findViewById(R.id.tv_green);
        TextView tv_blue = (TextView) findViewById(R.id.tv_blue);

        tv_red.setOnClickListener(this);
        tv_blue.setOnClickListener(this);
        tv_green.setOnClickListener(this);


        iv.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:

                        startX = event.getRawX();

                        startY = event.getRawY();

                        Log.i("MainActivity", "Touched");
                        break;
                    case MotionEvent.ACTION_MOVE:
                        Log.i("MainActivity", "Moving");
                        endX = event.getRawX();
                        endY = event.getRawY();
                        canvas.drawLine(startX, startY, endX, endY, paint);
                        startX = endX;
                        startY = endY;
                        iv.setImageBitmap(bitmap);
                        break;
                    case MotionEvent.ACTION_UP:
                        Log.i("MainActivity", "Released");
                }
                return true;
            }
        });
    }

    public void save(View v){
        try {
            File file = new File(Environment.getExternalStorageDirectory().getPath(),System.currentTimeMillis()+".jpg");
            FileOutputStream fos = new FileOutputStream(file);
            bitmap.compress(Bitmap.CompressFormat.JPEG,100,fos);

            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.tv_blue:
                paint.setColor(Color.BLUE);
                Toast.makeText(getApplicationContext(),"Blue Selected",Toast.LENGTH_SHORT).show();
                break;
            case R.id.tv_green:
                paint.setColor(Color.GREEN);
                Toast.makeText(getApplicationContext(),"Green Selected",Toast.LENGTH_SHORT).show();
                break;
            case R.id.tv_red:
                paint.setColor(Color.RED);
                Toast.makeText(getApplicationContext(),"Red Selected",Toast.LENGTH_SHORT).show();
                break;
        }
    }
}

2 Android 音乐播放 - MediaPlayer

核心:MediaPlayer的生命周期

  1. Idle :空闲状态 setDataSource()
  2. Initialized:有两个方法 prepare()同步(播放本地音乐) prepareAsync异步准备(播放网络音乐)!重点 在异步准备中有一个preparing生命周期 播放网络音乐要设置setOnpreparedLinstener监听.

    mediaPlayer.prepareAsync();
    mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
    @Override
    public void onPrepared(MediaPlayer mp) {
    mediaPlayer.start();
    }
    });    
    
  3. Prepared:seekTo() start()

  4. Started:pause() stop()单向模式 OnCompletionListener
  5. Paused
  6. Stoped

  7. MediaPlayer的使用

    public class MainActivity extends AppCompatActivity {
        private static SeekBar seekBar;
        private Iservice iservice;
    
        public static Handler handler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
            Bundle data = msg.getData();
            int duration = data.getInt("duration");
            int currentPosition = data.getInt("currentPosition");
    
            seekBar.setMax(duration);
            seekBar.setProgress(currentPosition);
            }
        };
        @Override
        protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    
        Intent intent  = new Intent(this,MusicPlayerService.class);
        startService(intent);
    
        MyConn conn = new MyConn();
    
        bindService(intent,conn,BIND_AUTO_CREATE);
    
        seekBar = (SeekBar) findViewById(R.id.seekBar);
    
        seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
        @Override
        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
    
        }
    
        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {
    
        }
    
        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {
        iservice.callPlayMusicPosition(seekBar.getProgress());
        }
        });
    
        }
    
        public void play(View v){
        iservice.callPlay();
        }
    
        public void pause(View v){
        iservice.callPause();
        }
    
        public void restart(View v){
        iservice.callRestart();
    
        }
    
        public class MyConn implements ServiceConnection{
    
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
    
        iservice = (Iservice) service;
        }
    
        @Override
        public void onServiceDisconnected(ComponentName name) {
    
        }
        }
    }
    
  8. Service服务中播放音乐

    public class MusicPlayerService extends Service {
    
    MediaPlayer mediaPlayer;
    
    @Override
    public IBinder onBind(Intent intent) {
        return new MyBinder();
    }
    
    @Override
    public void onCreate() {
    
        mediaPlayer = new MediaPlayer();
        try {
            mediaPlayer.setDataSource("/mnt/sdcard/Bruno Mars - Talking to the Moon.mp3");
            mediaPlayer.prepare();
        } catch (IOException e) {
            e.printStackTrace();
        }
    
        super.onCreate();
    }
    
    public void play() {
        mediaPlayer.start();
        updateSeekBar();
    }   
    
    public void pause() {
        mediaPlayer.pause();
    }
    
    public void restart() {
        mediaPlayer.start();
    }
    
    public void playMusicPosition(int position){
    
        mediaPlayer.seekTo(position);
    
    }
    
    public class MyBinder extends Binder implements Iservice {
    
        @Override
        public void callPlay() {
            play();
        }
    
        @Override
        public void callPause() {
            pause();
        }
    
        @Override
        public void callRestart() {
            restart();
        }
    
        @Override
        public void callPlayMusicPosition(int position) {
            playMusicPosition(position);
        }
    }
    }
    
  9. SeekBar进度条更新

    private void updateSeekBar() {
        //获取当前歌曲的总进度
        final int duration = mediaPlayer.getDuration();
        final Timer timer = new Timer();
        final TimerTask task = new TimerTask() {
            @Override
            public void run() {
                int currentPosition = mediaPlayer.getCurrentPosition();
    
                Message msg = Message.obtain();
    
                Bundle bundle = new Bundle();
                bundle.putInt("duration",duration);
                bundle.putInt("currentPosition",currentPosition);
    
                msg.setData(bundle);
    
                MainActivity.handler.sendMessage(msg);
            }
        };
    
        timer.schedule(task,1000,1000);
    
        mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mp) {
                Toast.makeText(getApplicationContext(),"Music Play Finished",Toast.LENGTH_SHORT).show();
                timer.cancel();
                task.cancel();
            }
        });
    }
    

3 Android 视频播放 – MediaPlayer

3.1 surfaceView – 重量级控件 跟普通TextView Button等空间不同 当我们使用surfaceView的时候 无法按正常获取空间的方式直接播放视频。 这是因为surfaceView是重量级控件,加载需要时间 否则无法拿到这个类的的实例,SurfaceView没有被初始化就无法获取到Holder。 这个时候可以通过surfaceView的生命周期方法来播放视频。

  1. 使用SurfaceView必须要拿到surfaceHolder–在控件中获取holder。
  2. 通过使用回调函数holder.addCallback()中surfaceCreated方法就可以确定surfaceView已经初始化成功

    holder.addCallback(new SurfaceHolder.Callback() {
        //当该方法调用的时候意味着SurfaceView已经初始化好 可以使用了
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
    
        }
    
        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    
        }
        //当播放界面被切换出去就会执行destroy方法   
        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            if(mediaPlayer.isPlaying()&&mediaPlayer!=null) {
                currentPosition = mediaPlayer.getCurrentPosition();
    
                mediaPlayer.stop();
                mediaPlayer.reset();
                mediaPlayer.release();
        }
    });
    

对比播放音乐 一定要切记使用SurfaceView播放video时要使用mediaPlayer.setDisplay(holder) 并且将holder传入 因为视频播放时依赖于载体的 而Holder就是用来维护视频播放器的 重点

3.2 videoView 继承自SurfaceView 相当于是Surface的自定义控件 适合用于简单地播放视频广告

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //[1]找到控件    测量  排版 绘制 
        VideoView vv = (VideoView) findViewById(R.id.vv);
        //[2]设置视频播放的路径
        vv.setVideoPath("http://192.168.77.83:8080/cc.mp4");
        //[3]开始播放 
        vv.start();

    }
}

3.3 Vitamo 视频播放框架 可以直接引入整个类库 – 就是一个完整的应用

由于Google提供的MediaPlayer只支持MP4 3GP类型的文件格式 所以这个时候三方类库就比较好用了 Vitamo可以支持基本市面上所有的视频播放

主要步骤

1 引入vitamio框架 以library、

2 在布局中定义VideoView

<io.vov.vitamio.widget.VideoView 
android:id="@+id/vv"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>

3 mainactivity代码

//插件vitamio框架检查是否可用
if (!LibsChecker.checkVitamioLibs(this)) {
    return;
}

final VideoView vv = (VideoView) findViewById(R.id.vv);
vv.setVideoPath("http://192.168.1.2:8080/haha.avi");
vv.setOnPreparedListener(new OnPreparedListener() {

    @Override
    public void onPrepared(MediaPlayer mp) {
        vv.start();

    }
});
//设置video的控制器
vv.setMediaController(new MediaController(this));

4 一定要在清单文件初始化InitActivity

<activity android:name="io.vov.vitamio.activity.InitActivity"></activity>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值