彩世界开奖app官网-彩世界平台官方网址(彩票平台)
做最好的网站
来自 前端技术 2019-12-07 08:09 的文章
当前位置: 彩世界开奖app官网 > 前端技术 > 正文

Android 调用系统功能实现图片选择器,你可能会遇

示例

彩世界平台官方网址 1

图片选择器在手机应用中屡见不鲜,设置头像、聊天传图等常见类似场景都需要使用。为了保持不同设备上体验的一致性和较好的兼容性,比较稳妥的做法是在应用内自实现相机拍照、相册选图和图片裁剪功能。但是,这个实现过程比较复杂,费时费力。更多时候,或者说在项目初期,我们都会选择直接调用系统提供的这些功能来完成一个图片选择器。然而,由于安卓设备的多样性,总会遇到各种各样的兼容问题。本文就来总结总结,调用系统相机、相册和裁剪功能实现图片选择器的过程中,我们需要注意的一些地方。

API

resetClipRect

重置裁剪框,重新变为最大

cropImage.resetClipRect();

clip

裁剪图像,根据当前的裁剪框进行裁剪

cropImage.clip();

getClipImgData

获取已裁剪的图像

var base64 = cropImage.getClipImgData();

rotate

旋转图片

cropImage.clip(isClockWise);

destroy

销毁当前的裁剪对象

如果一个容器需要重新生成裁剪对象,一定要先销毁以前的

cropImage.destroy();

这是根据指定角度旋转图片的代码:

示例

彩世界平台官方网址 2

当上传多张图片至服务器时,为了提升传输效率,往往会采用 zip 格式压缩处理。这里提供一个递归压缩代码,方便大家有需要的时候借鉴参考:

效果

彩世界平台官方网址 3
彩世界平台官方网址 4
彩世界平台官方网址 5
彩世界平台官方网址 6
彩世界平台官方网址 7

部分手机,比如三星手机,调用系统相机拍照所得的照片可能会发生自动旋转问题,常见为旋转 90°。所以,要求我们在拍照之后,使用图片之前,判断图片是否发生过旋转,如果是,要将照片旋转回来。

API

scaleImageData

ImageData类型的数据进行缩放,将数据放入新的ImageData

ImageScale.scaleImageData(imageData, newImageData, {
    // 0: nearestNeighbor
    // 1: bilinearInterpolation
    // 2: bicubicInterpolation
    // 3: bicubicInterpolation2
    processType: 0,
});

scaleImage

Image类型的对象进行缩放,返回一个base64字符串

var base64 = ImageScale.scaleImage(image, {
    width: 80,
    height: 80,
    mime: 'image/png',
    // 0: nearestNeighbor
    // 1: bilinearInterpolation
    // 2: bicubicInterpolation
    // 3: bicubicInterpolation2
    processType: 0,
});

compressImage

compressImage,返回一个base64字符串

与scale的区别是这用的是canvas自动缩放,并且有很多参数可控

var base64 = ImageScale.compressImage(image, {
    // 压缩质量
    quality: 0.92,
    mime: 'image/jpeg',
    // 压缩时的放大系数,默认为1,如果增大,代表图像的尺寸会变大(最大不会超过原图)
    compressScaleRatio: 1,
    // ios的iPhone下主动放大一定系数以解决分辨率过小的模糊问题
    iphoneFixedRatio: 2,
    // 是否采用原图像素(不会改变大小)
    isUseOriginSize: false,
    // 增加最大宽度,增加后最大不会超过这个宽度
    maxWidth: 0,
    // 使用强制的宽度,如果使用,其它宽高比系数都会失效,默认整图使用这个宽度
    forceWidth: 0,
    // 同上,但是一般不建议设置,因为很可能会改变宽高比导致拉升,特殊场景下使用
    forceHeight: 0,
});

第三,根据经验,outputX 与 outputY 值设置太大时,容易出现卡屏现象;

效果

示例较为粗糙

彩世界平台官方网址 8

示例中调用系统裁剪的代码如下:

图像缩放

上述的图片裁剪中其实已经附带缩放功能,但是鉴于那是基于整套裁剪流程的,不满足一些场景(譬如只要针对图片压缩的)。

因此,单独又将图像缩放提取成一个模块,以适用于此类场景。

