Android - 图片的处理,触摸监听事件,音乐播放,SoundPool,VideoView,视频播放,SurfaceView,拍照录像,传感器

转载请注明出处:https://blog.csdn.net/mythmayor/article/details/72876850

1.图形的表示方法

位图:

由许多点组成的点阵图。构成位图的点称为像素。目前Android中使用的都是位图。

位图大小的计算
  1. 单色 = 1位 = 八分之一byte,每个像素占用八分之一byte
    200 * 200 / 8 = 5000
  2. 2的24次幂色(约1600万) = 24位 = 3byte,每个像素占用3byte
    200 * 200 * 3 = 120000
  3. 256色 = 8位 = 1byte,每个像素占用1byte
    200 * 200 = 40000
矢量图:

矢量图形是通过计算机将一串线条和图形转换为一系列指令,在计算机中只存储这些指令,而不是像素。矢量图形看起来没有位图图像真实,但矢量图形的存储空间比位图图像要小得多,而且矢量图形通过拉伸、移动、放大等操作,图形不会产生实真。

2.如何加载图片到内存中

//加载一个图片到内存
Bitmap bitmap = BitmapFactory.decodeFile("mnt/sdcard/big.jpg");

3.加载大图片时存在什么问题

  1. 存在的问题
    • 加载大图片会出现内存溢出的异常(OOM)
  2. 产生的原因
    • Android中的图片使用32位的ARGB模式,A-透明度、R-红色值、G-绿色值、B-蓝色值。每个像素都要占用4个byte。
    • 用图片的分辨率乘以4就得到了图片在Android中所需的内存空间大约为15M,模拟器默认每个应用占用的内存为16M,所以就产生了内存溢出(OOM)

4.加载大图片的处理方法

  1. 思路
    • 先获取图片的分辨率和手机的分辨率,计算出压缩比,加载时加载压缩后的图片
  2. 实现

    Bitmap bitmap = decodeSampledBitmapFromResource("mnt/sdcard/big.jpg", screenWidth, screenHeight);
    iv.setImageBitmap(bitmap);
    
    /**
     * 获取压缩后的图片
     * @param pathName 图片的路径
     * @param reqWidth 要显示的宽
     * @param reqHeight 要显示的高
     */
    public static Bitmap decodeSampledBitmapFromResource(String pathName,  
        int reqWidth, int reqHeight) {  
        //1.第一次解析将inJustDecodeBounds设置为true,来获取图片大小  
        final BitmapFactory.Options options = new BitmapFactory.Options();  
        options.inJustDecodeBounds = true;  
        BitmapFactory.decodeFile(pathName, options);  
    
        //2.调用计算压缩比的工具方法,计算出 inSampleSize的值
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); 
    
        //3.使用计算好的压缩比,获得压缩后的图片
        options.inJustDecodeBounds = false;  
        return BitmapFactory.decodeFile(pathName, options);  
    }  
    
    /**
     * 计算压缩比
     * @param options 加载图片的参数
     * @param reqWidth 要显示的宽
     * @param reqHeight 要显示的高
     */
    public static int calculateInSampleSize(BitmapFactory.Options options,  
         int reqWidth, int reqHeight) {  
        //源图片的高度和宽度  
        final int height = options.outHeight;  
        final int width = options.outWidth;  
        int inSampleSize = 1;  
    
        //如果源图片的宽高大于要显示的宽高,则需要压缩
        if (height > reqHeight || width > reqWidth) {  
            // 计算出实际宽高和目标宽高的比率  
            final int heightRatio = Math.round((float) height / (float) reqHeight);  
            final int widthRatio = Math.round((float) width / (float) reqWidth);  
            // 选择宽和高中最小的比率作为inSampleSize的值,这样可以保证最终图片的宽和高  
            // 一定都会大于等于目标的宽和高。  
            inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;  
        }  
        return inSampleSize;  
    }
    

5.如何创建一个缩放的图片

  1. 创建一个空白的bitmap,宽高信息和原图保存一致

    Bitmap copyBitmap = Bitmap.createBitmap(srcBitmap.getWidth(), srcBitmap.getHeight(),    srcBitmap.getConfig());
    
  2. 创建一个画板

    Canvas canvas = new Canvas(copyBitmap);
    
  3. 创建画笔

    Paint paint = new Paint();
    
  4. 作画

    Matrix matrix = new Matrix();
    //matrix 变化矩阵   
    matrix.setScale(0.6f, 0.6f);
    canvas.drawBitmap(srcBitmap, matrix, paint);
    

