Android对图片常用(拍照,图库,拖拉,压缩上传)操作

本篇博客内容:

  • android app拍照功能

  • 部分手机上拍照图片旋转问题

  • android图库获取图片功能

  • 查看图片,进行缩放,拖拉功能

  • 压缩图片,上传功能

Android 拍照功能:
一般的拍照功能是调用系统中相机来实现,即通过Intent来调用其他运用程序(相机运用程序)来实现。众所周知,通过Intent调用手机上的其他运用程序,都应该先检查手机上是否装有能Intent开启的其他程序。代码如下:

  //MediaStore.ACTION_IMAGE_CAPTURE开启手机中相机
  Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
         //检查手机上是否装有Intent对应开启的程序
        if (intent.resolveActivity(getPackageManager()) != null) { 
              startActivityForResult(intent, requestCode);
        }
  }

从代码可知,调用开启相机是通过startActivityForResult(), 那返回的是什么呢?
一个压缩后的Bitmap,还是一个图片的path. ?以上代码产生的是一个系统压缩后返回的Bitmap。在onActivityResult()中Intent#Bundle,Bundle中data(key)对应的值(value).

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
        Bundle extras = data.getExtras();
        Bitmap imageBitmap = (Bitmap) extras.get("data");
        mImageView.setImageBitmap(imageBitmap);
    }
}

一个系统压缩后的Bitmap实际作用不大,适合做一个图标。 若是需要按尺寸来加载图片,然后将图片压缩上传,利用以上代码是完全行不通的。 这时候,便需要知道相机拍照产生的原始图片的Path,根据path来实现项目需求。

