Android手機拍照或從本地相簿選取圖片設定頭像。適配小米、華為、7.0
阿新 • • 發佈:2019-01-27
1,讓使用者通過選擇本地相簿之類的圖片庫中已有的影象,裁剪後作為頭像。
2,讓使用者啟動手機的相機拍照,拍完照片後裁剪,然後作為頭像。
程式碼如下
MainActivity.Java檔案:
package portrait.bala.portrait; import android.Manifest; import android.content.ContentValues; import android.content.Intent; import android.content.pm.PackageManager; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Matrix; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.provider.MediaStore; import android.support.annotation.NonNull; import android.support.v4.app.ActivityCompat; import android.support.v4.content.FileProvider; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.ImageView; import android.widget.Toast; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileNotFoundException; public class MainActivity extends AppCompatActivity { /* 頭像檔案 */ private static final String IMAGE_FILE_NAME = "temp_head_image.jpg"; private static final String CROP_IMAGE_FILE_NAME = "bala_crop.jpg"; /* 請求識別碼 */ private static final int CODE_GALLERY_REQUEST = 0xa0; private static final int CODE_CAMERA_REQUEST = 0xa1; private static final int CODE_RESULT_REQUEST = 0xa2; // 裁剪後圖片的寬(X)和高(Y),480 X 480的正方形。 private static int output_X = 480; private static int output_Y = 480; //改變頭像的標記位 private int new_icon=0xa3; private ImageView headImage = null; private String mExtStorDir; private Uri mUriPath; private final int PERMISSION_READ_AND_CAMERA =0;//讀和相機許可權 private final int PERMISSION_READ =1;//讀取許可權 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mExtStorDir = Environment.getExternalStorageDirectory().toString(); headImage = (ImageView) findViewById(R.id.imageView); Button buttonLocal = (Button) findViewById(R.id.buttonLocal); buttonLocal.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // choseHeadImageFromGallery(); checkReadPermission(); } }); Button buttonCamera = (Button) findViewById(R.id.buttonCamera); buttonCamera.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // choseHeadImageFromCameraCapture(); checkStoragePermission();//檢查是否有許可權 } }); } // 從本地相簿選取圖片作為頭像 private void choseHeadImageFromGallery() { // 設定檔案型別 (在華為手機中不能獲取圖片,要替換程式碼) /*Intent intentFromGallery = new Intent(); intentFromGallery.setType("image*//*"); intentFromGallery.setAction(Intent.ACTION_GET_CONTENT); startActivityForResult(intentFromGallery, CODE_GALLERY_REQUEST);*/ Intent intentFromGallery = new Intent(Intent.ACTION_PICK, null); intentFromGallery.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*"); startActivityForResult(intentFromGallery, CODE_GALLERY_REQUEST); } // 啟動手機相機拍攝照片作為頭像 private void choseHeadImageFromCameraCapture() { String savePath = mExtStorDir; Intent intent = null; // 判斷儲存卡是否可以用,可用進行儲存 if (hasSdcard()) { //設定拍照存放到自己指定的目錄,可以先建好 File file = new File(savePath); if (!file.exists()) { file.mkdirs(); } Uri pictureUri; File pictureFile = new File(savePath, IMAGE_FILE_NAME); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); pictureUri = FileProvider.getUriForFile(this, getPackageName()+".fileProvider", pictureFile); /*ContentValues contentValues = new ContentValues(1); contentValues.put(MediaStore.Images.Media.DATA, pictureFile.getAbsolutePath()); pictureUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);*/ } else { intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); pictureUri = Uri.fromFile(pictureFile); } /*if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); ContentValues contentValues = new ContentValues(1); contentValues.put(MediaStore.Images.Media.DATA, pictureFile.getAbsolutePath()); pictureUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues); } else { intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); pictureUri = Uri.fromFile(pictureFile); }*/ if (intent != null) { intent.putExtra(MediaStore.EXTRA_OUTPUT, pictureUri); startActivityForResult(intent, CODE_CAMERA_REQUEST); } } } public Uri getImageContentUri(File imageFile) { String filePath = imageFile.getAbsolutePath(); Cursor cursor = getContentResolver().query( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new String[] { MediaStore.Images.Media._ID }, MediaStore.Images.Media.DATA + "=? ", new String[] { filePath }, null); if (cursor != null && cursor.moveToFirst()) { int id = cursor.getInt(cursor .getColumnIndex(MediaStore.MediaColumns._ID)); Uri baseUri = Uri.parse("content://media/external/images/media"); return Uri.withAppendedPath(baseUri, "" + id); } else { if (imageFile.exists()) { ContentValues values = new ContentValues(); values.put(MediaStore.Images.Media.DATA, filePath); return getContentResolver().insert( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); } else { return null; } } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent intent) { // 使用者沒有進行有效的設定操作,返回 if (resultCode == RESULT_CANCELED) { Toast.makeText(getApplication(), "取消", Toast.LENGTH_LONG).show(); return; } switch (requestCode) { case CODE_GALLERY_REQUEST: cropRawPhoto(intent.getData()); break; case CODE_CAMERA_REQUEST: if (hasSdcard()) { File tempFile = new File( Environment.getExternalStorageDirectory(), IMAGE_FILE_NAME); // cropRawPhoto(Uri.fromFile(tempFile)); cropRawPhoto(getImageContentUri(tempFile)); } else { Toast.makeText(getApplication(), "沒有SDCard!", Toast.LENGTH_LONG) .show(); } break; case CODE_RESULT_REQUEST: /*if (intent != null) { setImageToHeadView(intent); //此程式碼在小米有異常,換以下程式碼 }*/ try { Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(mUriPath)); setImageToHeadView(intent,bitmap); } catch (FileNotFoundException e) { e.printStackTrace(); } break; } super.onActivityResult(requestCode, resultCode, intent); } private void checkStoragePermission() { int result = ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE); int resultCAMERA = ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA); if (result == PackageManager.PERMISSION_DENIED||resultCAMERA == PackageManager.PERMISSION_DENIED) { String[] permissions = {/*Manifest.permission.WRITE_EXTERNAL_STORAGE ,*/Manifest.permission.CAMERA,Manifest.permission.READ_EXTERNAL_STORAGE}; ActivityCompat.requestPermissions(this, permissions, PERMISSION_READ_AND_CAMERA); } else { choseHeadImageFromCameraCapture(); } } private void checkReadPermission() { int permission = ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE); if (permission==PackageManager.PERMISSION_DENIED){ String[] permissions ={Manifest.permission.READ_EXTERNAL_STORAGE}; ActivityCompat.requestPermissions(this,permissions, PERMISSION_READ); }else { choseHeadImageFromGallery(); } } //許可權申請回調 @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); switch (requestCode){ case PERMISSION_READ_AND_CAMERA: for (int i=0;i<grantResults.length;i++){ if (grantResults[i]==PackageManager.PERMISSION_DENIED){ Toast.makeText(this, "why ??????", Toast.LENGTH_SHORT).show(); return; } } choseHeadImageFromCameraCapture(); break; case PERMISSION_READ: if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { choseHeadImageFromGallery(); } break; } } /** * 裁剪原始的圖片 */ public void cropRawPhoto(Uri uri) { Intent intent = new Intent("com.android.camera.action.CROP"); intent.setDataAndType(uri, "image/*"); // 設定裁剪 intent.putExtra("crop", "true"); // aspectX , aspectY :寬高的比例 intent.putExtra("aspectX", 1); intent.putExtra("aspectY", 1); // outputX , outputY : 裁剪圖片寬高 intent.putExtra("outputX", output_X); intent.putExtra("outputY", output_Y); intent.putExtra("return-data", true); //startActivityForResult(intent, CODE_RESULT_REQUEST); //直接呼叫此程式碼在小米手機有異常,換以下程式碼 String mLinshi = System.currentTimeMillis() + CROP_IMAGE_FILE_NAME; File mFile = new File(mExtStorDir, mLinshi); // mHeadCachePath = mHeadCacheFile.getAbsolutePath(); mUriPath = Uri.parse("file://" + mFile.getAbsolutePath()); //將裁剪好的圖輸出到所建檔案中 intent.putExtra(MediaStore.EXTRA_OUTPUT, mUriPath); intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString()); //注意:此處應設定return-data為false,如果設定為true,是直接返回bitmap格式的資料,耗費記憶體。設定為false,然後,設定裁剪完之後儲存的路徑,即:intent.putExtra(MediaStore.EXTRA_OUTPUT, uriPath); // intent.putExtra("return-data", true); intent.putExtra("return-data", false); // intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); startActivityForResult(intent, CODE_RESULT_REQUEST); } /** * 提取儲存裁剪之後的圖片資料,並設定頭像部分的View */ private void setImageToHeadView(Intent intent,Bitmap b) { /*Bundle extras = intent.getExtras(); if (extras != null) { Bitmap photo = extras.getParcelable("data"); headImage.setImageBitmap(photo); }*/ try { if (intent != null) { // Bitmap bitmap = imageZoom(b);//看個人需求,可以不壓縮 headImage.setImageBitmap(b); // long millis = System.currentTimeMillis(); /*File file = FileUtil.saveFile(mExtStorDir, millis+CROP_IMAGE_FILE_NAME, bitmap); if (file!=null){ //傳遞新的頭像資訊給我的介面 Intent ii = new Intent(); setResult(new_icon,ii); Glide.with(this).load(file).apply(RequestOptions.circleCropTransform()) // .apply(RequestOptions.fitCenterTransform()) .apply(RequestOptions.placeholderOf(R.mipmap.user_logo)).apply(RequestOptions.errorOf(R.mipmap.user_logo)) .into(mIvTouxiangPersonal); // uploadImg(mExtStorDir,millis+CROP_IMAGE_FILE_NAME); uploadImg(mExtStorDir,millis+CROP_IMAGE_FILE_NAME); }*/ } } catch (Exception e) { e.printStackTrace(); } } /** * 檢查裝置是否存在SDCard的工具方法 */ public static boolean hasSdcard() { String state = Environment.getExternalStorageState(); if (state.equals(Environment.MEDIA_MOUNTED)) { // 有儲存的SDCard return true; } else { return false; } } private Bitmap imageZoom(Bitmap bitMap) { //圖片允許最大空間 單位:KB double maxSize =1000.00; //將bitmap放至陣列中,意在bitmap的大小(與實際讀取的原檔案要大) ByteArrayOutputStream baos = new ByteArrayOutputStream(); bitMap.compress(Bitmap.CompressFormat.JPEG, 100, baos); byte[] b = baos.toByteArray(); //將位元組換成KB double mid = b.length/1024; //判斷bitmap佔用空間是否大於允許最大空間 如果大於則壓縮 小於則不壓縮 if (mid > maxSize) { //獲取bitmap大小 是允許最大大小的多少倍 double i = mid / maxSize; //開始壓縮 此處用到平方根 將寬頻和高度壓縮掉對應的平方根倍 (1.保持刻度和高度和原bitmap比率一致,壓縮後也達到了最大大小佔用空間的大小) bitMap = zoomImage(bitMap, bitMap.getWidth() / Math.sqrt(i), bitMap.getHeight() / Math.sqrt(i)); } return bitMap; } /*** * 圖片的縮放方法 * * @param bgimage * :源圖片資源 * @param newWidth * :縮放後寬度 * @param newHeight * :縮放後高度 * @return */ public static Bitmap zoomImage(Bitmap bgimage, double newWidth, double newHeight) { // 獲取這個圖片的寬和高 float width = bgimage.getWidth(); float height = bgimage.getHeight(); // 建立操作圖片用的matrix物件 Matrix matrix = new Matrix(); // 計算寬高縮放率 float scaleWidth = ((float) newWidth) / width; float scaleHeight = ((float) newHeight) / height; // 縮放圖片動作 matrix.postScale(scaleWidth, scaleHeight); Bitmap bitmap = Bitmap.createBitmap(bgimage, 0, 0, (int) width, (int) height, matrix, true); return bitmap; } }
佈局檔案有三個元件:放置頭像的ImageView,兩個Button,其中一個Button觸發從本地相簿選取圖片作為頭像的操作時間;另外一個Button觸發手機拍攝照片作為頭像的操作事件。activity_main.xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <ImageView android:id="@+id/imageView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@mipmap/ic_launcher" /> <Button android:id="@+id/buttonLocal" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="本地相簿選取頭像" /> <Button android:id="@+id/buttonCamera" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="手機拍照選取頭像" /> </LinearLayout>
還要許可權動態申請 WRITE_EXTERNAL_STORAGE
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
解決7.0 不能直接暴露獲取SD卡路徑的方法
在清單檔案中新增provider,
FileUriExposedException
FileProvider
建立XML資料夾
file_paths.xml
<paths> <external-path path="Android/data/portrait.bala.portrait/" name="files_root" /> <external-path path="." name="external_storage_root" /> <external-path name="external_files" path="."/> </paths>
或者
<paths>
<external-path name="external_files" path="."/>
</paths>
要修改包名
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="portrait.bala.portrait.fileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"/>
</provider>
在application類中新增如下程式碼
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
if (Build.VERSION.SDK_INT >= 18){
builder.detectFileUriExposure();
}
StrictMode.setVmPolicy(builder.build());
FileUtil
public class FileUtil {
/**
* 獲取目錄檔案大小
*
* @param dir
* @return
*/
public static long getDirSize(File dir) {
if (dir == null) {
return 0;
}
if (!dir.isDirectory()) {
return 0;
}
long dirSize = 0;
File[] files = dir.listFiles();
for (File file : files) {
if (file.isFile()) {
dirSize += file.length();
} else if (file.isDirectory()) {
dirSize += file.length();
dirSize += getDirSize(file); // 遞迴呼叫繼續統計
}
}
return dirSize;
}
/**
* 轉換檔案大小
*
* @param fileS
* @return B/KB/MB/GB
*/
public static String formatFileSize(long fileS) {
java.text.DecimalFormat df = new java.text.DecimalFormat("#.00");
String fileSizeString = "";
if (fileS < 1024) {
fileSizeString = df.format((double) fileS) + "B";
} else if (fileS < 1048576) {
fileSizeString = df.format((double) fileS / 1024) + "KB";
} else if (fileS < 1073741824) {
fileSizeString = df.format((double) fileS / 1048576) + "MB";
} else {
fileSizeString = df.format((double) fileS / 1073741824) + "G";
}
return fileSizeString;
}
public static File saveFile(String filePath,String fileName, Bitmap bitmap){
ByteArrayOutputStream baos =new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG,100,baos);
byte[] bytes = baos.toByteArray();
try {
File file = new File(filePath, fileName);
FileOutputStream fos = new FileOutputStream(file);
fos.write(bytes);
fos.close();
return file;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
URI轉成圖片路徑
if (data != null) {
// 獲取圖片URI
val uri = data.data
// 將URI轉換為路徑:
val proj = arrayOf(MediaStore.Images.Media.DATA)
val cursor = managedQuery(uri, proj, null, null, null)
// 這個是獲得使用者選擇的圖片的索引值
val columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)
cursor.moveToFirst()
// 最後根據索引值獲取圖片路徑
val photoPath = cursor.getString(columnIndex)
}