6.矩阵的缩放、平移、旋转

//缩放,参数为:x轴的缩放比例,y轴的缩放比例
matrix.setScale(0.6f, 0.6f);
//平移,参数为:x轴的平移距离,y轴的平移距离
matrix.setTranslate(100, 100);
//旋转,参数为:旋转的角度,中心点的x轴坐标,中心点的y轴坐标
matrix.setRotate(30, srcBitmap.getWidth()/2, srcBitmap.getHeight()/2);

7.矩阵的缩放、平移、旋转的叠加使用

Matrix提供了四种操作:translate(平移)、rotate(旋转)、scale(缩放)、skew(倾斜)

  1. pre是在队列最前面插入,post是在队列最后面追加,而set先清空队列在添加
  2. 下面通过一些例子具体说明:

    matrix.preScale(2f,1f);    
    matrix.preTranslate(5f, 0f);   
    matrix.postScale(0.2f, 1f);    
    matrix.postTranslate(0.5f, 0f);  
    执行顺序:translate(5, 0) -> scale(2f, 1f) -> scale(0.2f, 1f) -> translate(0.5f, 0f) 
    
    matrix.postTranslate(2f, 0f);   
    matrix.preScale(0.2f, 1f);     
    matrix.setScale(1f, 1f);   
    matrix.postScale(5f, 1f);   
    matrix.preTranslate(0.5f, 0f);   
    执行顺序:translate(0.5f, 0f) -> scale(1f, 1f) ->  scale(5f, 1)
    

8.SeekBar的监听事件

seekbar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {

    //停止滑动时调用,只调用一次
    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {

    }

    //开始滑动时调用,只调用一次
    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {

    }

    //进度发生改变时调用,可能调用很多次
    @Override
    public void onProgressChanged(SeekBar seekBar, int progress,
            boolean fromUser) {

    }
});

9.控件的触摸监听事件onTouchListener

  • 触摸事件中有三个重要的事件

    • MotionEvent.ACTION_DOWN 按下,只触发一次
    • MotionEvent.ACTION_MOVE 移动,可触发多次
    • MotionEvent.ACTION_UP 抬起,只触发一次
  • 随手涂鸦的核心逻辑

    iv.setOnTouchListener(new OnTouchListener() {
        int startX;
        int startY;
    
        @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();
                canvas.drawLine(startX, startY, newX, newY, paint);
                iv.setImageBitmap(alterBitmap);
    
                // 重新初始化起始位置
                startX = (int) event.getX();
                startY = (int) event.getY();
                break;
            case MotionEvent.ACTION_UP:// 离开
                break;
            }
            return true;//false代表的是事件没有处理完毕,等待事件处理完毕。true代表事件已经处理完毕了,事件结束被消费掉了,继续下一个事件。
        }
    });
    

10.在代码里使用dip单位

在代码里填写的数字一般都是以px为单位的,下面是将px转化为dip的方法

DisplayMetrics displaysMetrics = new DisplayMetrics(); 
getWindowManager().getDefaultDisplay().getMetrics(displaysMetrics); 
int dipValue = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 320, displaysMetrics);

11.保存bitmap到手机中

File file = new File(Environment.getExternalStorageDirectory(),
        SystemClock.currentThreadTimeMillis() + ".jpg");
FileOutputStream stream = new FileOutputStream(file);
//参数分别为:图片格式,图片质量,输出流
alterBitmap.compress(CompressFormat.JPEG, 100, stream);
stream.close();