实际开发中,会指定图片的路径,即图片产生到指定文件下,且指定图片格式为.png 。将图片存储路径通过Intent#putExtra()告诉系统。

   private String imagePath1;
  /**
     * 打开相机
     */
    public void openCamera(int requestCode) {
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        if (intent.resolveActivity(getPackageManager()) != null) {
            File bitmapCacheFile = BitmapUtils.getBitmapDiskFile(BaseApplication.getAppContext());
            if (bitmapCacheFile != null) {
                imagePath1 = bitmapCacheFile.getAbsolutePath();
                //指定拍照存储路径
                intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(bitmapCacheFile));
                startActivityForResult(intent, requestCode);
            }
        }
    }  

    /**
     * 获得存储bitmap的文件
     * getExternalFilesDir()提供的是私有的目录,在app卸载后会被删除
     *
     * @param context
     * @param
     * @return
     */
    public static File getBitmapDiskFile(Context context) {
        String cachePath;
        if (Environment.MEDIA_MOUNTED.equals(Environment
                .getExternalStorageState())
                || !Environment.isExternalStorageRemovable()) {

            cachePath = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES).getPath();
        } else {
            cachePath = context.getFilesDir().getPath();
        }
        return new File(cachePath + File.separator + getBitmapFileName());
    }

    public static final String bitmapFormat = ".png";

    /**
     * 生成bitmap的文件名:日期,md5加密
     *
     * @return
     */
    public static String getBitmapFileName() {
        StringBuilder stringBuilder = new StringBuilder();
        try {
            final MessageDigest mDigest = MessageDigest.getInstance("MD5");
            String currentDate = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
            mDigest.update(currentDate.getBytes("utf-8"));
            byte[] b = mDigest.digest();
            for (int i = 0; i < b.length; ++i) {
                String hex = Integer.toHexString(0xFF & b[i]);
                if (hex.length() == 1) {
                    stringBuilder.append('0');
                }
                stringBuilder.append(hex);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        String fileName = stringBuilder.toString() + bitmapFormat;
        return fileName;
    }

向sd-card写入信息,权限是少不了的:

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

在onActivityResult中获取拍照成功的标示,这里系统将不会返回有任何数据,这时候需要自己开启异步线程去按适屏计算加载图片:

 @Override
 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == CAREMA) {
            if (resultCode == RESULT_OK) {
                //开启工作线程去加载,拍照产生的图片
                new Thread(loadBitmapRunnable).start();
            }
        }
  } 

      /**
     * 根据path路径,找到file,从而生成bitmap
     */
    private Runnable loadBitmapRunnable = new Runnable() {
        @Override
        public void run() {
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            try {
                Thread.sleep(500);
                Bitmap bitmap = BitmapUtils.decodeFileCreateBitmap(imagePath1 
                               , iv.getWidth(), iv.getHeight());
                handler.obtainMessage(CAREMA, bitmap).sendToTarget();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }; 

     public synchronized static Bitmap decodeFileCreateBitmap(String path, int targetWith, int targerHeight) {
        try {
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeFile(path, options);
            options.inSampleSize = calculateScaleSize(options, targetWith, targerHeight);
            options.inJustDecodeBounds = false;
            Bitmap bitmap = BitmapFactory.decodeFile(path, options);
            return getNormalBitamp(bitmap, path);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }  

    /**
     * 采用向上取整的方式,计算压缩尺寸
     *
     * @param options
     * @param targetWith
     * @param targerHeight
     * @return
     */
    private static int calculateScaleSize(BitmapFactory.Options options, int targetWith, int targerHeight) {
        int simpleSize;
        if (targetWith > 0&&targerHeight>0) {
            int scaleWith = (int) Math.ceil((options.outWidth * 1.0f) / targetWith);
            int scaleHeight = (int) Math.ceil((options.outHeight * 1.0f) / targerHeight);
            simpleSize = Math.max(scaleWith, scaleHeight);
        } else {
            simpleSize = 1;
        }
        return simpleSize;
    }

高效和快速加载图片,请参考 Android高效加载Bitmap

部分手机上回遇到图片旋转问题,这时候需要使用到ExifInterface这类,手动回复图片原本方向:

 /**
     * 根据存储的bitamp中旋转角度,来创建正常的bitamp
     *
     * @param bitmap
     * @param path
     * @return
     */
    public static Bitmap getNormalBitamp(Bitmap bitmap, String path) {
        int rotate = getBitmapRotate(path);
        Bitmap normalBitmap=null;
        switch (rotate) {
            case 90:
            case 180:
            case 270:
                try {
                    Matrix matrix = new Matrix();
                    matrix.postRotate(rotate);
                    normalBitmap = bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth() 
                           , bitmap.getHeight(), matrix, true);
                    if (bitmap != null && !bitmap.isRecycled()) {
                        bitmap.recycle();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    normalBitmap=bitmap;
                }
                break;
            default:
                normalBitmap=bitmap;
                break;
        }
        return normalBitmap;
    }  

  /**
     * ExifInterface :这个类为jpeg文件记录一些image 的标记
     * 这里,获取图片的旋转角度
     *
     * @param path
     * @return
     */
    public static int getBitmapRotate(String path) {
        int degree = 0;
        try {
            ExifInterface exifInterface = new ExifInterface(path);
            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 (Exception e) {
            e.printStackTrace();
        }
        return degree;
    }

最后创建一个主线程捆绑的Handler,进行加载适屏处理后的bitmap:


    private Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            if (msg.what == CAREMA) {
                if (msg.obj instanceof Bitmap) {
                    iv.setImageBitmap((Bitmap) msg.obj);
                }
            } 
            return false;
        }
    });

拍照效果如下:
1.选择拍照功能:

这里写图片描述

2.相机运用进行拍照:

这里写图片描述

3.加载拍照产生的图片:

这里写图片描述

android图库获取图片功能

获取图库中相片,也是通过Intent来开启图库运用来实现的。

    /**
     * 打开图库
     */
    public void openMapStorage(int requestCode) {
        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
        intent.setType("image/*");
        if (intent.resolveActivity(getPackageManager()) != null) {
            startActivityForResult(intent, requestCode);
        }
    }

在 onActivityResult中接收图库返回的信息,这里是返回一个Uri。


    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
       if (requestCode == PHOTO) {
            if (resultCode == RESULT_OK) {
                uri = data.getData();
                new Thread(loadUriCreateBitmapRunnable).start();
            }
        }
    }

根据Uri来查询图库的数据库,找到图片路径,从而加载图片:

/**
     * 根据Uri,查询到path,找到对应的file,生成bitmap
     */
    private Runnable loadUriCreateBitmapRunnable = new Runnable() {
        @Override
        public void run() {
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            Cursor cursor = null;
            try {
                Thread.sleep(500);
                if (uri != null) {
                    cursor = getContentResolver().query(uri,  
                             new String[]{MediaStore.Images.Media.DATA}, null, null, null);
                    if (cursor != null && cursor.moveToFirst()) {
                        imagePath1 = cursor.getString( 
                                cursor.getColumnIndex(MediaStore.Images.Media.DATA));
                        if (imagePath1 != null) {
                            Bitmap bitmap = BitmapUtils.decodeFileCreateBitmap(imagePath1,  
                                      iv.getWidth(), iv.getHeight());
                            handler.obtainMessage(CAREMA, bitmap).sendToTarget();

                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (cursor != null) {
                    cursor.close();
                }
            }
        }
    };

最后创建一个主线程捆绑的Handler,进行加载图库选中的bitmap:


    private Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            if (msg.what == CAREMA) {
                if (msg.obj instanceof Bitmap) {
                    iv.setImageBitmap((Bitmap) msg.obj);
                }
            } 
            return false;
        }
    });

