新版-Android手機拍照或從本地相冊選取圖片設(shè)置頭像高版本適配
傳送門
https://github.com/jiaweizeng/BalaPortrait
設(shè)置頭像通常有兩種方式:
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;//讀和相機權(quán)限
private final int PERMISSION_READ =1;//讀取權(quán)限
@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();//檢查是否有權(quán)限
}
});
}
// 從本地相冊選取圖片作為頭像
private void choseHeadImageFromGallery() {
// 設(shè)置文件類型 (在華為手機中不能獲取圖片飞几,要替換代碼)
/*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()) {
//設(shè)定拍照存放到自己指定的目錄,可以先建好
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) {
// 用戶沒有進行有效的設(shè)置操作,返回
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();
}
}
//權(quán)限申請回調(diào)
@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/*");
// 設(shè)置裁剪
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); //直接調(diào)用此代碼在小米手機有異常躁锁,換以下代碼
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());
//注意:此處應(yīng)設(shè)置return-data為false,如果設(shè)置為true卵史,是直接返回bitmap格式的數(shù)據(jù)战转,耗費內(nèi)存。設(shè)置為false以躯,然后槐秧,設(shè)置裁剪完之后保存的路徑,即: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);
}
/**
* 提取保存裁剪之后的圖片數(shù)據(jù)寸潦,并設(shè)置頭像部分的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();
}
}
/**
* 檢查設(shè)備是否存在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放至數(shù)組中社痛,意在bitmap的大屑(與實際讀取的原文件要大)
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitMap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
byte[] b = baos.toByteArray();
//將字節(jié)換成KB
double mid = b.length/1024;
//判斷bitmap占用空間是否大于允許最大空間 如果大于則壓縮 小于則不壓縮
if (mid > maxSize) {
//獲取bitmap大小 是允許最大大小的多少倍
double i = mid / maxSize;
//開始壓縮 此處用到平方根 將寬帶和高度壓縮掉對應(yīng)的平方根倍 (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();
// 創(chuàng)建操作圖片用的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觸發(fā)從本地相冊選取圖片作為頭像的操作時間撵儿;另外一個Button觸發(fā)手機拍攝照片作為頭像的操作事件乘客。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>
還要權(quán)限動態(tài)申請 WRITE_EXTERNAL_STORAGE
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
解決7.0 不能直接暴露獲取SD卡路徑的方法
在清單文件中添加provider,
FileUriExposedException
https://developer.android.google.cn/reference/android/os/FileUriExposedException.html
FileProvider
https://developer.android.google.cn/reference/android/support/v4/content/FileProvider.html
創(chuàng)建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); // 遞歸調(diào)用繼續(xù)統(tǒng)計
}
}
return dirSize;
}
/**
* 轉(zhuǎn)換文件大小
*
* @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轉(zhuǎn)成圖片路徑
if (data != null) {
// 獲取圖片URI
val uri = data.data
// 將URI轉(zhuǎn)換為路徑:
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()
// 最后根據(jù)索引值獲取圖片路徑
val photoPath = cursor.getString(columnIndex)
}