1八、图片 & 多媒体

1、图片处理

1.一、BitMap

image

1.Bitmap介绍android

Bitmap实如今android.graphics包中。可是Bitmap类的构造函数是私有的,外面并不能实例化,只能是经过JNI实例化。算法

这必然是某个辅助类提供了建立Bitmap的接口,而这个类的实现经过JNI接口来实例化Bitmap的,这个类就是BitmapFactory。编程

Bitmap是一片连续的内存空间,而咱们的应用程序的内存是散乱的,若是当零散的空间下容纳不了bitmap时就致使OOM。数组

2.BitmapFactory缓存

image

其中提供的一个Opitions属性能够用来提早获取到图片的宽高信息,下面会介绍。网络

2.Bitmap优化app

当前有一张图片,大小仅为1M,可是其规格为3648*2736,如今须要加载此图片总像素数=3648*2736=9980928jsp

假设如今像素采用ARGB_4444标准,则其占用的总空间为:
图片占用空间=总像素数 *像素的单位 ----> 9980928 * 2bytes=19961856bytes = 19M > 16M  OOM内存溢出ide

因此咱们必须对图片进行缩放才能加载:函数

假设:
图片的宽和高: 3648 * 2736 屏幕的宽和高: 320 * 480

计算缩放比:
宽度缩放比例: 3648 / 320 = 11 高度缩放比例: 2736 / 480 = 5

比较宽和高的缩放比例, 哪个大用哪个进行缩放
缩放后的图片:
3648 / 11 = 331
2736 / 11 = 248
缩放后图片的宽和高: 331* 248
331* 248=882088 * 2bytes=160K

a22c3152-2354-44a5-a1d9-3ca23c142e73

3.解析图片的三种方式

1.使用BitmapFactory解析图片

78550ecf-1412-4659-ad77-cd44e52fe1aa

2.使用BitmapDrawable解析图片

9f920025-6e75-4225-98ea-a1f2e8cbb8ec

3.使用InputStream和BitmapDrawable绘制

27c8f43d-d402-4f20-a466-15cd4c50792a

1.二、图片特效

图片的特效包括,图形的缩放、镜面、倒影、旋转、位移等。图片的特效是将原图的图形矩阵乘以一个特效矩阵,造成一个新的图形矩阵来实现的。
Matrix维护了一个3*3的矩阵去更改像素点的坐标。
图形的默认矩阵用数组表示为:
{ 1, 0, 0,     表示向量x = 1x + 0y + 0z
  0, 1, 0,     表示向量y = 0x + 1y + 0z
  0, 0, 1 }    表示向量z = 0x + 0y + 1z
经过更改图形矩阵的值,能够作出缩放/镜面/倒影等图片特效。

1.缩放

0f2ac410-065a-42a4-a499-0cbf4727088c

2.镜面

f34cc74c-e920-4cc0-beeb-4b16a0bd6cf4

惟一不一样的地方在矩阵处

3.倒影

1807c6d8-1ef1-471e-95c6-d7ae32da6b87

4.旋转

b982289c-3955-47a0-9039-22c1bc49ef2e

5.位移

5619153f-7493-470a-bc5e-64460480d97b

1.三、颜色过滤器

Android提供了颜色过滤器来进行颜色处理。

a)ColorMatrixColorFilter:经过使用一个4*5的颜色矩阵来建立一个颜色过滤器,改变图片的颜色信息。

b) 图形颜色默认矩阵是一个4x5的矩阵, 数组表现为:

{1, 0, 0, 0, 0, // red  1*R + 0*G + 0*B + 0*A + 0

0, 1, 0, 0, 0, // green  0*R + 1*G + 0*B + 0*A + 0

0, 0, 1, 0, 0, // blue 0*R + 0*G + 1*B + 0*A + 0

0, 0, 0, 1, 0} // alpha 0*R + 0*G + 0*B + 1*A + 0

颜色矩阵的每一行的最后一个值更改时,其对应的颜色值就会发生改变,因此更改颜色只需修改其对应颜色矩阵行的最后一项的值便可,最大值范围为255

