package com.polyvi.xface.extension.camera; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.concurrent.CountDownLatch; import android.app.ProgressDialog; import android.content.ContentResolver; import android.content.Intent; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Rect; import android.graphics.RectF; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.view.View; import android.view.Window; import android.view.WindowManager; import com.polyvi.xface.R; /* * 此类完成图片的裁剪,裁剪后的图片将会保存到当前应用程序的工作空间中, * 并且将裁剪后的图片在工作空间中的URI(以“file:”开头)返回给调用 * 此类的类。 */ public class ImageCroppingActivity extends MonitoredActivity { private int mAspectX, mAspectY; // 裁剪图片参数 private final Handler mHandler = new Handler(); private boolean mCircleCrop = false; boolean isSaveButtonClicked; // 裁剪图片后是否点击了“保存”按钮. private CropImageView mImageView; private Bitmap mBitmap; private HighlightView mCrop; private Uri targetUri; private Uri cropped_image_uri = null; //此URI由用户提供,亦即用户希望将裁剪后的图片保存到哪个空图片文件中 ,在启动本Activity的Intent中获取 private HighlightView hv; private ContentResolver mContentResolver; private static final int DEFAULT_WIDTH = 512; private static final int DEFAULT_HEIGHT = 384; private int width; private int height; private int sampleSize = 1; public final static String SOURCE_IMAGE_URI = "source_image_uri"; // Intent key word for getting URI of the cropping image. public final static String CROPPED_IMAGE_URI = "cropped_image_uri"; // Intent key word for setting URI of the cropped image; @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); requestWindowFeature(Window.FEATURE_NO_TITLE); // Make UI fullscreen. getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.image_crop_activity); initViews(); // 初始化图片裁剪Activity的界面按钮响应 Intent intent = getIntent(); targetUri = intent.getParcelableExtra(SOURCE_IMAGE_URI); //获取将被裁剪图片的URI cropped_image_uri = intent.getParcelableExtra(CROPPED_IMAGE_URI); //获取裁剪后图片的URI mContentResolver = getContentResolver(); //下面判断可能是多余的,因为执行此Activity时,mBitmap必然为null if (mBitmap == null) { getBitmapSize(); // 获取图片的宽和高,其值保存在本类的height和width属性中 getBitmap(); //为sampleSize和mBitmap赋值,即此时mBitmap不再为空 } if (mBitmap == null) { finish(); return; } startFaceDetection(); // 显示图片,实现裁剪工作 } /** * 初始化裁剪Activity */ private void initViews(){ mImageView = (CropImageView) findViewById(R.id.imgcutimageview); mImageView.mContext = this; findViewById(R.id.imgcutdiscard).setOnClickListener( new View.OnClickListener() { public void onClick(View v) { setResult(RESULT_CANCELED); finish(); } }); // 选择好裁剪区域后,响应“舍弃”按钮 findViewById(R.id.imgcutsave).setOnClickListener( new View.OnClickListener() { public void onClick(View v) { onSaveClicked(); } }); // 选择好裁剪区域后,响应“保存”按钮 } /** * 获取图片分辨率,即图片的宽和高的值 */ private void getBitmapSize(){ InputStream is = null; try { is = getInputStream(targetUri); BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeStream(is, null, options); width = options.outWidth; height = options.outHeight; }catch(IOException e) { e.printStackTrace(); }finally { if (is != null) { try { is.close(); } catch (IOException ignored) { } } } } /** * 获取图片文件中存储的数据 */ private void getBitmap(){ InputStream is = null; try { try { is = getInputStream(targetUri); } catch (IOException e) { e.printStackTrace(); } while ((width / sampleSize > DEFAULT_WIDTH * 2) || (height / sampleSize > DEFAULT_HEIGHT * 2)) { sampleSize *= 2; } BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = sampleSize; mBitmap = BitmapFactory.decodeStream(is, null, options); } finally { if (is != null) { try { is.close(); } catch (IOException ignored) { } } } } /** * 获取图片文件的输入流 * * @param mUri * @return InputStream */ private InputStream getInputStream(Uri mUri) throws IOException{ try { if (mUri.getScheme().equals("file")) { return new java.io.FileInputStream(mUri.getPath()); } else { return mContentResolver.openInputStream(mUri); } } catch (FileNotFoundException ex) { return null; } } /** * 根据图片文件的Uri获取文件的路径 * * @param mUri 文件的URI * * @return 文件的存储路径 */ private String getFilePath(Uri mUri){ try { if (mUri.getScheme().equals("file")) { return mUri.getPath(); } else { return getFilePathByUri(mUri); } } catch (FileNotFoundException ex) { return null; } } /** * 根据图片文件的Uri获取文件的路径,文件URL以“content”开头 * * @param mUri 文件的URI * * @return 文件的存储路径 */ private String getFilePathByUri(Uri mUri) throws FileNotFoundException{ String imgPath ; Cursor cursor = mContentResolver. query(mUri, null, null,null, null); cursor.moveToFirst(); imgPath = cursor.getString(1); return imgPath; } /** * 显示图片和图片裁剪框,以允许对图片进行裁剪 */ private void startFaceDetection() { if (isFinishing()) { return; } mImageView.setImageBitmapResetBase(mBitmap, true); startBackgroundJob(this, null, getResources().getString( R.string.runningFaceDetection), new Runnable() { public void run() { final CountDownLatch latch = new CountDownLatch(1); mHandler.post(new Runnable() { public void run() { final Bitmap b = mBitmap; if (b != mBitmap && b != null) { mImageView.setImageBitmapResetBase(b, true); mBitmap.recycle(); mBitmap = b; } if (mImageView.getScale() == 1F) { mImageView.center(true, true); } latch.countDown(); } }); try { latch.await(); } catch (InterruptedException e) { throw new RuntimeException(e); } mRunFaceDetection.run(); } }, mHandler); } private static class BackgroundJob extends MonitoredActivity.LifeCycleAdapter implements Runnable { private final MonitoredActivity mActivity; private final ProgressDialog mDialog; private final Runnable mJob; private final Handler mHandler; private final Runnable mCleanupRunner = new Runnable() { public void run() { mActivity.removeLifeCycleListener(BackgroundJob.this); if (mDialog.getWindow() != null) mDialog.dismiss(); } }; public BackgroundJob(MonitoredActivity activity, Runnable job, ProgressDialog dialog, Handler handler) { mActivity = activity; mDialog = dialog; mJob = job; mActivity.addLifeCycleListener(this); mHandler = handler; } public void run() { try { mJob.run(); } finally { mHandler.post(mCleanupRunner); } } @Override public void onActivityDestroyed(MonitoredActivity activity) { // We get here only when the onDestroyed being called before // the mCleanupRunner. So, run it now and remove it from the // queue mCleanupRunner.run(); mHandler.removeCallbacks(mCleanupRunner); } @Override public void onActivityStopped(MonitoredActivity activity) { mDialog.hide(); } @Override public void onActivityStarted(MonitoredActivity activity) { mDialog.show(); } } private static void startBackgroundJob(MonitoredActivity activity, String title, String message, Runnable job, Handler handler) { // Make the progress dialog uncancelable, so that we can gurantee // the thread will be done before the activity getting destroyed. ProgressDialog dialog = ProgressDialog.show(activity, title, message, true, false); new Thread(new BackgroundJob(activity, job, dialog, handler)). start(); } Runnable mRunFaceDetection = new Runnable() { float mScale = 1F; Matrix mImageMatrix; // Create a default HightlightView if we found no face in the // picture. private void makeDefault() { if(hv != null){ mImageView.remove(hv); } hv = new HighlightView(mImageView); int width = mBitmap.getWidth(); int height = mBitmap.getHeight(); Rect imageRect = new Rect(0, 0, width, height); // make the default size about 4/5 of the width or height int cropWidth = Math.min(width, height) * 4 / 5; int cropHeight = cropWidth; if (mAspectX != 0 && mAspectY != 0) { if (mAspectX > mAspectY) { cropHeight = cropWidth * mAspectY / mAspectX; } else { cropWidth = cropHeight * mAspectX / mAspectY; } } int x = (width - cropWidth) / 2; int y = (height - cropHeight) / 2; RectF cropRect = new RectF(x, y, x + cropWidth, y + cropHeight); hv.setup(mImageMatrix, imageRect, cropRect, mCircleCrop, mAspectX != 0 && mAspectY != 0); mImageView.add(hv); } public void run() { mImageMatrix = mImageView.getImageMatrix(); mScale = 1.0F / mScale; mHandler.post(new Runnable() { public void run() { makeDefault(); mImageView.invalidate(); if (mImageView.mHighlightViews.size() == 1) { mCrop = mImageView.mHighlightViews.get(0); mCrop.setFocus(true); } } }); } }; /** * 点击保存按钮后,完成对裁剪后的图片的处理。操作成功后,向调用本裁剪程序的程序传回裁剪后图片的URI * * 注意:Android系统默认裁剪程序是传回的是一个bitmap对象,如果此bitmap数据比较大的话就会引 * 起系统出错,并抛出异常:android.os.transactiontoolargeexception。为了避免出现 * 异常,所以在这里采取先保存裁剪后图片的数据到应用程序的工作空间,再将此图片URI传回,应用程序就可 * 以根据这个URI就可以拿到裁剪后的图片。 * */ private void onSaveClicked() { if (mCrop == null) { return; } if (isSaveButtonClicked) return; isSaveButtonClicked = true; final Bitmap croppedImage; Rect r = mCrop.getCropRect(); int width = r.width(); int height = r.height(); // If we are circle cropping, we want alpha channel, which is the // third param here. croppedImage = Bitmap.createBitmap(width,height,Bitmap.Config.RGB_565); Canvas canvas = new Canvas(croppedImage); Rect dstRect = new Rect(0, 0, width, height); canvas.drawBitmap(mBitmap, r, dstRect, null); // Release bitmap memory as soon as possible mImageView.clear(); mBitmap.recycle(); mBitmap = null; mImageView.setImageBitmapResetBase(croppedImage, true); mImageView.center(true, true); mImageView.mHighlightViews.clear(); String imgPath = getFilePath(targetUri);// 原图片的路径 String tempStr = null; if(cropped_image_uri != null){ tempStr = getFilePath(cropped_image_uri); } else { tempStr = imgPath + "_crop.jpg"; } final String cropPath = tempStr; //裁剪后图片的路径 mHandler.post(new Runnable(){ @Override public void run() { saveDrawableToCache(croppedImage,cropPath); } }); Uri cropUri = Uri.fromFile(new File(cropPath)); //这里将裁剪后图片的URI传回给调用者,其实也可以不传,因为,裁剪后图片的URI和调用者传来的URI是一样的。 Intent intent = new Intent("inline-data"); intent.putExtra(CROPPED_IMAGE_URI, cropUri); setResult(RESULT_OK, intent); finish(); } /** * 将Bitmap对象中的图片数据放入指定的图片文件中 * * @param bitmap //图片对象的数据 * @param filePath //图片要存放的绝对路径 * */ private void saveDrawableToCache(Bitmap bitmap,String filePath){ try { File file = new File(filePath); OutputStream outStream = new FileOutputStream(file); bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outStream); outStream.flush(); outStream.close(); } catch (IOException e) { e.printStackTrace(); } } @Override protected void onPause() { super.onPause(); } @Override protected void onDestroy() { super.onDestroy(); } }