涉及到的知識點:
1,調用系統拍照功能,拿到圖片進行裁剪,上傳服務器
2,調用手機相冊,拿到圖片進行裁剪,上傳服務器
3,拍照權限,讀取寫入存儲卡權限的請求和處理。
4,上傳服務器使用的我以前封裝的retrofit2和rxjava的類
有興趣的可以看retrofit2+rxjava2封裝解析
下面進入正題
1,實現拍照功能
首先我在主界面放了倆個按鈕一個點擊拍照,一個點擊從相冊獲取,底部放置一個ImageView用來顯示截取后的圖片,至于為什么一定要截取,后面我會講到
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="cn.swt.m.photodemo.MainActivity">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="takePhoto"
android:text="拍照"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="getPhotoFromPhone"
android:text="從相冊獲取"/>
<ImageView
android:id="@+id/photo_img"
android:layout_width="200dp"
android:layout_height="200dp"/>
</LinearLayout>
當拍照按鈕被點擊的時候,請求拍照權限
public void takePhoto(View view) {
MPermissions.requestPermissions(MainActivity.this, 4, android.Manifest.permission.CAMERA, android.Manifest.permission.WRITE_EXTERNAL_STORAGE);
}
權限申請我用的弘洋大神的庫具體可以看
Android 6.0 運行時權限處理完全解析
請求權限成功后,打開照相機
@PermissionGrant(4)
public void requestCameraSuccess() {
String sdStatus = Environment.getExternalStorageState();
if (!sdStatus.equals(Environment.MEDIA_MOUNTED)) { // 檢測sd是否可用
Toast.makeText(this, "sd卡不可用", Toast.LENGTH_SHORT).show();
return;
}
if (targetUri == null) {
File dirfile = new File(photopath);
if (!dirfile.exists()) {
dirfile.mkdirs();
}
tempFile = new File(photopath, getPhotoFileName());
targetUri = Uri.fromFile(tempFile);
}
Intent intent2 = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent2.putExtra(MediaStore.EXTRA_OUTPUT, targetUri);
startActivityForResult(intent2, PHOTO_REQUEST_TAKEPHOTO);// 采用ForResult打開
}
解釋一下上面的代碼
因為需要把拍照得到的照片保存到sd卡中,所以先判斷sd卡是不是可用
接下來指定一個路徑,根據路徑判斷文件夾是否存在,如果不存在就創建一個
代碼中定義了一些常量
private static final int PHOTO_REQUEST_TAKEPHOTO = 1;//拍照
private static final int PHOTO_REQUEST_GALLERY = 2;//相冊
private static final int PHOTO_REQUEST_CUT = 3;// 結果
private Uri targetUri;//拍照時 指定的存儲路徑
File tempFile; //拍照存儲的文件
File cropFilePath;//裁剪后的文件
接下來指定拍照之后文件的路徑和文字
tempFile = new File(photopath, getPhotoFileName());
public static final String photopath = Environment.getExternalStorageDirectory() + "/photodemo";
private String getPhotoFileName() {
Date date = new Date(System.currentTimeMillis());
SimpleDateFormat dateFormat = new SimpleDateFormat("'IMG'_yyyyMMdd_HHmmss");
return dateFormat.format(date) + ".jpg";
}
當拍照結束后,會在外部的根目錄生成一個photodemo的文件夾里面會有一張名字為IMG_加上當前時間.jpg的一張圖片,這張圖片是拍照后得到的 沒有經過裁剪的
最后調用系統的拍照功能,同時指定照片生成的路徑
Intent intent2 = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent2.putExtra(MediaStore.EXTRA_OUTPUT, targetUri);
startActivityForResult(intent2, PHOTO_REQUEST_TAKEPHOTO);// 采用ForResult打開
可以看到startActivityForResult調用的拍照功能,同時requestcode==PHOTO_REQUEST_TAKEPHOTO
所以重寫onActivityResult在onActivityResult中拿到拍攝的照片
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case PHOTO_REQUEST_TAKEPHOTO:
if (resultCode == RESULT_OK) {
startPhotoZoom(targetUri, 200);// 裁剪圖片
}
break;
case PHOTO_REQUEST_GALLERY:
if (resultCode == RESULT_OK) {
startPhotoZoom(data.getData(), 200);// 裁剪圖片
}
break;
case PHOTO_REQUEST_CUT:
if (data != null && !"".equals(data)) {
Bundle extras = data.getExtras();
head = extras.getParcelable("data");
if (head != null) {
mImageView.setImageBitmap(head);
/**
* 上傳服務器代碼
*/
RequestBody requestFile =
RequestBody.create(MediaType.parse("image/png"), cropFilePath);
// MultipartBody.Part is used to send also the actual file name
MultipartBody.Part Part =
MultipartBody.Part.createFormData("avatar", cropFilePath.getName(), requestFile);
Upload(Part);
}
}
break;
default:
break;
}
}
首先只看下面這個,其他的case后面會講到
case PHOTO_REQUEST_TAKEPHOTO:
if (resultCode == RESULT_OK) {
startPhotoZoom(targetUri, 200);// 裁剪圖片
}
break;
private void startPhotoZoom(Uri uri, int size) {
Intent intent = new Intent("com.android.camera.action.CROP");
intent.setDataAndType(uri, "image/*");
// crop為true是設置在開啟的intent中設置顯示的view可以剪裁
intent.putExtra("crop", "true");
// aspectX aspectY 是寬高的比例
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
// outputX,outputY 是剪裁圖片的寬高
intent.putExtra("outputX", size);
intent.putExtra("outputY", size);
intent.putExtra("return-data", true);
cropFilePath = new File(photopath, getCroPhotoFileName());
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(cropFilePath));
startActivityForResult(intent, PHOTO_REQUEST_CUT);
}
前面說了 當拍照完后,再調用系統的裁剪
簡單的代碼上面我都標了注釋,我設置裁剪圖片的寬度是200,200,可以自行改變
通過 intent.setDataAndType(uri, "image/*");根據拍照后設置的圖片路徑拿到照片
最后設置裁剪后的圖片存放的位置,因為上傳服務器是上傳裁剪后的圖片
cropFilePath = new File(photopath, getCroPhotoFileName());
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(cropFilePath));
--------------------------------
private String getCroPhotoFileName() {
Date date = new Date(System.currentTimeMillis());
SimpleDateFormat dateFormat = new SimpleDateFormat("'CROIMG'_yyyyMMdd_HHmmss");
return dateFormat.format(date) + ".jpg";
}
這里說一下為什么要裁剪圖片,
前綴為croimg的是裁剪后的圖片
對比一下大小,差別很大,同時在手機上,控件加載并不需要這么大的圖片,所以裁剪看來是很必要的
最后拿到裁剪后的圖片后
case PHOTO_REQUEST_CUT:
if (data != null && !"".equals(data)) {
Bundle extras = data.getExtras();
head = extras.getParcelable("data");
if (head != null) {
mImageView.setImageBitmap(head);
/**
* 上傳服務器代碼
*/
RequestBody requestFile =
RequestBody.create(MediaType.parse("image/png"), cropFilePath);
// MultipartBody.Part is used to send also the actual file name
MultipartBody.Part Part =
MultipartBody.Part.createFormData("avatar", cropFilePath.getName(), requestFile);
Upload(Part);
}
}
break;
首先head是我定義的一個bitmap
Bitmap head;
可以直接通過
Bundle extras = data.getExtras();
head = extras.getParcelable("data");
拿到一張bitmap,同時這張bitmap也是裁剪過后的
然后根據裁剪后的圖片的路徑找到文件,并上傳到服務器
2,從相冊選擇照片
前面在第二個button定義了一個點擊事件
public void getPhotoFromPhone(View view) {
MPermissions.requestPermissions(MainActivity.this, 3, Manifest.permission.READ_EXTERNAL_STORAGE);
}
請求讀取外部存儲卡的權限,請求成功后
@PermissionGrant(3)
public void requestContactSuccess() {
String sdStatus = Environment.getExternalStorageState();
if (!sdStatus.equals(Environment.MEDIA_MOUNTED)) { // 檢測sd是否可用
Toast.makeText(this, "sd卡不可用", Toast.LENGTH_SHORT).show();
return;
}
// 設置文件類型
Intent intent = new Intent(Intent.ACTION_PICK, null);
intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
startActivityForResult(intent, PHOTO_REQUEST_GALLERY);
}
@PermissionDenied(3)
public void requestContactFailed() {
Toast.makeText(this, "沒有權限", Toast.LENGTH_SHORT).show();
}
代碼很簡單,也是調用手機系統的選擇照片功能
拿到照片后,繼續裁剪
case PHOTO_REQUEST_GALLERY:
if (resultCode == RESULT_OK) {
startPhotoZoom(data.getData(), 200);// 裁剪圖片
}
break;
然后和上面開始走一樣的邏輯
最后簡單的說明retrofit2單文件上傳的參數說明
requestbody放置參數,比如我的接口需要一個token的參數
RequestBody tokenString =
RequestBody.create(
MediaType.parse("multipart/form-data"), token);
requestpart 放置文件,比如上面裁剪后得到的圖片,根據文件路徑拿到圖片位置
RequestBody requestFile =
RequestBody.create(MediaType.parse("image/png"), cropFilePath);
// MultipartBody.Part is used to send also the actual file name
MultipartBody.Part Part =
MultipartBody.Part.createFormData("avatar", cropFilePath.getName(), requestFile);
上傳到服務器
最后看下效果圖,因為是模擬器跑的 所以沒有真實的鏡頭
完成代碼下載地址http://download.csdn.net/download/qq_15527709/9975422
現在csdn下載沒有0分了,至少1分。。這不能怪我。。