图片所遇到的坑

本文详细介绍了在Android开发中处理图片的各种常见问题,包括从相册获取图片、图片剪裁、拍照并剪裁、图片压缩、本地存储以及图片在APP与服务器之间的传输。通过实例代码展示了如何实现这些功能,如启动系统相机、使用系统剪裁工具、文件存储以及BASE64编码等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文记录使用Android时关于图片所遇到的坑。

主要记录以下问题:

  • 如何获取相册中的图片
  • 如何剪裁所得到的图片
  • 如何拍照并剪裁图片
  • 如何压缩图片
  • 图片保存在手机本地的存取
  • 图片在APP与服务器之间的传输

如何获取相册中的图片


简单来说,我们就是要写一个Intent去相册中挑选图片,见代码:

Intent intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI);
startActivityForResult(intent, GET_FROM_GALLERY);

这样我们就能改onActivtyResult中接收的到的数据:

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode) {
            case GET_FROM_GALLERY:
                if(resultCode == Activity.RESULT_OK){
                    //获取所选照片的URI
                    Uri selectedImage = data.getData();
//                    设置照片
//                    Bitmap bitmap = null;
//                    try {
//                        bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), selectedImage);
//                    } catch (IOException e) {
//                        e.printStackTrace();
//                    }
//                    image.setImageBitmap(bitmap);
                    performCrop(selectedImage);
                }
                break;

这样我们就能简单的获得所选图片的Uri,从而进一步对图片操作,剪裁图片的方法就再performCrop(selectedImage)里。如果你想直接将所选择的图片设置起来,就是上面代码片段中被注释掉的那一部分了。

如何剪裁所得到的图片


首先我们要的到图片的Uri,然后再写一个Intent,没错,剪裁其实是调用了系统的图片编辑器,所以还是要用Intent,如果你想实现自己的剪裁器或某些特效(比如微信的头像剪裁),那就要辛苦一下自己写个工具类了。

关于启动系统剪裁的Intent是这么写的:

/**
     * 剪裁图片, 要得到图片的uri才能使用, 可以先启动选择图片的intent, 然后在onActivityResult中
     * 得到uri, 然后再调用这个方法
     * @param contentUri 需要剪裁的图片的uri
     */
    private void performCrop(Uri contentUri) {
        try {
            //Start Crop Activity

            Intent cropIntent = new Intent("com.android.camera.action.CROP");

            cropIntent.setDataAndType(contentUri, "image/*");
            // set crop properties
            cropIntent.putExtra("crop", "true");
            // indicate aspect of desired crop
            cropIntent.putExtra("aspectX", 1);
            cropIntent.putExtra("aspectY", 1);
            // indicate output X and Y
            cropIntent.putExtra("outputX", 280);
            cropIntent.putExtra("outputY", 280);

            // retrieve data on return
            cropIntent.putExtra("return-data", true);
            // start the activity - we handle returning in onActivityResult
            startActivityForResult(cropIntent, GET_FROM_GALLERY_CROP);
        }
        // respond to users whose devices do not support the crop action
        catch (ActivityNotFoundException anfe) {
            // display an error message
            String errorMessage = "your device doesn't support the crop action!";
            Toast toast = Toast.makeText(this, errorMessage, Toast.LENGTH_SHORT);
            toast.show();
        }
    }

我们看到Intent中设置了一些参数,比如剪裁图片的位置,还有剪裁图片的宽高比例,见名知意,我就不解释了。如果我们完成剪裁,那么接下来还是要在onActivityResult()方法中接受数据:

            case GET_FROM_GALLERY_CROP:
                if(resultCode == Activity.RESULT_OK){
                    //设置
                    Bundle extras = data.getExtras();
                    Bitmap selectedBitmap = extras.getParcelable("data");
                    image.setImageBitmap(selectedBitmap);

这样得到bitmap就是符合我们要求的bitmap了。

如何拍照并剪裁图片


当然了,剪裁图片不只一种方式,那我们该如何通过拍照获得图片并剪裁呢?
首先当然还是要写一个Intent,然后使用这个Intent去启动系统相机应用获得照片:

/**
     * 创建拍照的intent并存放照片的文件
     */
    private void dispatchTakePictureIntent() {
        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

        if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
            File photoFile = null;
            try {
                photoFile = createImageFile(loginUserPrefix);
            } catch (IOException ex) {
                Log.e("LocalImageTest", "创建存储文件失败");
            }
            if (photoFile != null) {
                Uri photoURI = Uri.fromFile(photoFile);
                takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
                startActivityForResult(takePictureIntent, GET_FROM_CAMERA);
            }
        }
    }

上面的代码除了简单的启动了相机之外,还指定了文件的存储位置。这样可以方便对图片存取的操作,在下面几节,这就不索罗。

那在得到了图片之后我们该怎么做了,当然还是老一套,先得到图片的Uri,然后写一个Intent去启动系统的剪裁应用呗:

            case GET_FROM_CAMERA:
                if(resultCode == Activity.RESULT_OK){
                    Uri selectedImage = Uri.fromFile(new File(mCurrentPhotoPath));
                    performCrop(selectedImage);
                }
                break;

在onActivity中接受图片,然后把图片的Uri得到,位置我们之前已经将图片保存的文件创建好了,所以这里可以很简单就得到Uri。

如何压缩图片


        FileOutputStream out = null;
        try {
            out = new FileOutputStream(mCurrentPhotoPath);
            bitmap.compress(Bitmap.CompressFormat.JPEG, 85, out);

压缩图片相对来说比较简单,只是调用了一个方法就完成了,需要创建一个输出流,对应的是压缩之后的图片的位置。

图片在手机本地的存取


一般每个应用在手机里都有一个自己的文件夹,用于存储本应用的图片以及其他文件,所以这一部分可以说是在APP开发过程中很重要的一步。

我写了一个工具类,里面是关于图片在手机中存取的整个方法,每个方法都由注释,名字也很好懂,我就不啰嗦了:

package utils;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.Toast;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;


public class FileUtils {
    public static final String HAPPY_GO = "HappyGo";
    public static final String PHOTO_SUFFIX = ".jpg";
    public static final String TAG = "TAG";
    public static final File APP_DIR = new File(Environment.getExternalStorageDirectory(), HAPPY_GO);

    /**
     * 从文件中加载bitmap
     * @param name 文件名
     * @return Bitmap对象
     */
    public static Bitmap loadImageFromStorage(String name)
    {
        if (!APP_DIR.exists()) {
            if(!APP_DIR.mkdir()){
                Log.e(TAG, "创建App_Dir失败");
                return null;
            }
        }
        File file = getMediaFile(name);
        Bitmap bitmap = null;
        try {
            bitmap = BitmapFactory.decodeStream(new FileInputStream(file));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }finally {
            return bitmap;
        }
    }

    /**
     * 将bitmap存储到文件中
     * @param name 文件名
     * @param image bitmap
     */
    public static void storeImageIntoStorage(String name, Bitmap image) {
        File pictureFile = getMediaFile(name);
        if (pictureFile == null) {
            Log.e(TAG, "Error creating media file, check storage permissions: ");
            return;
        }
        try {
            FileOutputStream fos = new FileOutputStream(pictureFile);
            image.compress(Bitmap.CompressFormat.JPEG, 100, fos);
            fos.close();
        } catch (FileNotFoundException e) {
            Log.d(TAG, "File not found: " + e.getMessage());
        } catch (IOException e) {
            Log.d(TAG, "Error accessing file: " + e.getMessage());
        }
    }

    /**
     * 创建文件
     * @param name 文件名
     * @return File对象
     */
    public static File getMediaFile(String name){
        File mediaFile;
        String fileName = name + PHOTO_SUFFIX;
        mediaFile = new File(APP_DIR, fileName);
        return mediaFile;
    }

    /**
     * 删除文件
     * @param name 文件名
     * @return 删除成功则返回true
     */
    public static boolean deleteMediaFile(String name){
        File mediaFile;
        String fileName = name + PHOTO_SUFFIX;
        mediaFile = new File(APP_DIR, fileName);
        boolean result = mediaFile.delete();
        return result;
    }

}

图片在APP与服务器之间的传输

如果要实现一个上传头像的功能,我相信图片在APP和服务器之间的传输是必不可少的,那么传输到底该怎么传呢?可不是直接传一个bitmap,一般后台都没这个类。我的方式是把图片转码成字符串,然后将这个字符串传给后台,保存在数据库,然后需要的时候,后台在把这个由图片转变来的字符串传回前端。

当然了,这里涉及一个工具类,把一个图片文件转码成字符串。这个jar包是BASE64Encoder。

有个这个jar包,我们就能直接写两个方法,实现字符串和文件之间的相互转化了:

/**
     * 将手机内存的某一图片转化为字符串
     * @param fileName 文件名
     * @return 字符串
     */
    public static String changePictureIntoString(String fileName) {
        byte[] data = null;
        String path = FileUtils.getMediaFile(fileName).getAbsolutePath();
        try {
            InputStream in = new FileInputStream(path);
            data = new byte[in.available()];
            in.read(data);
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        BASE64Encoder encoder = new BASE64Encoder();
        return encoder.encode(data);
    }

    /**
     * 将字符串还原成jpg图片,然后存储在手机内存中
     * @param imgStr 图片对应的字符串
     * @param fileName 文件名
     * @return 操作是否成功, true表示成功
     */
    public static boolean changeStringToPictureAndSave(String imgStr, String fileName) {
        if (imgStr == null)
            return false;
        BASE64Decoder decoder = new BASE64Decoder();
        try {
            // Base64解码
            byte[] bytes = decoder.decodeBuffer(imgStr);
            for (int i = 0; i < bytes.length; ++i) {
                if (bytes[i] < 0) {
                    bytes[i] += 256;
                }
            }
            //获取文件的绝对路径
            String path = FileUtils.getMediaFile(fileName).getAbsolutePath();
            //将数据保存到文件中
            OutputStream out = new FileOutputStream(path);
            out.write(bytes);
            out.flush();
            out.close();
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 将字符串直接转化为bitmap
     * @param string 字符串
     * @return bitmap
     */
    public static Bitmap stringToBitmap(String string){
        Bitmap bitmap=null;
        try {
            byte[]bitmapArray;
            bitmapArray= Base64.decode(string, Base64.DEFAULT);
            bitmap= BitmapFactory.decodeByteArray(bitmapArray, 0, bitmapArray.length);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return bitmap;
    }

    /**
     * 将bitmap直接转化为字符串
     * @param bitmap bitmap
     * @return 字符串
     */
    public static String bitmapToString(Bitmap bitmap){
        String string=null;
        ByteArrayOutputStream bStream=new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.JPEG, 30, bStream);
        byte[]bytes=bStream.toByteArray();
        string= Base64.encodeToString(bytes, Base64.DEFAULT);
        return string;
    }

这里有四个方法,前两个方法是需要文件操作的,而后面两个方法是很简单,直接实现字符串和bitmap对象之前的相互转化,不过它的代码是原本10k的文件变成了160k-220k的文件,在服务器上很占地方,而且传输很慢,很消耗流量。所以我还是推荐使用前两个方法。方法都由解释,我就不复述了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值