Android7.0 頭像 拍照、照片裁剪
首先,好規則,看看自己的實現效果:
當然,這個github 各種開源庫,這裡只講 Android 自帶的功能。
其實這個也不難,關鍵點無非就2個:
- 7.0 之後相機的 uri 獲取
- 裁剪時的 uri 獲取
這裡可以放一下底部 popupwindow 的佈局,另外可以看我的 popupwindow封裝:
拒絕無用功,封裝一個通用的 PopupWindow
<?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"
android:id="@+id/pop_root_ly"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:orientation="vertical">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="5dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id ="@+id/pop_pic"
android:layout_width="match_parent"
android:layout_height="40dp"
android:gravity="center"
android:text="@string/pic"
android:textColor="@color/black"
android:textSize="18sp" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?android:attr/listDivider"
android:padding="2dp" />
<TextView
android:id="@+id/pop_camera"
android:layout_width="match_parent"
android:layout_height="40dp"
android:gravity="center"
android:text="@string/camera"
android:textColor="@color/black"
android:textSize="18sp" />
</LinearLayout>
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_height="wrap_content">
<TextView
android:id="@+id/pop_cancel"
android:layout_width="match_parent"
android:layout_height="40dp"
android:gravity="center"
android:text="取消"
android:textColor="@color/black"
android:textSize="18sp" />
</android.support.v7.widget.CardView>
</LinearLayout>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
而頭像,我用的時開源框架,circleiamgeview,這個用來處理圓形頭像就可以了,這裡就不貼出來了,連結如下:
compile 'de.hdodenhof:circleimageview:2.1.0'
- 1
- 1
其中點選事件如下:
圖片:
/*Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image*//**//*");
startActivityForResult(intent,ToolUtils.SCAN_OPEN_PHONE);*/
//由於模擬器相簿的重新整理問題,採用如下開啟方式,實際開發請採用上面這種
Intent intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(intent, ToolUtils.SCAN_OPEN_PHONE);
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
相機:
/**
* 開啟相簿
*/
private void cameraPic() {
//建立一個file,用來儲存拍照後的照片
File outputfile = new File(mActivity.getExternalCacheDir(),"output.png");
try {
if (outputfile.exists()){
outputfile.delete();//刪除
}
outputfile.createNewFile();
} catch (Exception e) {
e.printStackTrace();
}
Uri imageuri ;
if (Build.VERSION.SDK_INT >= 24){
imageuri = FileProvider.getUriForFile(mActivity,
"com.rachel.studyapp.fileprovider", //可以是任意字串
outputfile);
}else{
imageuri = Uri.fromFile(outputfile);
}
//啟動相機程式
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
intent.putExtra(MediaStore.EXTRA_OUTPUT,imageuri);
startActivityForResult(intent,ToolUtils.PHONE_CAMERA);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
在拍照這裡,7.0 是需要做處理的,google 在7.0 之後,考慮到安全性的問題,用 fileProvider 來封裝 uri了,所以,這裡我們也處理一下,mCameraUri 是用來儲存拍照後,照片的 uri,可以簡單理解成該圖片的索引。
注意!!!如果是6.0的手機,請新增許可權申請,我這裡已經封裝好,就不貼出來干擾大家了。
既然上面填寫了 fileprovider 了,那麼用過content provider的都知道,我們得配置 provider 屬性了,如下,在你的 Androidmanifest.xml 下新增:
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.rachel.studyapp.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<!--提供共享路徑-->
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"/>
</provider>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
上面得 authorities 必須跟你剛才在 getUriForFile 得authorities 一致,exported 這裡填寫 false,不然又得來許可權問題了,grantUriPermissions 表示授予臨時訪問,然後再在下面新增一個 mete-data 標籤,用來提供 fileprovider 的共享路徑。
file_paths 名字隨便取,然後在新建一個 xml 資料夾,席間 file_paths 檔案填寫如下程式碼:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<paths>
<external-path path="" name="camera_photos" />
</paths>
</resources>
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
解釋如下:
- external-path 表示用來指定共享路徑的
- name 隨便取,只是一個標籤,買個關子,下面會解釋
- path 這個比較重要,如果不設定,則表示將整個 SD 卡進行共享,然後制定了,比如 path=”Pictrues”那麼,就只共享 sd卡下的 Pictures 資料夾
既然都配置好了,我們把上面的 mCameraUri 列印一下,如下所示:
cameraPic: content://com.rachel.studyapp.fileprovider/camera_photos/Android/data/com.rachel.studyapp/cache/output.png
- 1
- 1
看到這個 camera_photos 了吧,其實它就是一個 虛擬目錄,可以不管的。這個就是 7.0 之後 uri 經過封裝後的樣子。
拍照之後,就是要處理獲取的圖片了,在 onActivityReslut 中的處理也非常簡單,既然不管是從相簿,還是從相簿,我們都獲取到了正確的 uri,那麼,在獲取到 這個 uri 之後,啟動裁剪的 activity 就可以了,如下:
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK){
switch (requestCode){
case ToolUtils.SCAN_OPEN_PHONE: //從相簿圖片後返回的uri
//啟動裁剪
startActivityForResult(CutForPhoto(data.getData()),ToolUtils.PHONE_CROP);
break;
case ToolUtils.PHONE_CAMERA: //相機返回的 uri
//啟動裁剪
String path = mActivity.getExternalCacheDir().getPath();
String name = "output.png";
startActivityForResult(CutForCamera(path,name),ToolUtils.PHONE_CROP);
break;
case ToolUtils.PHONE_CROP:
try {
//獲取裁剪後的圖片,並顯示出來
Bitmap bitmap = BitmapFactory.decodeStream(
mActivity.getContentResolver().openInputStream(mCutUri));
mUserLogoIcon.setImageBitmap(bitmap);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
break;
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
可以看到,裁剪分為兩個,一個是從相簿中獲取,另一個則是拍照之後,再來裁剪的。不一樣的地方,往下看:
相簿裁剪:
/**
* 圖片裁剪
* @param uri
* @return
*/
@NonNull
private Intent CutForPhoto(Uri uri) {
try {
//直接裁剪
Intent intent = new Intent("com.android.camera.action.CROP");
//設定裁剪之後的圖片路徑檔案
File cutfile = new File(Environment.getExternalStorageDirectory().getPath(),
"cutcamera.png"); //隨便命名一個
if (cutfile.exists()){ //如果已經存在,則先刪除,這裡應該是上傳到伺服器,然後再刪除本地的,沒伺服器,只能這樣了
cutfile.delete();
}
cutfile.createNewFile();
//初始化 uri
Uri imageUri = uri; //返回來的 uri
Uri outputUri = null; //真實的 uri
Log.d(TAG, "CutForPhoto: "+cutfile);
outputUri = Uri.fromFile(cutfile);
mCutUri = outputUri;
Log.d(TAG, "mCameraUri: "+mCutUri);
// crop為true是設定在開啟的intent中設定顯示的view可以剪裁
intent.putExtra("crop",true);
// aspectX,aspectY 是寬高的比例,這裡設定正方形
intent.putExtra("aspectX",1);
intent.putExtra("aspectY",1);
//設定要裁剪的寬高
intent.putExtra("outputX", ToolUtils.dip2px(mActivity,200)); //200dp
intent.putExtra("outputY",ToolUtils.dip2px(mActivity,200));
intent.putExtra("scale",true);
//如果圖片過大,會導致oom,這裡設定為false
intent.putExtra("return-data",false);
if (imageUri != null) {
intent.setDataAndType(imageUri, "image/*");
}
if (outputUri != null) {
intent.putExtra(MediaStore.EXTRA_OUTPUT, outputUri);
}
intent.putExtra("noFaceDetection", true);
//壓縮圖片
intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
return intent;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
相機裁剪:
/**
* 拍照之後,啟動裁剪
* @param camerapath 路徑
* @param imgname img 的名字
* @return
*/
@NonNull
private Intent CutForCamera(String camerapath,String imgname) {
try {
//設定裁剪之後的圖片路徑檔案
File cutfile = new File(Environment.getExternalStorageDirectory().getPath(),
"cutcamera.png"); //隨便命名一個
if (cutfile.exists()){ //如果已經存在,則先刪除,這裡應該是上傳到伺服器,然後再刪除本地的,沒伺服器,只能這樣了
cutfile.delete();
}
cutfile.createNewFile();
//初始化 uri
Uri imageUri = null; //返回來的 uri
Uri outputUri = null; //真實的 uri
Intent intent = new Intent("com.android.camera.action.CROP");
//拍照留下的圖片
File camerafile = new File(camerapath,imgname);
if (Build.VERSION.SDK_INT >= 24) {
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
imageUri = FileProvider.getUriForFile(mActivity,
"com.rachel.studyapp.fileprovider",
camerafile);
} else {
imageUri = Uri.fromFile(camerafile);
}
outputUri = Uri.fromFile(cutfile);
//把這個 uri 提供出去,就可以解析成 bitmap了
mCutUri = outputUri;
// crop為true是設定在開啟的intent中設定顯示的view可以剪裁
intent.putExtra("crop",true);
// aspectX,aspectY 是寬高的比例,這裡設定正方形
intent.putExtra("aspectX",1);
intent.putExtra("aspectY",1);
//設定要裁剪的寬高
intent.putExtra("outputX", ToolUtils.dip2px(mActivity,200));
intent.putExtra("outputY",ToolUtils.dip2px(mActivity,200));
intent.putExtra("scale",true);
//如果圖片過大,會導致oom,這裡設定為false
intent.putExtra("return-data",false);
if (imageUri != null) {
intent.setDataAndType(imageUri, "image/*");
}
if (outputUri != null) {
intent.putExtra(MediaStore.EXTRA_OUTPUT, outputUri);
}
intent.putExtra("noFaceDetection", true);
//壓縮圖片
intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
return intent;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
上面兩個,相似度極高,不同的是,相簿是直接獲取 uri,而相機,則是通過拍照之後,獲取的 uri,你這裡可以合併一下,只是引數不一樣而已,這裡我為了讓大家有個對比的效果,就分開出來了。
上面一個,注意到用了 intent.putExtra(“return-data”,false);如果這裡設定為 true,那麼,返回是就是一個 btimap,但這樣做在記憶體不足的情況下,會報 OOM 的,所以,像這種大圖片的,我們一般是在本地生成一個裁剪之後的圖片,然後獲取到這個裁剪之後的 uri,再解析出來,才是正確的做法,所以這裡設定為false,並用 mcuturi 來作為這個本地儲存的 uri。
這樣就講完了。
這裡推薦一下,github,我覺得還不錯的兩個裁剪開源庫,SmartCropper,連結如下:
https://github.com/pqpo/SmartCropper
- 1
- 1
TakePhoto :
https://github.com/crazycodeboy/TakePhoto/
- 1
- 1