一,基本状况
不说废话,先直接上图,大体状况就是,在一则New Message中添加一些附件(图片,视频,声音等)和信息一块儿发送,以下图; java
二,基本思路 android
其实,这就相似于咱们的拍照上传,是采用同样的处理方法,这里选择临时拍一张照片做为一块儿发送的附件,即上图中的Capture Picture。主要步骤: 异步
1)点击Capture Picture时,会启动系统Camera应用程序来拍照;主要使用startActivityForResult(Intent intent, int requestCode)方法来启动Camera程序;
2)拍摄照片;这里就是用Camera进行拍照,这里不作介绍;
3)保存照片,并将照片数据返回给Message应用;主要用到一个setResult(int resultCode, Intent intent)方法,返回到原调用程序,关闭Camera;
4)在Message应用中处理返回的数据;重写onActivityResult(int requestCode, int resultCode, Intent data)方法来处理返回的数据; ide
三,具体流程 布局
首先,咱们已经启动Message-->New Message-->Attachment,在Message主活动ComposeMessageActivity中有一个addAttachment()方法来实现为新信息添加附件; ui
private void addAttachment(int type, boolean replace) { switch (type) { case AttachmentTypeSelectorAdapter.ADD_IMAGE: /* TODO */ break; case AttachmentTypeSelectorAdapter.TAKE_PICTURE: { Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); intent.putExtra(MediaStore.EXTRA_OUTPUT, TempFileProvider.SCRAP_CONTENT_URI); startActivityForResult(intent, REQUEST_CODE_TAKE_PICTURE); break; } case AttachmentTypeSelectorAdapter.ADD_VIDEO: break; case AttachmentTypeSelectorAdapter.RECORD_VIDEO: { break; case AttachmentTypeSelectorAdapter.ADD_SOUND: break; case AttachmentTypeSelectorAdapter.RECORD_SOUND: break; case AttachmentTypeSelectorAdapter.ADD_SLIDESHOW: break; default: break; }
新建一个 MediaStore.ACTION_IMAGE_CAPTURE 意图的Intent,ACTION_IMAGE_CAPTURE 的定义为"android.media.action.IMAGE_CAPTURE",这就时告诉系统,要进行抓取图片,调用startActivityForResult()启动Camera。 this
在Camera中会进行正常的onCreate()-->Open Camera-->Preview,在此处会有一个不一样之处, spa
public void onCreate(Bundle icicle) { super.onCreate(icicle); …… mIsImageCaptureIntent = isImageCaptureIntent(); setContentView(R.layout.camera); if (mIsImageCaptureIntent) { mReviewDoneButton = (Rotatable) findViewById(R.id.btn_done); mReviewCancelButton = (Rotatable) findViewById(R.id.btn_cancel); findViewById(R.id.btn_cancel).setVisibility(View.VISIBLE); } else { mThumbnailView = (RotateImageView) findViewById(R.id.thumbnail); mThumbnailView.enableFilter(false); mThumbnailView.setVisibility(View.VISIBLE); } …… }
private boolean isImageCaptureIntent() { String action = getIntent().getAction(); return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)); }
从上面代码能够看出,Camera会判断当前的action,若为ACTION_IMAGE_CAPTURE,isImageCaptureIntent()会返回true,这时显示的界面也会有所不一样。 code
接下来就是Shutter键进行拍照,都知道正常拍照后会回到PictureCallback中的onPictureTaken()方法,再次Preview,保存图片等一些事务处理,而在这里也会回到onPictureTaken(),但有一些不一样,再也不Preview,而是停留在拍照的画面; 视频
public void onPictureTaken …… if (!mIsImageCaptureIntent) { startPreview(); startFaceDetection(); } if (!mIsImageCaptureIntent) { Size s = mParameters.getPictureSize(); mImageSaver.addImage(jpegData, mLocation, s.width, s.height); } else { mJpegImageData = jpegData; if (!mQuickCapture) { showPostCaptureAlert(); } else { doAttach(); } } …… }
mQuickCapture = getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
从前面咱们已经知道mIsImageCaptureIntent()返回true,取反则代码会直接运行else语句,这时咱们来到showPostCaptureAlert()方法,onCreate方法中mQuickCapture默认会是false。
private void showPostCaptureAlert() { if (mIsImageCaptureIntent) { Util.fadeOut(mIndicatorControlContainer); Util.fadeOut(mShutterButton); int[] pickIds = {R.id.btn_retake, R.id.btn_done}; for (int id : pickIds) { Util.fadeIn(findViewById(id)); } } }到这里,咱们仍是会看到mIsImageCaptureIntent,看来 mIsImageCaptureIntent始终贯彻整个过程,时咱们区别正常Camera拍照的根本所在,在showPostCaptureAlert()中将咱们的Shutter键改为R.id.btn_retake,另外新加一个R.id.btn_done,这是一个选择当前照片的ImageButton,其布局文件xml中配置了一个点击方法android:onClick="onReviewDoneClicked";
@OnClickAttr public void onReviewRetakeClicked(View v) { hidePostCaptureAlert(); startPreview(); startFaceDetection(); } @OnClickAttr public void onReviewDoneClicked(View v) { doAttach(); } @OnClickAttr public void onReviewCancelClicked(View v) { doCancel(); }其实,不仅是btn_done,其余的两个按钮btn_retake,btn_cacel都配置了相应的点击方法,因此咱们最终仍是回到了doAttach()方法,仔细的人会发现这个方法咱们以前看到过。
private void doAttach() { if (mPausing) { return; } byte[] data = mJpegImageData; if (mCropValue == null) { // First handle the no crop case -- just return the value. If the // caller specifies a "save uri" then write the data to it's // stream. Otherwise, pass back a scaled down version of the bitmap // directly in the extras. if (mSaveUri != null) { OutputStream outputStream = null; try { outputStream = mContentResolver.openOutputStream(mSaveUri); outputStream.write(data); outputStream.close(); setResultEx(RESULT_OK); finish(); } catch (IOException ex) { // ignore exception } finally { Util.closeSilently(outputStream); } } else { int orientation = Exif.getOrientation(data); Bitmap bitmap = Util.makeBitmap(data, 50 * 1024); bitmap = Util.rotate(bitmap, orientation); setResultEx(RESULT_OK, new Intent("inline-data").putExtra("data", bitmap)); finish(); } } else { // Save the image to a temp file and invoke the cropper Uri tempUri = null; FileOutputStream tempStream = null; try { File path = getFileStreamPath(sTempCropFilename); path.delete(); tempStream = openFileOutput(sTempCropFilename, 0); tempStream.write(data); tempStream.close(); tempUri = Uri.fromFile(path); } catch (FileNotFoundException ex) { setResultEx(Activity.RESULT_CANCELED); finish(); return; } catch (IOException ex) { setResultEx(Activity.RESULT_CANCELED); finish(); return; } finally { Util.closeSilently(tempStream); } Bundle newExtras = new Bundle(); if (mCropValue.equals("circle")) { newExtras.putString("circleCrop", "true"); } if (mSaveUri != null) { newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri); } else { newExtras.putBoolean("return-data", true); } Intent cropIntent = new Intent("com.android.camera.action.CROP"); cropIntent.setData(tempUri); cropIntent.putExtras(newExtras); startActivityForResult(cropIntent, CROP_MSG); } }
private void setupCaptureParams() { Bundle myExtras = getIntent().getExtras(); if (myExtras != null) { mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT); mCropValue = myExtras.getString("crop"); } }
mCropValue为null,至于mSaveUri在onCreate()方法中会调用一个setupCaptureParams()方法,取得Uri;这时会执行setResultEx(RESULT_OK),并调用 finish()关闭Camera。
protected void setResultEx(int resultCode) { mResultCodeForTesting = resultCode; setResult(resultCode); }
setResult()方法终于出现了,至此,Camera的功能已经完成。
而后,咱们继续回到ComposeMessageActivity,来到onActivityResult();
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { …… switch (requestCode) { case REQUEST_CODE_CREATE_SLIDESHOW: if (data != null) { WorkingMessage newMessage = WorkingMessage.load(this, data.getData()); if (newMessage != null) { mWorkingMessage = newMessage; mWorkingMessage.setConversation(mConversation); drawTopPanel(false); updateSendButtonState(); invalidateOptionsMenu(); } } break; case REQUEST_CODE_TAKE_PICTURE: { // create a file based uri and pass to addImage(). We want to read the JPEG // data directly from file (using UriImage) instead of decoding it into a Bitmap, // which takes up too much memory and could easily lead to OOM. File file = new File(TempFileProvider.getScrapPath(this)); Uri uri = Uri.fromFile(file); addImageAsync(uri, false); break; } …… } }再次switch (requestCode),case REQUEST_CODE_TAKE_PICTURE,这也是咱们最初startActivityForResult()中发送的参数,最终回到Capture Picture的处理:
1) TempFileProvider.getScrapPath(this)获取媒体文件的路径,我打印了绝对路径,一看结果我想你们都懂了,这是一个系统隐藏文件.temp.jpg:
/mnt/sdcard/Android/data/com.android.mms/cache/.temp.jpg;
2)调用addImageAsync(uri, false)方法异步加载图片到Message。