功能包括:

  • 图像的缩放、压缩

  • 一些常用的缩放算法(双立方,双线性,近邻)

调用系统相机实现拍照功能的核心代码如下:

图像裁剪

功能包括:

  • canvas绘制图片

  • 裁剪框选择裁剪大小

  • 旋转功能

  • 放大镜(方便旋转)

  • 裁剪功能

  • 缩放、压缩功能(通过参数控制)

很多朋友相信有过这样的经验,使用 toString() 或者 getPath() 方法获取 Uri 对象所对应的文件路径,其实这是错误的!通过 getPath() 获取的结果字符串是:

更多

关于详细参数说明与更多使用

请参考源码

这样,当发生屏幕旋转时,不会导致 Activity 销毁重建,而是执行 onConfigurationChanged() 方法:

完善与不足

虽然说一些注意的功能都已经实现,但是从细节角度考虑,还是有很多有待完善的地方的。

譬如,裁剪框的实现方式不优雅。

譬如,旋转不支持其它角度。

譬如,内部源码有待优化。

...

虽然说有计划未来某段时间重构,但考虑到实际的时间安排,可能得等到很后了。

你可能会用到 setImageURI() 方法给 ImageView 设置图片内容,这里也有一个地方需要注意。我们先看一下这个方法的源码:

使用

引入

dist/image-clip.css
dist/image-clip.js

全局变量

ImageClip

调用方法

var cropImage = new ImageClip(options);

cropImage.method()
public static String fileToBase64String(String filePath) { File photoFile = new File; try { FileInputStream fis = new FileInputStream(photoFile); ByteArrayOutputStream baos = new ByteArrayOutputStream; byte[] buffer = new byte[1000]; while (fis.read!=-1) { baos.write; } baos.close(); fis.close(); return Arrays.toString(Base64.encode(baos.toByteArray(), Base64.DEFAULT)); }catch (IOException e) { e.printStackTrace(); } return null;}

关于图像裁剪、压缩

在HTML5时代,canvas的功能已经非常强大了,可以进行像素级的操作。像图像裁剪、压缩就都是基于canvas来实现的。

关于其中原理,无非就是利用canvas自带的API,复杂一点的就是裁剪框以及旋转后的坐标计算,因此不再赘述。

本文中的图像裁剪、压缩都是基于canvas完成的。

第二,调用系统相册并裁剪时,如果使用MediaStore.EXTRA_OUTPUT参数,Uri 尽量不要设置为源文件对应的 Uri 值,另做保存,不损坏系统相册中的源图文件;

前言

前段时间遇到了一个移动端对图像进行裁剪、压缩、旋转的需求。
考虑到已有各轮子的契合度都不高,于是自己重新造了一个轮子。

第一,设置 return-data 参数为 true 时,返回的 Bitmap 对象也为缩略图,获取方式与前面所述相机拍照获取 Bitmap 的方式一致;

源码

图像裁剪:

图像缩放:

其对应的文件路径应该是这个样子的:

更多

关于详细参数说明与更多使用

请参考源码

这里简单使用一个示例代码,演示调用系统相机或相册,获取图片,然后使用系统裁剪功能处理图片,并显示到一个 ImageButton 视图里面:

使用

引入

dist/image-scale.js

全局变量

ImageScale

调用方法