a)首先咱们声明一个初始化矩阵数组和颜色过滤器

bfecdb3b-394a-44bb-b94d-e59a90c01e5e

b)重写progressBar的onProgressChanged方法,并修改矩阵数组的值后,从新建立新的颜色过滤器。

3d5ac1ed-5794-4e1e-b441-1c55d47fd652

58fcefef-0aea-4b7f-b9ed-449019633160

1.四、手绘

a)首先在布局文件建立一个ImageView,让其宽高都填充父窗体,并在代码中进行实例化,并设置它的Touch事件。

mIv = (ImageView) findViewById(R.id.iv);
mIv.setOnTouchListener(this);

b) 而后重写onTouch(View v, MotionEvent event)方法对手势进行操做

@Override
public boolean onTouch(View v, MotionEvent event) {
    int action = event.getAction();
    switch (action) {
    case MotionEvent.ACTION_DOWN:
        mStartX = (int) event.getX();
        mStartY = (int) event.getY();
        if(mBitmap == null){
            mBitmap = Bitmap.createBitmap(mIv.getWidth(),mIv.getHeight(),Config.ARGB_8888);
            // 画布
            mCanvas = new Canvas(mBitmap);
            // 设置背景色白色
            mCanvas.drawColor(Color.WHITE);
            mPaint = new Paint();
            // 设置画笔颜色为红色,线条粗细为5
            mPaint.setColor(Color.RED);
            mPaint.setStrokeWidth(5);
        }
        break;
    case MotionEvent.ACTION_MOVE:
        // 记录移动到的位置坐标
        int moveX = (int) event.getX();
        int moveY = (int) event.getY();
        // 绘制线条,链接起始位置和当前位置
        mCanvas.drawLine(mStartX, mStartY, moveX, moveY, mPaint);
        // 设置显示
        mIv.setImageBitmap(mBitmap);
        // 将起始位置改成为当前移动到的位置
        mStartX = moveX;
        mStartY = moveY;
        break;
    case MotionEvent.ACTION_UP:
        break;
    }
    return true;
}

c) 上述基本功能已经完成,咱们还须要一个清空的方法

// 清除界面
public void clear(View view){
    mBitmap = null;
    mIv.setImageBitmap(null);
}

1.五、三级缓存

1. 简介

如今android应用中不可避免的要使用图片,有些图片是能够变化的,须要每次启动时从网络拉取。

如今有一个问题:假如每次启动的时候都从网络拉取图片的话,势必会消耗不少流量。在当前的情况下,对于非wifi用户来讲,流量仍是很贵的,

一个很耗流量的应用,其用户数量级确定要受到影响。固然,我想,向百度美拍这样的应用,必然也有其内部的图片缓存策略。总之,图片缓存是很重要并且是必须的。

2.图片缓存的原理

实现图片缓存也不难,须要有相应的cache策略。这里我采用 内存-文件-网络 三层cache机制,其中内存缓存包括强引用缓存和软引用缓存(SoftReference),

其实网络不算cache,这里姑且也把它划到缓存的层次结构中。当根据url向网络拉取图片的时候,先从内存中找,若是内存中没有,再从缓存文件中查找,

若是缓存文件中也没有,再从网络上经过http请求拉取图片。在键值对(key-value)中,这个图片缓存的key是图片url的hash值,value就是bitmap。因此,

按照这个逻辑,只要一个url被下载过,其图片就被缓存起来了。

关于Java中对象的软引用(SoftReference),若是一个对象具备软引用,内存空间足够,垃圾回收器就不会回收它;若是内存空间不足了,就会回收这些对象的内存。

只要垃圾回收器没有回收它,该对象就能够被程序使用。软引用可用来实现内存敏感的高 速缓存。使用软引用能防止内存泄露,加强程序的健壮性。 

固然,这种回收机制只存在于Android 2.3版本以前,Android 2.3以后垃圾回收器会更倾向于回收软引用、弱引用,它们变得再也不可靠。