效果如下:

1.在图库中选中图片:

这里写图片描述

2.加载选中的图片 :

这里写图片描述

android 查看图片,进行缩放,拖拉功能

1.首先加载适屏的图片,通过设置Matrix 来,放置到中间:

    private Runnable loadBitmapRunnable = new Runnable() {
        @Override
        public void run() {
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            try {
                Thread.sleep(500);
                Bitmap bitmap = BitmapUtils.decodeFileCreateBitmap(path 
                       , toucher_iv.getWidth(), toucher_iv.getHeight());
                handler.obtainMessage(0, bitmap).sendToTarget();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    };  
      //记录当前图片的Matrix
    private Matrix beforeMatrix = new Matrix();

    private Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            if (msg.what == 0) {
                bitmap = (Bitmap) msg.obj;

                if (bitmap == null) {
                    return false;
                }
                float y = ((getPhoneMetrics(BaseApplication.getAppContext()).heightPixels -  
                               bitmap.getHeight()) / 2);
                if (y > 0) {
                    beforeMatrix.setTranslate(0, y);
                    toucher_iv.setImageMatrix(beforeMatrix);
                }
                toucher_iv.setImageBitmap(bitmap);
            }
            return false;
        }
    });

2.进行设置缩放,和拖拉的功能代码:
先实现 View.OnTouchListener类,复写onTouch(View v, MotionEvent event)方法。

 //记录当前图片的Matrix
    private Matrix beforeMatrix = new Matrix();
    //改变状态后的Matrix
    private Matrix changeMatrix = new Matrix();
    //记录当前图片处于的状态
    private int currentState = 0;//当前状态
    private final static int MOVE = 1;//拖动
    private final static int SCALLE = 2;//缩放
    //记录两个坐标(float类型)
    private PointF before_PointF = new PointF();
    //记录下一开两个手指间的距离:
    private float before_Distance;
    //记录下开始时,两个手指间的中心点
    private PointF midPointf;


    /**
     * 1.拖动分析:
     * 在原本Matrix基础上添加移动的x,y距离,实现拖动
     * 做法:先记录开始的Matrix1,
     * 然后计算移动的x,y,
     * 创建一个以Matrix1为基础的新的Matrix2,添加移动的x,y
     * 图片设置新的Matrix2
     * <p/>
     * <p/>
     * 2.缩放分析:
     * 先记录中心点(缩放,旋转都需要用到),原本的Matrix,原本手指间距离,通过距离比率来设置
     *
     * @param v
     * @param event
     * @return
     */
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
            //手指压下屏幕
            case MotionEvent.ACTION_DOWN:
                currentState = MOVE;
                beforeMatrix.set(toucher_iv.getImageMatrix());
                before_PointF.set(event.getX(), event.getY());
                break;
            //手指在滑动
            case MotionEvent.ACTION_MOVE:
                if (currentState == MOVE) {//拖动状态
                    float distance_x = event.getX() - before_PointF.x;
                    float ditance_y = event.getY() - before_PointF.y;
                    changeMatrix.set(beforeMatrix);
                    changeMatrix.postTranslate(distance_x, ditance_y);
                } else if (currentState == SCALLE) {//缩放状态
                    float endDistance = distance(event);
                    if (endDistance > 10f) {
                        float scale = endDistance / before_Distance;
                        changeMatrix.set(beforeMatrix);
                        changeMatrix.postScale(scale, scale, midPointf.x, midPointf.y);
                    }
                }
                break;
            //手指离开触摸
            case MotionEvent.ACTION_UP:
                currentState = 0;
                break;
            //多一个手指在触摸
            case MotionEvent.ACTION_POINTER_DOWN:
                currentState = SCALLE;
                //开始时,两个手指间距离
                before_Distance = distance(event);
                if (before_Distance > 10f) {
                    midPointf = mid(event);
                    beforeMatrix.set(toucher_iv.getImageMatrix());
                }
                break;
            //还有手指在触摸
            case MotionEvent.ACTION_POINTER_UP:
                currentState = 0;
                break;
        }
        toucher_iv.setImageMatrix(changeMatrix);
        return true;
    }

    /**
     * 计算两个手指间的距离
     */
    private float distance(MotionEvent event) {
        float dx = event.getX(1) - event.getX(0);
        float dy = event.getY(1) - event.getY(0);
        /** 使用勾股定理返回两点之间的距离 */
        return (float) Math.sqrt(dx * dx + dy * dy);
    }

    /**
     * 计算两个手指间的中间点
     */
    private PointF mid(MotionEvent event) {
        float midX = (event.getX(1) + event.getX(0)) / 2;
        float midY = (event.getY(1) + event.getY(0)) / 2;
        return new PointF(midX, midY);
    }

