很多 APP 應用都有用戶頭像功能,用戶既可以調用攝像頭馬上拍一張美美的自拍,也可以打開相冊選取一張心儀的照片作為頭像。
1 調用攝像頭
布局文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
<? xml version = "1.0" encoding = "utf-8" ?> < LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android" android:id = "@+id/activity_main" android:layout_width = "match_parent" android:layout_height = "match_parent" android:orientation = "vertical" > <!--拍照按鈕--> < Button android:id = "@+id/open_photo" android:layout_width = "match_parent" android:layout_height = "wrap_content" android:text = "拍照" /> <!--照片展示--> < ImageView android:id = "@+id/img" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:layout_gravity = "center_horizontal" /> </ 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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
|
public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity" ; public static final int OPEN_PHOTO_REQUEST_CODE = 1 ; private Uri imgUrl = null ; private ImageView imageView; @Override protected void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); //為按鈕添加【打開攝像頭】事件 findViewById(R.id.open_photo).setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { File file = new File(getExternalCacheDir(), "imageView.jpg" ); //存儲照片 if (file.exists()) { file.delete(); } try { file.createNewFile(); } catch (IOException e) { e.printStackTrace(); } if (Build.VERSION.SDK_INT >= 24 ) { //版本高于 7.0 imgUrl = FileProvider.getUriForFile(MainActivity. this , "net.deniro.camera.fileProvider" , file); } else { imgUrl = Uri.fromFile(file); } //打開攝像頭 Intent intent = new Intent( "android.media.action.IMAGE_CAPTURE" ); intent.putExtra(MediaStore.EXTRA_OUTPUT, imgUrl); //指定圖片輸出地址 startActivityForResult(intent, OPEN_PHOTO_REQUEST_CODE); } }); //顯示拍攝的照片 imageView = (ImageView) findViewById(R.id.img); } @Override protected void onActivityResult( int requestCode, int resultCode, Intent data) { Log.d(TAG, "requestCode: " + requestCode); Log.d(TAG, "imgUrl: " + imgUrl); switch (requestCode) { case OPEN_PHOTO_REQUEST_CODE: if (resultCode == RESULT_OK) { try { //解析圖片并顯示 Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imgUrl)); imageView.setImageBitmap(bitmap); } catch (FileNotFoundException e) { e.printStackTrace(); } } break ; default : break ; } } } |
getExternalCacheDir() 可以獲取 SD 卡中專門用于存放當前應用緩存數據的位置。Android6.0+ 開始,讀取存放在 SD 卡中的任何其它目錄都被列為危險權限,因此需要設定運行時權限才可以操作,這里使用了與應用關聯的目錄,所以就可以跳過這一步。
getUriForFile() 方法接收三個參數:Context對象、任意唯一的字符串與 File對象。從 android 7.0+ 系統開始,直接使用本地真實的路徑被認為是不安全的,會拋出一個 FileExposedException 異常,而 FileProvider 是一種特殊的內容提供器,它使用與內容提供器類似的機制對數據進行保護。
在 AndroidManifest.xml 中注冊剛才定義的內容提供器:
1
2
3
4
5
6
7
8
9
10
|
< provider android:name = "android.support.v4.content.FileProvider" android:authorities = "net.deniro.camera.fileProvider" android:exported = "false" android:grantUriPermissions = "true" > <!--指定 Uri 的共享路徑--> < meta-data android:name = "android.support.FILE_PROVIDER_PATHS" android:resource = "@xml/file_paths" /> </ provider > |
android:authorities 就是我們在 FileProvider.getUriForFile() 方法中傳入的第二個參數。
使用 <meta-data> 指定了 Uri 的共享路徑,在此引用了 xml 資源。
在 IDEA 中可以通過快捷鍵 ctrl + enter 直接在 xml 文件夾下創建文件:
快捷創建
默認為 xml 文件夾
file_paths.xml:
1
2
3
4
5
6
7
|
<? xml version = "1.0" encoding = "utf-8" ?> < PreferenceScreen xmlns:android = "http://schemas.android.com/apk/res/android" > <!--指定共享的 Uri --> <!--name:名稱(任意)--> <!--path:共享的路徑,空值表示共享整個 SD 卡--> < external-path name = "img" path = "" /> </ PreferenceScreen > |
Android 4.4 之前,還需要設置訪問 SD 卡應用的關聯目錄權限:
1
|
< uses-permission android:name = "android.permission.WRITE_EXTERNAL_STORAGE" /> |
拍照后效果:
2 從相冊中選取照片
直接從相冊中選取一張現有的照片比打開攝像頭拍一張照片更加常用,因此,一個好的 app,應該將這兩種方式都實現。
修改布局文件,加入【打開相冊】按鈕:
1
2
3
4
5
6
7
|
<!--打開相冊選取照片--> < Button android:id = "@+id/choose_photo" android:layout_width = "match_parent" android:layout_height = "wrap_content" android:text = "打開相冊" /> |
在活動類中加入打開相冊選取照片的處理邏輯:
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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
|
/** * 打開相冊請求碼 */ public static final int CHOOSE_PHOTO_REQUEST_CODE = 2 ; ... @Override protected void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); ... //打開相冊 findViewById(R.id.choose_photo).setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { if (ContextCompat.checkSelfPermission(MainActivity. this , Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(MainActivity. this , new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, CHOOSE_PHOTO_REQUEST_CODE); } else { openAlbum(); } } }); /** * 打開相冊 */ private void openAlbum() { Intent intent = new Intent( "android.intent.action.GET_CONTENT" ); intent.setType( "image/*" ); startActivityForResult(intent, CHOOSE_PHOTO_REQUEST_CODE); } /** * 請求 WRITE_EXTERNAL_STORAGE 權限 * * 相冊中的照片一般是存放在 SD 卡上的,所以從 SD 卡中讀取照片需要申請權限 * * WRITE_EXTERNAL_STORAGE 表示讀寫 SD 卡的能力權限 * @param requestCode * @param permissions * @param grantResults */ @Override public void onRequestPermissionsResult( int requestCode, @NonNull String[] permissions, @NonNull int [] grantResults) { switch (requestCode) { case CHOOSE_PHOTO_REQUEST_CODE: if (grantResults.length > 0 && grantResults[ 0 ] == PackageManager.PERMISSION_GRANTED) { openAlbum(); } else { Toast.makeText( this , "被拒絕啦" , Toast.LENGTH_SHORT).show(); } break ; default : break ; } super .onRequestPermissionsResult(requestCode, permissions, grantResults); } @Override protected void onActivityResult( int requestCode, int resultCode, Intent data) { Log.d(TAG, "requestCode: " + requestCode); Log.d(TAG, "imgUrl: " + imgUrl); switch (requestCode) { case OPEN_PHOTO_REQUEST_CODE: if (resultCode == RESULT_OK) { try { //解析圖片并顯示 Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imgUrl)); imageView.setImageBitmap(bitmap); } catch (FileNotFoundException e) { e.printStackTrace(); } } break ; case CHOOSE_PHOTO_REQUEST_CODE: if (Build.VERSION.SDK_INT >= 19 ) { //4.4 及以上系統 handleFor4_4(data); } else { handleForBefore4_4(data); } break ; default : break ; } } /** * 處理相片 * android 4.4- * * @param data */ private void handleForBefore4_4(Intent data) { display(getImagePath(data.getData(), null )); } /** * 處理相片 * android 4.4+ * * @param data */ @RequiresApi (api = Build.VERSION_CODES.KITKAT) private void handleFor4_4(Intent data) { String imagePath = null ; Uri uri = data.getData(); //需要解析 if (DocumentsContract.isDocumentUri( this , uri)) { //是 Document 類型,使用 document id 來處理 String documentId = DocumentsContract.getDocumentId(uri); String mediaDocumentAuthority = "com.android.providers.media.documents" ; String downloadDocumentAuthority = "com.android.providers.downloads.documents" ; if (mediaDocumentAuthority.equals(uri.getAuthority())) { //media 格式 String id = documentId.split( ":" )[ 1 ]; //數字格式的 ID String selection = MediaStore.Images.Media._ID + "=" + id; imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection); } else if (downloadDocumentAuthority.equals(uri.getAuthority())) { Uri contentUri = ContentUris.withAppendedId(Uri.parse( "content://downloads/public_downloads" ), Long.valueOf(documentId)); imagePath = getImagePath(contentUri, null ); } } else if ( "content" .equalsIgnoreCase(uri.getScheme())) { //content 類型 uri imagePath = getImagePath(uri, null ); } else if ( "file" .equalsIgnoreCase(uri.getScheme())) { //file 類型,則直接獲取路徑 imagePath = uri.getPath(); } display(imagePath); } /** * 顯示 * @param path */ private void display(String path) { if (path== null ){ Toast.makeText( this , "無法獲取圖片" , Toast.LENGTH_SHORT).show(); } else { imageView.setImageBitmap(BitmapFactory.decodeFile(path)); } } /** * 獲取圖片路徑 * * @param uri * @param selection * @return */ private String getImagePath(Uri uri, String selection) { String path = null ; Cursor cursor = getContentResolver().query(uri, null , selection, null , null ); if (cursor != null ) { if (cursor.moveToFirst()) { path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA)); } cursor.close(); } return path; } |
這里請求了 WRITE_EXTERNAL_STORAGE 權限,以為相冊中的照片一般是存放在 SD 卡上的,所以從 SD 卡中讀取照片需要申請權限。WRITE_EXTERNAL_STORAGE 表示讀寫 SD 卡的能力權限。
為了兼容新老版本的手機(以 Android 4.4 為分水嶺),因為 Android 4.4+ 的版本返回的 Uri 需要解析才可以使用。
點擊【打開相冊】按鈕,會彈出讀取 SD 卡的權限申請:
選取照片后的效果:
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:https://www.jianshu.com/p/1bd723fc4826