从代码上来讲,采用一个ImageManager来负责图片的管理和缓存,函数接口为public void loadBitmap(String url, Handler handler) ;其中url为要下载的图片地址,

handler为图片下载成功后的回调,在handler中处理message,而message中包含了图片的信息以及bitmap对象。ImageManager中使用的ImageMemoryCache(内存缓存)、

ImageFileCache(文件缓存)以及LruCache(最近最久未使用缓存)会在后续文章中介绍。

3838b7ba-3094-4194-8ab6-8ceea57891bc

2、多媒体

1.一、音频播放

一、MediaPlayer

该播放器同时只能播放一个音乐文件,文件大小并无限制。MediaPlayer必须严格按照状态图操做,不然就会出现错误。

032294fb-e009-4714-971b-2b118f637bc1

MediaPlayer播放的实例:

// 建立MediaPlayer对象
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setDataSource(Environment.getExternalStorageDirectory() + "/bg.mp3");
// 准备
mediaPlayer.prepare();
// 判断当前音乐是否在播放
boolean isPlaying = mediaPlayer.isPlaying();
if(!isPlaying){
    mediaPlayer.start();
}else{
    Toast.makeText(this,"音乐正在播放中",0).show();
}

二、SoundPool

SoundPool特色是能够自行设置声音的品质、音量、播放比率等参数。而且它能够同时管理多个音频流,每一个流都有独自的ID,对某个音频流的管理都是经过ID进行的。

a)SoundPool最大只能申请1M的内存空间,只能用一些很短的声音片断,而不是用它来播放歌曲或者作游戏背景音乐。

b)SoundPool提供了pause和stop方法,但这些方法建议最好不要轻易使用,由于有些时候它们可能会使你的程序莫名其妙的终止。

c)SoundPool的效率问题。其实SoundPool的效率在这些播放类中算是很好的,这可能会影响用户体验。

d7fe9422-87e3-4e59-80ec-151e0251009d

SoundPool播放的实例:

// 建立SoundPool对象
SoundPool soundPool = new SoundPool(1,AudioManager.STREAM_MUSIC,1);
// 加载SoundPool音乐文件
int soundID = soundPool.load(this,R.raw.shoot1,1);
// 播放soundPool音乐
soundPool.play(soundID,1,1,0,0,1);

1.二、视频播放

1.第一种

a)第一种方式能够经过surfaceView和MediaPlayer来实现该功能,首先建立一个SufaceView实例

2.第二种

a)设置布局文件,布局文件比较简单,所以这里只给你VideoView标签。

mSurface = (SurfaceView) findViewById(R.id.sfv);     
mHolder = mSurface.getHolder();
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

b) 而后经过以下代码播放,注意视频知支持mp4格式

/ 建立播放器对象
MediaPlayer player = new MediaPlayer();
// 获取音乐路径
String path = "/mnt/sdcard/oppo.mp4";
// 给播放器设置音乐路径
player.setDataSource(path);
// 设置音乐格式
player.setAudioStreamType(AudioManager.STREAM_MUSIC);
// 设置输出画面
player.setDisplay(mHolder);
// 准备
player.prepare();
// 播放
player.start();

 

2.第二种

a)设置布局文件,布局文件比较简单,所以这里只给你VideoView标签。

<VideoView 
    android:id="@+id/video"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

b) 设置VideoView的播放文件路径和媒体控制器,调用start方法便可播放媒体文件,固然如今只是让当前类实现了MediaController接口

VideoView video = (VideoView) findViewById(R.id.video);
video.setVideoPath("/mnt/sdcard/oppo.mp4");
video.setMediaController(new MediaController(this));
// 开始播放
video.start();
// 设置当前播放器窗口为焦点
video.requestFocus();

c)这时,视频已经能够播放,若是想实现本身的业务逻辑,能够定义类实现MediaPlayerControl接口,其中提供大量的回调方法供咱们使用。

1.三、传感器