12.向手机里添加图片后,如何通知图库应用更新

  1. 发送一个SD卡挂载的广播

    Intent intent = new Intent();
    intent.setAction(Intent.ACTION_MEDIA_MOUNTED);
    intent.setData(Uri.fromFile(Environment.getExternalStorageDirectory()));
    sendBroadcast(intent);
    
  2. 发送一个通知媒体扫描器扫描文件的广播

    Intent intent = new Intent();
    intent.setAction(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
    intent.setData(Uri.fromFile(file));
    sendBroadcast(intent);
    
  3. 直接使用MediaScannerConnection扫描文件

    MediaScannerConnection.scanFile(this, new String[]{file.toString()}, null, null);
    

13.设置bitmap某个像素点为某个颜色

//设置横坐标为x,纵坐标为y的点的颜色为color
Bitmap.setPixel(int x, int y, int color)

14.getX和getRawX的区别

  • getRowX:触摸点相对于屏幕的坐标
  • getX: 触摸点相对于按钮的坐标
  • getTop: 按钮左上角相对于父view的y坐标
  • getLeft: 按钮左上角相对于父view的x坐标

15.ColorMatrix的使用

ColorMatrix cm = new ColorMatrix();
cm.set(new float[] {
    1*result, 0, 0, 0, 0,   //红
    0, 1, 0, 0, 0,          //绿
    0, 0, 1, 0, 0,          //蓝
    0, 0, 0, 1, 0           //透明度
});
paint.setColorFilter(new ColorMatrixColorFilter(cm));
canvas.drawBitmap(srcBitmap, new Matrix(), paint);
iv.setImageBitmap(copyedBitmap);

16.网络音乐的播放prepareAsync()

播放网络上的音乐文件时,当网速较慢时,可能使界面卡住,所以要使用异步的prepare

//异步的准备,开启子线程去准备
mediaPlayer.prepareAsync();
//设置准备完毕的回调事件
mediaPlayer.setOnPreparedListener(new OnPreparedListener() {
    @Override
    public void onPrepared(MediaPlayer mp) {
        pd.dismiss();
        mediaPlayer.start();
    }
});

17.SoundPool的使用场景及使用方式

  1. 使用场景
    • 大量较短的音频文件需要播放时
  2. 使用方式

    //参数分别为:声音池中声音的最大数量,声音类型,声音质量(暂时没效果)
    SoundPool soundPool = new SoundPool(3, AudioManager.STREAM_MUSIC, 0);
    //参数分别为:上下文,音频资源id,优先级(暂时没效果)
    soundID = soundPool.load(this, R.raw.shoot, 1);
    //参数分别为:音频id,左声道音量,右声道音量,优先级,循环模式(0不循环,-1一直循环),播放的速率
    soundPool.play(soundID, 1.0f, 1.0f, 0, 0, 1.0f);
    

18.使用系统提供VideoView播放视频

  1. 在布局文件中添加VideoView控件

    <VideoView
        android:id="@+id/vv"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    
  2. 代码

    //设置视频文件
    vv.setVideoPath("/mnt/sdcard/oppo.3gp");
    
    //设置控制器
    MediaController mc = new MediaController(this);
    //mc控制的是那一个VideoView
    mc.setAnchorView(vv);
    //设置VideoView的控制器为mc
    vv.setMediaController(mc);
    
    //播放
    vv.start();
    

19.SurfaceView的作用、实现机制、特点

  1. 作用:单位时间内完成界面的大量多次更新,一般用于游戏开发
  2. 实现机制:双缓冲机制
    • A线程—-更新ui —–后台计算—更新ui
    • B线程—-后台计算—-更新ui —后台计算
  3. 特点:可以在子线程中更新UI
  4. 使用方式

    • 使用SurfaceHolder进行控制

      //拿到控制器
      SurfaceHolder holder = sv.getHolder();
      //获得画布
      Canvas canvas = holder.lockCanvas();
      //在画布上画
      canvas.drawXxx();
      ...
      //将画好的图形输出的屏幕上
      holder.unlockCanvasAndPost(canvas);
      

20.SurfaceView的生命周期

  • SurfaceView占用的内存和cpu的开销很大,当界面完全可见的时候才被创建完毕,如果界面最小化就会被销毁.

    sv.getHolder().addCallback(new Callback() {
    
        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            System.out.println("surface被销毁了");
        }
    
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            System.out.println("surface创建了");
        }
    
        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            System.out.println("surfaceda大小发生了变化");
        }
    });
    

21.使用SurfaceView播放视频的步骤

  1. 给MediaPlayer指定显示的控件

    mediaPlayer.setDisplay(surfaceHolder);
    
  2. 界面销毁时记录视频播放位置

    mediaPlayer.getCurrentPosition();
    
  3. 界面重新创建时指定从上次记录的位置开始播放

    mediaPlayer.seekTo(int milliseconds);
    

