當(dāng)前項(xiàng)目用到了拍照/從相冊選取照片并裁剪展示到ImageView上的功能,在網(wǎng)上找到很多資料,卻發(fā)現(xiàn)大同小異,知道我看到Ryan Hoo大神的文章 茅塞頓開,在此記錄一下原理以及實(shí)現(xiàn)步驟.
一.點(diǎn)擊相應(yīng)Button彈出拍照/從相冊選取/取消布局(如圖)
實(shí)現(xiàn)方式很多 可以用Dialogfragment 我的做法是PopWindow,代碼貼上:
import android.content.Context;
import android.graphics.drawable.ColorDrawable;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.PopupWindow;
import android.widget.TextView;
/** * PopWindow */
public class SelectPicturePopupWindow extends PopupWindow implements View.OnClickListener, PopupWindow.OnDismissListener {
private View mContentView;
private Context mContext;
private PictureCallBack mPictureCallBack;
public SelectPicturePopupWindow(Context context, PictureCallBack pictureCallBack) {
super(context);
this.mContext = context;
this.mPictureCallBack = pictureCallBack;
init(context);
}
private void init(Context context) {
mContentView = LayoutInflater.from(context).inflate(R.layout.popup_select_picture,null);
mContentView.setOnClickListener(this); // 設(shè)置SelectPicPopupWindow的View
this.setContentView(mContentView); // 設(shè)置SelectPicPopupWindow彈出窗體的寬
this.setWidth(ViewGroup.LayoutParams.MATCH_PARENT); // 設(shè)置SelectPicPopupWindow彈出窗體的高
this.setHeight(ViewGroup.LayoutParams.MATCH_PARENT); // 刷新狀態(tài)
this.update(); // 實(shí)例化一個(gè)ColorDrawable顏色為半透明
ColorDrawable dw = new ColorDrawable(0000000000); // 點(diǎn)back鍵和其他地方使其消失,設(shè)置了這個(gè)才能觸發(fā)OnDismisslistener 对碌,設(shè)置其他控件變化等操作
this.setBackgroundDrawable(dw);
TextView cancel = (TextView) mContentView.findViewById(R.id.popup_cancel);
TextView gallery = (TextView) mContentView.findViewById(R.id.popup_select_from_gallery);
TextView takePicture = (TextView) mContentView.findViewById(R.id.popup_take_picture);
cancel.setOnClickListener(this);
gallery.setOnClickListener(this);
takePicture.setOnClickListener(this);
setOnDismissListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.popup_cancel: //取消
break;
case R.id.popup_select_from_gallery: //圖片圖庫
if (mPictureCallBack != null) {
mPictureCallBack.onSelectFromGallery();
}
break;
case R.id.popup_take_picture: //拍照
if (mPictureCallBack != null) {
mPictureCallBack.onTakePicture();
}
break;
}
dismiss();
}
@Override
public void onDismiss() {
dismiss();
}
public interface PictureCallBack{
void onTakePicture();
void onSelectFromGallery();
}
/**
* 顯示popupWindow
* @param parent
*/
public void show(View parent) {
if (!this.isShowing()) {
// 以下拉方式顯示popupwindow
showAtLocation(parent, Gravity.NO_GRAVITY,0,0);
} else {
this.dismiss();
}
}
}
布局文件:
<?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:background="#88000000"
android:gravity="bottom"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="10dp"
android:background="@drawable/popup_white_bg"
android:orientation="vertical">
<TextView
android:id="@+id/popup_take_picture"
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center"
android:paddingLeft="20dp"
android:text="拍照"
android:textColor="#666666"/>
<View
android:layout_width="match_parent"
android:layout_height="1px" android:background="#dfdfdf"/>
<TextView
android:id="@+id/popup_select_from_gallery"
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center"
android:paddingLeft="20dp"
android:text="照片圖庫"
android:textColor="#666666"/>
</LinearLayout>
<TextView
android:id="@+id/popup_cancel"
android:layout_width="match_parent"
android:layout_height="45dp"
android:background="@drawable/popup_white_bg"
android:gravity="center"
android:paddingLeft="20dp"
android:text="取消"
android:textColor="#666666"/>
</LinearLayout>
注釋也很清晰 在此不做過多贅述,接下來進(jìn)入重點(diǎn),首先是拍照截圖:
(一).在Button點(diǎn)擊事件中彈出剛自定義的Popwindow:
@Override
public void onClick(View view) {
if (view.getId() == R.id.btn_hey) {
//彈出PopWindow
if (mSelectPicturePopup == null) {
mSelectPicturePopup = new SelectPicturePopupWindow(this, this);
}
mSelectPicturePopup.show(view);
}
}
(二).準(zhǔn)備好使用到的Uri:
private static final String IMAGE_FILE_LOCATION = "file:///sdcard/test.jpg";
Uri imageUri = Uri.parse(IMAGE_FILE_LOCATION);
(三).在回調(diào)中調(diào)用Camera程序進(jìn)行拍照:
//拍照 調(diào)用相機(jī)
@Override
public void onTakePicture() {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intent, CODE_TAKE_PICTURE);
}
(四). onActivityResult中拿到返回的數(shù)據(jù),并傳遞給截圖的程序:
case CODE_TAKE_PICTURE:
if(resultCode == RESULT_OK){
cutPhoto(2,1,280,140,imageUri,TAKE_CUT);
}
break;
使用系統(tǒng)自帶的裁剪程序的方法我封裝好了一個(gè)方法:
Tip:
intent.putExtra("return-data", false);
false代表不返回?cái)?shù)據(jù),返回url
true代表返回?cái)?shù)據(jù),bitmap
private void cutPhoto(int aspectX,int aspectY,int outputX,int outputY,Uri uri,int requestCode) {
// 裁剪圖片意圖
Intent in = new Intent("com.android.camera.action.CROP");
in.setDataAndType(uri, "image/*");
in.putExtra("crop", "true");
// 裁剪框的比例蒿偎,2:1
in.putExtra("aspectX", aspectX);
in.putExtra("aspectY", aspectY);
// 裁剪后輸出圖片的尺寸大小
in.putExtra("outputX", outputX);
in.putExtra("outputY", outputY);
in.putExtra("scale", true);
in.putExtra(MediaStore.EXTRA_OUTPUT, uri);
in.putExtra("return-data", true);
in.putExtra("outputFormat", "PNG");// 圖片格式
in.putExtra("noFaceDetection", true);// 取消人臉識別
startActivityForResult(in, requestCode);// 開啟一個(gè)帶有返回值的Activity
}
(五).最后一步,處理返回的Bitmap:
case TAKE_CUT:
//拍照 拿到剪切數(shù)據(jù)
Bitmap bmap2 = data.getParcelableExtra("data");
iv_photo.setImageBitmap(bmap2);
如果你在裁剪意圖中返回的是url,那么只需這樣轉(zhuǎn)換就好:
if(imageUri != null){
Bitmap bitmap = decodeUriAsBitmap(imageUri);
iv_photo.setImageBitmap(bitmap);
}
private Bitmap decodeUriAsBitmap(Uri uri){
Bitmap bitmap = null;
try {
bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri));
} catch (FileNotFoundException e) {
e.printStackTrace();
return null;
}
return bitmap;
}
貼上效果圖:
從相冊圖庫選取只有意圖和url不一樣:
//從相冊選取 調(diào)用android的圖庫
@Override
public void onSelectFromGallery() {
Intent i = new Intent(Intent.ACTION_PICK,MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(i, CODE_FROM_GALLERY);}
在onActivityResult中
case CODE_FROM_GALLERY:
if (data != null) {
Uri uri = data.getData();
if (uri != null) {
cutPhoto(3,2,280,140,uri,PHOTO_REQUEST_CUT);
}
}
至此功能已經(jīng)完成 拿到bitmap之后,我們要上傳到服務(wù)器,我這邊要求是Base64,那么也很簡單:
public String bitmaptoString(Bitmap bitmap) {
String s = null;
// 將Bitmap轉(zhuǎn)換成Base64字符串
ByteArrayOutputStream bStream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, bStream);
byte[] bytes = bStream.toByteArray();
s = Base64.encodeToString(bytes, Base64.DEFAULT);
return s;
}
bug以及我不理解的地方:
1.點(diǎn)擊裁剪界面的取消按鈕程序會直接退出,不知道怎么控制.
2(已解決,方法:將裁剪圖片方法中返回?cái)?shù)據(jù)改為false,使用url進(jìn)行操作,因?yàn)橹苯臃祷豣itmap,一張相片3M多,會造成OOM)
如果我將裁剪的尺寸設(shè)置為寬800 高400,那么裁剪界面的確定取消按鈕都會無反應(yīng)
如果我將裁剪的尺寸設(shè)置為寬560 高280那么程序會蹦,報(bào)這個(gè)錯(cuò):
System: stat file error, path is /data/app/com.lxd.photocut-2/lib/arm64, exception is android.system.ErrnoException: stat failed: ENOENT (No such file or directory)
3.拍照后并沒有保存到我們的手機(jī)相冊中,我看了下qq微信,拍照后相冊中也會有,我在查閱第一行代碼(第二版)之后,按照郭霖大神的思路去寫 發(fā)現(xiàn)無效.代碼如下:
private Uri imageUri;
//創(chuàng)建File對象 用于存儲拍照后的圖片
File outputImage = new File(getExternalCacheDir(),"test111.jpg");
//如果文件存在 先刪除
try {
if(outputImage.exists()){
outputImage.delete();
}
outputImage.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
if(Build.VERSION.SDK_INT >= 24){
imageUri = FileProvider.getUriForFile(MainActivity.this,"com.lxd.photocut.fileprovider",outputImage);
}else {
imageUri = Uri.fromFile(outputImage);
}
//調(diào)用相機(jī)
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intent, CODE_TAKE_PICTURE);
思路是這樣的:首先創(chuàng)建File對象,放到了SD卡的應(yīng)用關(guān)聯(lián)緩存目錄下(因?yàn)?.0系統(tǒng)開始讀寫SD卡也被列為危險(xiǎn)權(quán)限),接著將File對象轉(zhuǎn)換成uri對象.我覺得是因?yàn)槲沂謾C(jī)沒有裝SD卡 那么相片存儲的位置該怎么獲取到呢?
以上.希望大神交流指點(diǎn)~