效果如下:
这里没有制作gif来显示效果,感兴趣的可以下载项目来体验效果:

这里写图片描述

android 压缩图片
现在的手机拍照产生的相片都了几M,甚至可能是5M多,上传到服务器是不会上传那么大的图片的。这边需要用到压缩。
压缩图片一般通过Bitmap#compress()来实现的。 一般都是通过for循环来计算最小压缩体积:

这里是将Bitmap按最小体积量来压缩成byte[]来上传的。

 public static byte[] bitmapCompressToByteArray(Bitmap bitmap, int bitmapSize, boolean isRecycle) {

        int quality = 100;//范围0~100
        ByteArrayOutputStream byteArrayOutputStream = null;
        try {
            byteArrayOutputStream = new ByteArrayOutputStream();
            bitmap.compress(Bitmap.CompressFormat.PNG,quality,byteArrayOutputStream);
            while (byteArrayOutputStream.toByteArray().length/1024>bitmapSize){
                   if(quality<=10){
                        break;
                   }
                   byteArrayOutputStream.reset();
                   quality-=10;
                   bitmap.compress(Bitmap.CompressFormat.PNG,quality,byteArrayOutputStream);
            }
            byte[] b = byteArrayOutputStream.toByteArray();
            if (isRecycle) {
                bitmap.recycle();
            }

            return  b;
        } catch (Exception e) {
            e.printStackTrace();
            return  null;
        } finally {
            try {
                if (byteArrayOutputStream != null) {
                    byteArrayOutputStream.close();
                }
            } catch (Exception e1) {
                e1.printStackTrace();
            }
        }
    }

经过测试发现,若是一个几M的图片进行这样的压缩处理,时间花费是很久的,甚至可能是几分钟时间。

这里的的处理方式是:按上传的体积量来计算出图片的分辨率,按这个分辨率来加载适合的图片,然后将这个图片再来编码成byte[]

    /**
     * 将bitmap编码成byte[]
     */
    private Runnable bitmapEndecodeByteRunnable = new Runnable() {
        @Override
        public void run() {
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            try {
                //这里是250*250大小的图片
                byte[] b = BitmapUtils.bitmapCompressToByteArray( 
                      BitmapUtils.decodeFileCreateBitmap(imagePath1 
                       , 250, 250), 200, true);
                handler.obtainMessage(FILEUPLOAD, b).sendToTarget();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }; 

最后将图片对应的byte[]通过volley中自定义的MultiPartRequest上传:

private Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
          if (msg.what == FILEUPLOAD) {
                byte[] b = (byte[]) msg.obj;
                if (b != null) {
                    sendFileUploadRequest(b);
                }
            }
            return false;
        }
    });

    /**
     * 将bitmap编码成byte[],然后上传到服务器
     *
     * @param bitmap
     */
    public void sendFileUploadRequest(byte[] bitmap) {
        System.out.print("开始上传");
        MultiPartRequest<JsonBean> request = new MultiPartRequest<>(Request.Method.POST, "http://192.168.1.102:8080/SSMProject/file/fileUpload", JsonBean.class, new Response.Listener<JsonBean>() {
            @Override
            public void onResponse(JsonBean jsonBean) {
                path_tv.setText("bitmap存储在:"+jsonBean.path);
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError volleyError) {
                Toast.makeText(MultiPartRequestActivity.this, volleyError.getMessage(), Toast.LENGTH_LONG).show();
            }
        });
        request.addFile(bitmap);

        request.setRetryPolicy(new DefaultRetryPolicy(50000,
                DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
                DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
        request.setTag(TAG);
        VolleySingleton.getInstance().addToRequestQueue(request);
    }

效果图在 Volley源码分析之自定义MultiPartRequest(文件上传)案例中

项目代码:http://download.csdn.net/detail/hexingen/9681762

相关知识点:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值