Android手机中内置了不少传感器,其主要类型有:方向、加速度(重力)、光线、磁场、距离(临近性)、温度等。

image

1.传感器的使用

a) 获取传感器管理器SensorManager

SensorManager sm = (SensorManager) getSystemService(SENSOR_SERVICE);

b) 经过传感器管理器对象得到指定类型的传感器:

// 获取指定传感器对象,获取系统默认的重力加速度传感器
Sensor sensor = sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

c) 经过传感器管理器对象得到手机中全部的传感器

// 获取手机支持的全部传感器
List<Sensor> sensorList = sm.getSensorList(Sensor.TYPE_ALL);
for (int i = 0; i < sensorList.size(); i++) {
    Sensor sensor = sensorList.get(i);
    // 获取传感器名称
    String name = sensor.getName();
    // 获取传感器厂商
    String vendor = sensor.getVendor();
    // 获取传感器版本号
    int version = sensor.getVersion();
}

d) 使用传感器管理器对象注册传感器来使一个传感器工做:

sm.registerListener(new SensorEventListener() {
    // 当传感器改变时触发该函数
    @Override
    public void onSensorChanged(SensorEvent event) {
        
    }
    // 当传感器精度更改时触发该函数
    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        
    }
},sensor,SensorManager.SENSOR_DELAY_NORMAL);

(1) listener :传感器事件监听器

(2) sensor :要被注册的传感器对象

(3) rate  :采样率,分为最快、游戏、普通、用户界面几种当应用程序请求特定的采样率时,其实只是对传感器子系统的一个建议,不保证特定的采样率可用。

采样率的四种类型详解:
最快: SensorManager.SENSOR_DELAY_FASTEST,最低延迟,通常不是特别敏感的处理不推荐使用,该种模式可能形成耗电,因为传递的为原始数据,

       算法不处理好会影响游戏逻辑和UI的性能
游戏: SensorManager.SENSOR_DELAY_GAME,游戏延迟,通常绝大多数的实时性较高的游戏都使用该级别
普通: SensorManager.SENSOR_DELAY_NORMAL,标准延迟,对于通常的益智类或EASY级别的游戏可使用,但太低的采样率可能对一些赛车类游戏有跳帧现象

用户界面: SensorManager.SENSOR_DELAY_UI,通常对于屏幕方向自动旋转使用,相对节省电能和逻辑处理,通常游戏开发中咱们不使用

总结:因为传感器比较多,这里只是简单的概述,后续会花大量的时间整理传感器的使用。

1.四、摄像头

调用系统摄像头进行拍照和摄像无需添加权限,直接调用便可。只需知道系统摄像头的action和category就能够调用系统摄像头。

a)打开Android源码,查看”\packages\apps\”文件文件目录下的Camera应用,即系统摄像头的应用程序。打开其清单文件文件,查看其Activity的action和category信息。

b)Camera类的action和category

<intent-filter>
    <action android:name="android.media.action.IMAGE_CAPTURE" />
    <category android:name="android.intent.category.DEFAULT" />
</intent-filter>

VideoCamera类的action和category

<intent-filter>
    <action android:name="android.media.action.VIDEO_CAMERA" />
    <category android:name="android.intent.category.DEFAULT" />
</intent-filter>

c) 已知调用系统摄像头拍照和摄像功能对应的action和category信息,采用隐式调用的方式调用Activity。

因为但愿在调用拍照或摄像功能后回到当前应用的界面,且得知拍照或摄像的结果如何是否成功使用startActivityForResult方法开启Activity,并重写onActivityResult方法处理回传的数据。

1.拍照功能

Intent intent = new Intent();
// 设置Action
intent.setAction("android.media.action.IMAGE_CAPTURE");
// 建立一个文件
File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath(),"my.jsp");
// 建立Uri对象
Uri uri = Uri.fromFile(file);
// 设置图片输出路径
intent.putExtra(MediaStore.EXTRA_OUTPUT,uri);
// 开启Activity
startActivityForResult(intent,100);

2.摄像功能