ImageScale.method()
public String zipCompass(String filePath){ File zipFile = new File(Environment.getExternalStorageDirectory(), System.currentTimeMillis()   ".zip"); try{ //指定了两个待压缩的文件,都在assets目录中 String[] filenames = new String[]{ "activity_main.xml", "strings.xml" }; FileOutputStream fos = new FileOutputStream; ZipOutputStream zos = new ZipOutputStream; int i = 1; //枚举filenames中的所有待压缩文件 while (i <= filenames.length){ //从filenames数组中取出当前待压缩的文件名,作为压缩后的名称,以保证压缩前后文件名一致 ZipEntry zipEntry = new ZipEntry(filenames[i - 1]); //打开当前的zipEntry对象 zos.putNextEntry; FileInputStream is = new FileInputStream; byte[] buffer = new byte[8192]; int count = 0; //写入数据 while ((count = is.read >= 0){ zos.write(buffer, 0, count); } zos.flush(); zos.closeEntry(); is.close(); i  ; } zos.finish(); zos.close(); return zipFile.getAbsolutePath(); } catch (Exception e){ Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show(); return null; }}
  • crop:String 类型数据,发送裁剪信号
  • aspectXaspectY:int 类型数据,设置裁剪框的 X 与 Y 值比例
  • outputXoutputY:int 类型数据,设置裁剪输出的图片大小
  • scale:boolean 类型数据,设置是否支持裁剪缩放
  • return-data:boolean 类型数据,设置是否在 onActivityResult 方法的 intent 值中返回 Bitmap 对象
  • MediaStore.EXTRA_OUTPUT:Uri 类型数据,设置是否将裁剪结果保存到指定文件中

可以看到,这里的 uri 参数在内部持有缓存变量,当多次调用该方法而 uri 参数值不变时,图片展示内容不变。问题就在这,如果你多次拍照或裁剪保存的图片文件路径相同时,虽然每次处理过后实际存储的文件内容发生变化,但由于路径相同,uri 参数一致,导致多次调用 setImageURI() 设置图片内容时,ImageView 显示内容不变!这也是为什么示例代码中我用时间戳处理图片文件名的原因所在,保证每次存储的图片路径不同。

这是获取图片旋转角度的代码:

public class MainActivity extends FragmentActivity { public static final int REQUEST_CAMERA = 1; public static final int REQUEST_ALBUM = 2; public static final int REQUEST_CROP = 3; public static final String IMAGE_UNSPECIFIED = "image/*"; private ImageButton mPictureIb; private File mImageFile; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mPictureIb = (ImageButton) findViewById(R.id.ib_picture); } public void onClickPicker { new AlertDialog.Builder .setTitle .setItems(new String[]{"拍照", "相册"}, new OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { if  { selectCamera(); } else { selectAlbum .create; } private void selectCamera() { createImageFile(); if (!mImageFile.exists { return; } Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(mImageFile)); startActivityForResult(cameraIntent, REQUEST_CAMERA); } private void selectAlbum() { Intent albumIntent = new Intent(Intent.ACTION_PICK); albumIntent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, IMAGE_UNSPECIFIED); startActivityForResult(albumIntent, REQUEST_ALBUM); } private void cropImage{ Intent intent = new Intent("com.android.camera.action.CROP"); intent.setDataAndType(uri, IMAGE_UNSPECIFIED); intent.putExtra("crop", "true"); intent.putExtra("aspectX", 1); intent.putExtra("aspectY", 1); intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(mImageFile)); startActivityForResult(intent, REQUEST_CROP); } private void createImageFile() { mImageFile = new File(Environment.getExternalStorageDirectory(), System.currentTimeMillis()   ".jpg"); try { mImageFile.createNewFile(); } catch (IOException e) { e.printStackTrace(); Toast.makeText(this, "出错啦", Toast.LENGTH_SHORT).show(); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (RESULT_OK != resultCode) { return; } switch (requestCode) { case REQUEST_CAMERA: cropImage(Uri.fromFile(mImageFile)); break; case REQUEST_ALBUM: createImageFile(); if (!mImageFile.exists { return; } Uri uri = data.getData(); if (uri != null) { cropImage; } break; case REQUEST_CROP: mPictureIb.setImageURI(Uri.fromFile(mImageFile)); break; } }}

第四,可以不设置 outputX 与 outputY 参数,使用户根据自身按比例自由裁剪,就像示例代码这样。

值得注意的是,这里的 Bitmap 对象是拍照所得图片的一个缩略图,尺寸很小!系统这么做也是充分考虑到应用的内存占用问题。试想一下,如今手机设备中高清相机拍出来的照片,一张图的大小高达十几兆,如果返回这么大的图片,内存占用相当严重,何况很多时候知识临时使用而已。所以,调用系统相机时,一般都会添加 MediaStore.EXTRA_OUTPUT 参数,避免返回 Bitmap 对象。当然,这么做也能保证应用产生的数据,包括文件,都能存储在应用目录下,方便清理缓存时统一清除。

Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(mImageFile));startActivityForResult(cameraIntent, REQUEST_CAMERA);

彩世界平台官方网址 9

/** * 图片旋转操作 * * @param bm 需要旋转的图片 * @param degree 旋转角度 * @return 旋转后的图片 */private Bitmap rotateBitmap(Bitmap bm, int degree) { Bitmap returnBm = null; Matrix matrix = new Matrix(); matrix.postRotate; try { returnBm = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true); } catch (OutOfMemoryError e) { } if (returnBm == null) { returnBm = bm; } if (bm != returnBm) { bm.recycle(); } return returnBm;}

如果不为 intent 添加该数据的话,将在 onActivityResult 的 intent 对象中返回一个 Bitmap 对象,通过如下代码获取:

而正确的获取方式是:

content://media/external/images/media/3066

在部分手机,调用系统拍照功能时,可能会发生横竖屏切换过程,导致返回应用时当前 Activity 发生销毁重建,各个生命周期又重新走了一遍。此时,一些应用内的变量数据可能丢失,使用时容易发生空值异常,进而导致 app 崩溃退出。

/** * 获取图片旋转角度 * @param path 图片路径 * @return */private int parseImageDegree(String path) { int degree = 0; try { ExifInterface exifInterface = new ExifInterface; int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); switch (orientation) { case ExifInterface.ORIENTATION_ROTATE_90: degree = 90; break; case ExifInterface.ORIENTATION_ROTATE_180: degree = 180; break; case ExifInterface.ORIENTATION_ROTATE_270: degree = 270; break; } } catch (IOException e) { e.printStackTrace(); } return degree;}

看似完美,你以为上述代码就能结束了的话,那就大错特错啦!这里面还有一些兼容问题要处理,还有一些地方需要特殊说明。

说了这么多,别忘了在 AndroidManifest.xml 文件中添加系统权限(前面示例代码中没有考虑到 Android 6.0 运行时权限的问题,实际使用时注意添加处理):

Bitmap bmp = data.getParcelableExtra;

有时候,我们需要根据 Uri 获取文件路径。比如如果你不需要使用裁剪功能的话,调用系统相册选择图片后返回的就是一个 Uri 对象,我们需要从这个 Uri 对象中解析出对应的图片文件路径,便于上传至服务器等后续处理。

android:configChanges="orientation|screenSize"
@Overridepublic void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig);}
public void setImageURI { if (mResource != 0 || (mUri != uri && (uri == null || mUri == null || !uri.equals { updateDrawable; mResource = 0; mUri = uri; final int oldWidth = mDrawableWidth; final int oldHeight = mDrawableHeight; resolveUri(); if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) { requestLayout(); } invalidate(); }}
Intent intent = new Intent("com.android.camera.action.CROP");intent.setDataAndType(uri, IMAGE_UNSPECIFIED);intent.putExtra("crop", "true");intent.putExtra("aspectX", 1);intent.putExtra("aspectY", 1);intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(mImageFile));startActivityForResult(intent, REQUEST_CROP);

media/external/images/media/3066

/storage/emulated/0/Pictures/Screenshots/S70302-131606.jpg

private String parseFilePath { String[] filePathColumn = { MediaStore.Images.Media.DATA }; Cursor cursor = getContentResolver().query(uri, filePathColumn, null, null, null); cursor.moveToFirst(); int columnIndex = cursor.getColumnIndex(filePathColumn[0]); String picturePath = cursor.getString(columnIndex); cursor.close(); return picturePath;}

其中 MediaStore.EXTRA_OUTPUT 数据表示,拍照所得图片保存到指定目录下的文件(一般会在 SD 卡中创建当前应用的目录,并创建临时文件保存图片)。然后,在 onActivityResult 方法中根据文件路径获取图片。

可以看出,调用系统裁剪功能,需要设置一些 Extra 参数,很多人容易在这里产生疑惑,不知如何取舍,如何设值。这里列举一下常用的 Extra 名字、值类型和作用:

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

需要注意的是:

现在很多网络框架内部都做了封装处理,上传图片时只需要传递一个文件路径即可。但是,少数情况下,根据服务器需要,我们要对图片文件字节流编码后再上传。这是使用 Base64 编码并根据字节数组获取字符串的处理过程:

为了避免这种现象,我们需要在 AndroidManifest.xml 文件的对应 <activity> 标签中添加属性:

效果如图(不同设备,系统功能呈现有所不同):

彩世界平台官方网址 10

比如,这个 Uri 对象可能是:

本文由彩世界开奖app官网发布于前端技术,转载请注明出处:Android 调用系统功能实现图片选择器,你可能会遇

关键词: 功能 可能会 选择器