22.调用系统相机拍照的步骤

  1. 使用隐式意图调用系统拍照界面

    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    
    //设置生成照片的路径
    file = new File(Environment.getExternalStorageDirectory(),SystemClock.uptimeMillis()+".jpg"); 
    intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file)); 
    startActivityForResult(intent, 0);
    
  2. 在onActivityResult处理结果

    if(file!=null&&file.exists()&&file.length()>0){
        System.out.println(file.getAbsolutePath());
        ImageView iv = (ImageView) findViewById(R.id.iv);
        iv.setImageURI(Uri.fromFile(file));
    }
    

23.调用系统相机录视频的步骤

  1. 使用隐式意图调用系统录视频界面

    Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
    
    //设置生成视频的路径
    file = new File(Environment.getExternalStorageDirectory(),SystemClock.uptimeMillis()+".3gp"); 
    intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file)); 
    startActivityForResult(intent, 0);
    
  2. 在onActivityResult处理结果

    if(file!=null&&file.exists()&&file.length()>0){
        System.out.println(file.getAbsolutePath());
        VideoView vv= (VideoView) findViewById(R.id.vv);
        vv.setVideoPath(file.getAbsolutePath());
        MediaController mc = new MediaController(this);
        mc.setAnchorView(vv);
        vv.setMediaController(mc);
        vv.start();
    }
    

24.传感器的使用

* 传感器(英文名称:sensor)是一种检测装置,能感受到被测量的信息,并能将感受到的信息,按一定规律变换成为电信号或其他所需形式的信息输出

    #define SENSOR_TYPE_ACCELEROMETER                   1 //加速度
    #define SENSOR_TYPE_MAGNETIC_FIELD                  2 //磁力
    #define SENSOR_TYPE_ORIENTATION                     3 //方向
    #define SENSOR_TYPE_GYROSCOPE                       4 //陀螺仪
    #define SENSOR_TYPE_LIGHT                           5 //光线感应
    #define SENSOR_TYPE_PRESSURE                        6 //压力
    #define SENSOR_TYPE_TEMPERATURE                     7 //温度 
    #define SENSOR_TYPE_PROXIMITY                       8 //接近
    #define SENSOR_TYPE_GRAVITY                         9 //重力
    #define SENSOR_TYPE_LINEAR_ACCELERATION             10//线性加速度
  • 陀螺仪又叫角速度传感器,是不同于加速度计(G-sensor)的,他的测量物理量是偏转、倾斜时的转动角速度。在手机上,仅用加速度计没办法测量或重构出完整的3D动作,测不到转动的动作的,G-sensor只能检测轴向的线性动作。但陀螺仪则可以对转动、偏转的动作做很好的测量,这样就可以精确分析判断出使用者的实际动作。而后根据动作,可以对手机做相应的操作
  • 代码

    //1. 获取传感器管理器SensorManager
    sm = (SensorManager) getSystemService(SENSOR_SERVICE);
    
    //2. 获取某个传感器的引用,注册监听
    Sensor sensor = sm.getDefaultSensor(Sensor.TYPE_LIGHT);
    listener = new MyListnener();
    sm.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_NORMAL);
    
    //3.在监听器的回调事件中进行处理
    private class MyListnener implements SensorEventListener{
        //当传感器数据变化的时候调用的方法
        @Override
        public void onSensorChanged(SensorEvent event) {
            float light = event.values[0];
            System.out.println("光线强度:"+light);
        }
        //当传感器精度发生变化的时候调用的方法
        @Override
        public void onAccuracyChanged(Sensor sensor, int accuracy) {
    
        }
    }
    

25.总结

  1. 图片处理
    1. 加载图片到内存——为后续在内存中操作图片做准备
    2. 加载大图片到内存——加载大图片很容易出现OOM
    3. 在内存中对图片进行平移、旋转、缩放,两种特殊效果:镜面和倒影
    4. 图片操作的案例:随手涂鸦和撕衣服
    5. 对图片颜色的处理
  2. 音乐播放
    1. 多媒体播放器的状态图
    2. 同步与异步播放音乐文件
    3. SoundPool的使用
  3. 视频播放
    1. 使用系统控件VideoView播放视频
    2. 使用SurfaceView播放视频
  4. SurfaceView
    1. SurfaceView的作用、实现机制、特点
    2. SurfaceView的生命周期
    3. SurfaceView的使用方式
  5. 调用系统摄像头进行拍照和录制视频
  6. 手机中传感器的使用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值