Intent intent = new Intent();
intent.setAction("android.media.action.VIDEO_CAPTURE");
File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath(),"myVedio.mp4");
Uri uri = Uri.fromFile(file);
intent.putExtra(MediaStore.EXTRA_OUTPUT,uri);
startActivityForResult(intent,101);

3.自定义拍照

a)首先定义一个SurfaceView用做相机的预览

SurfaceView mSurfaceView = (SurfaceView) this.findViewById(R.id.surfaceView);
SurfaceHolder holder = mSurfaceView.getHolder();
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
holder.setKeepScreenOn(true);
holder.addCallback(new SurfaceCallback());

  b )建立SurfaceCallback来开启预览,其中提供了三个回调方法,方便咱们对相机预览进行管理。

/**sufaceView的回调*/
private final class SurfaceCallback implements Callback {
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        try {
            mCamera = Camera.open(0);// 0:打开后置    1:打开前置 
            if(mCamera != null){
                mCamera.setPreviewDisplay(holder);
                mCamera.setDisplayOrientation(getPreviewDegree(NewCameraCheck.this));
                mCamera.startPreview();
            }else{
                Toast.makeText(NewCameraCheck.this, "获取不到相机实例", Toast.LENGTH_SHORT).show();
            }          
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {
        if(mCamera != null){
            parameters = mCamera.getParameters(); 
            parameters.setPictureFormat(PixelFormat.JPEG);  
            parameters.setPreviewSize(width, height);
            parameters.setPreviewFrameRate(5);
            parameters.setPictureSize(width, height);
            parameters.setJpegQuality(80);
        }else{
            Toast.makeText(NewCameraCheck.this, "获取不到相机实例,没法设置参数。", Toast.LENGTH_SHORT).show();
        }
    }
    // 在Surface销毁时释放资源
    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        if(mCamera != null){
            //mCamera.stopPreview();
            mCamera.release(); 
            mCamera = null;
        }
    }
}

c) 提供一个静态方法用来对相机预览的旋转角度进行自动调整

// 提供一个静态方法,用于根据手机方向得到相机预览画面旋转的角度
public  int getPreviewDegree(Activity activity) {
    // 得到手机的方向
    int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
    mDegree = 0;
    // 根据手机的方向计算相机预览画面应该选择的角度
    switch (rotation) {
    case Surface.ROTATION_0:
        mDegree = 90;
        break;
    case Surface.ROTATION_90:
        mDegree = 0;
        break;
    case Surface.ROTATION_180:
        mDegree = 270;
        break;
    case Surface.ROTATION_270:
        mDegree = 180;
        break;
    }
    return mDegree;
}

d) 咱们能够经过调用Camera的tackPicture()方法进行拍照,若是咱们须要对焦拍照的话,使用下面这段代码拍照

public void tackImage(){
    if(mCamera == null){
        Toast.makeText(NewCameraCheck.this, "获取不到相机实例,拍照失败!", Toast.LENGTH_SHORT).show();
        return;
    }
    mCamera.autoFocus(new AutoFocusCallback() {
        @Override
        public void onAutoFocus(boolean success, Camera camera) {
            mCamera.takePicture(null, null, new MyPictureCallback());
            //mCamera.startPreview();
        }
    });
}

e) 除此以外,咱们相机适配须要注意清单文件中的配置,固然还要记得添加权限

清单文件配置:
<activity
    android:name="com.hll.phoneuser.activity.NewCameraCheck"
    android:screenOrientation="portrait"
    android:theme="@android:style/Theme.Light.NoTitleBar.Fullscreen" />  
权限配置:
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />

4.自定义摄像

使用Camera+MediaRecoder + SurfaceView控件可实现录制视频的功能。

a)首先定义一个SurfaceView用做相机的预览

setContentView(R.layout.activity_main);
SurfaceView mSurfaceView  = (SurfaceView) findViewById(R.id.surface);
SurfaceHolder holder = mSurfaceView .getHolder();
holder.addCallback(new MyVideoCallback());

b) 建立SurfaceCallback来开启预览,其中提供了三个回调方法,方便咱们对相机预览进行管理。

class MyVideoCallback implements Callback{
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        mCamera.open();
        try {
            mCamera.setPreviewDisplay(holder);
            mCamera.startPreview();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width,
            int height) {
        
    }
    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        mCamera.stopPreview();
        mCamera.release();
    }
}

c)开始录像须要设置大量的参数,代码中异常未处理,具体操做以下

// 中止预览
mCamera.stopPreview();
// 解锁摄像头
mCamera.unlock();
// 初始化MediaRecorder对象
MediaRecorder recorder = new MediaRecorder();
// 给recorder设置摄像头
recorder.setCamera(mCamera);
// 设置音频源
recorder.setAudioSource(AudioSource.CAMCORDER);
// 设置视频源
recorder.setVideoSource(VideoSource.CAMERA);
// 设置录像质量参数
CamcorderProfile profile = CamcorderProfile.get(CamcorderProfile.QUALITY_480P);
recorder.setProfile(profile);
// 设置录像输出路径
recorder.setOutputFile(Environment.getExternalStorageDirectory().getAbsolutePath()+"/test.mp4");
// 设置预览显示对象
recorder.setPreviewDisplay(mHolder.getSurface());
// 准备
recorder.prepare();
recorder.start();

d) 若是想中止录像的话,只须要调用以下代码:

mRecorder.stop();
mRecorder.reset();
mRecorder.release();
mCamera.lock();
mCamera.startPreview();

e) 固然别忘记添加权限

<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>

后续进阶能够参看:Android多媒体开发高级编程

3、拍照和相册获取图片

首先调用系统的相机或打开相册来获取图片

// 拍照
btnCamera.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(
                Environment.getExternalStorageDirectory(), "temp.jpg")));
        startActivityForResult(intent, PHOTOHRAPH);
    }
});
// 从相册选择
btnPhoto.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
        Intent intent = new Intent();
        intent.setAction(Intent.ACTION_PICK);
        intent.setType("image/*");
        startActivityForResult(intent, PHOTOZOOM);
    }
});

在activity中的onActivityResult中进行回调获取到打开的图片

public static final int NONE = 0;
public static final int PHOTOHRAPH = 1;// 拍照
public static final int PHOTOZOOM = 2; // 缩放
public static final int PHOTORESULE = 3;// 结果  
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    switch (requestCode) {
    case NONE:
        return;
    case PHOTOHRAPH:    // 拍照
        // 设置文件保存路径这里放在根目录下
        File picture = new File(Environment.getExternalStorageDirectory() + "/temp.jpg");
        startPhotoZoom(Uri.fromFile(picture));
        break;
    case PHOTOZOOM:    // 从相册选择
        startPhotoZoom(data.getData());
        break;
    case PHOTORESULE:
        Bundle extras = data.getExtras();
        if (extras != null) {
            Bitmap photo = extras.getParcelable("data");
            ByteArrayOutputStream stream = new ByteArrayOutputStream();
            photo.compress(Bitmap.CompressFormat.JPEG, 75, stream);// (0 - 100)压缩文件
            
            // 设置头像显示
            mIvPhoto.setImageBitmap(photo);
            dialog.dismiss();
        }
        break;
    }
}

其中封装的方法startPhotoZoom,是对图片进行宽高和压缩等处理:

public void startPhotoZoom(Uri uri) {
    Intent intent = new Intent("com.android.camera.action.CROP");
    intent.setDataAndType(uri, "image/*");
    intent.putExtra("crop", "true");
    // aspectX aspectY 是宽高的比例
    intent.putExtra("aspectX", 1);
    intent.putExtra("aspectY", 1);
    // outputX outputY 是裁剪图片宽高
    intent.putExtra("outputX", 64);
    intent.putExtra("outputY", 64);
    intent.putExtra("return-data", true);
    startActivityForResult(intent, PHOTORESULE);
}
相关文章
相关标签/搜索