帶你實現拍照||從相冊選擇->裁剪->上傳服務器

涉及到的知識點:
1,調用系統拍照功能,拿到圖片進行裁剪,上傳服務器
2,調用手機相冊,拿到圖片進行裁剪,上傳服務器
3,拍照權限,讀取寫入存儲卡權限的請求和處理。
4,上傳服務器使用的我以前封裝的retrofit2和rxjava的類

有興趣的可以看retrofit2+rxjava2封裝解析
下面進入正題

1,實現拍照功能

首先我在主界面放了倆個按鈕一個點擊拍照,一個點擊從相冊獲取,底部放置一個ImageView用來顯示截取后的圖片,至于為什么一定要截取,后面我會講到

<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="cn.swt.m.photodemo.MainActivity">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="takePhoto"
        android:text="拍照"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="getPhotoFromPhone"
        android:text="從相冊獲取"/>

    <ImageView
        android:id="@+id/photo_img"
        android:layout_width="200dp"
        android:layout_height="200dp"/>
</LinearLayout>

當拍照按鈕被點擊的時候,請求拍照權限

   public void takePhoto(View view) {
        MPermissions.requestPermissions(MainActivity.this, 4, android.Manifest.permission.CAMERA, android.Manifest.permission.WRITE_EXTERNAL_STORAGE);
    }

權限申請我用的弘洋大神的庫具體可以看
Android 6.0 運行時權限處理完全解析
請求權限成功后,打開照相機

  @PermissionGrant(4)
    public void requestCameraSuccess() {
        String sdStatus = Environment.getExternalStorageState();
        if (!sdStatus.equals(Environment.MEDIA_MOUNTED)) { // 檢測sd是否可用
            Toast.makeText(this, "sd卡不可用", Toast.LENGTH_SHORT).show();
            return;
        }
        if (targetUri == null) {
            File dirfile = new File(photopath);
            if (!dirfile.exists()) {
                dirfile.mkdirs();
            }
            tempFile = new File(photopath, getPhotoFileName());
            targetUri = Uri.fromFile(tempFile);
        }
        Intent intent2 = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        intent2.putExtra(MediaStore.EXTRA_OUTPUT, targetUri);
        startActivityForResult(intent2, PHOTO_REQUEST_TAKEPHOTO);// 采用ForResult打開
    }

解釋一下上面的代碼
因為需要把拍照得到的照片保存到sd卡中,所以先判斷sd卡是不是可用
接下來指定一個路徑,根據路徑判斷文件夾是否存在,如果不存在就創建一個
代碼中定義了一些常量

    private static final int PHOTO_REQUEST_TAKEPHOTO = 1;//拍照
    private static final int PHOTO_REQUEST_GALLERY = 2;//相冊
    private static final int PHOTO_REQUEST_CUT = 3;// 結果
    private Uri targetUri;//拍照時 指定的存儲路徑
    File tempFile; //拍照存儲的文件
    File cropFilePath;//裁剪后的文件

接下來指定拍照之后文件的路徑和文字

tempFile = new File(photopath, getPhotoFileName());
  public static final String photopath = Environment.getExternalStorageDirectory() + "/photodemo";


    private String getPhotoFileName() {
        Date date = new Date(System.currentTimeMillis());
        SimpleDateFormat dateFormat = new SimpleDateFormat("'IMG'_yyyyMMdd_HHmmss");
        return dateFormat.format(date) + ".jpg";
    }

當拍照結束后,會在外部的根目錄生成一個photodemo的文件夾里面會有一張名字為IMG_加上當前時間.jpg的一張圖片,這張圖片是拍照后得到的 沒有經過裁剪的

最后調用系統的拍照功能,同時指定照片生成的路徑

  Intent intent2 = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        intent2.putExtra(MediaStore.EXTRA_OUTPUT, targetUri);
        startActivityForResult(intent2, PHOTO_REQUEST_TAKEPHOTO);// 采用ForResult打開

可以看到startActivityForResult調用的拍照功能,同時requestcode==PHOTO_REQUEST_TAKEPHOTO
所以重寫onActivityResult在onActivityResult中拿到拍攝的照片


    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode) {
            case PHOTO_REQUEST_TAKEPHOTO:
                if (resultCode == RESULT_OK) {
                    startPhotoZoom(targetUri, 200);// 裁剪圖片
                }
                break;
            case PHOTO_REQUEST_GALLERY:
                if (resultCode == RESULT_OK) {
                    startPhotoZoom(data.getData(), 200);// 裁剪圖片
                }
                break;
            case PHOTO_REQUEST_CUT:
                if (data != null && !"".equals(data)) {
                    Bundle extras = data.getExtras();
                    head = extras.getParcelable("data");
                    if (head != null) {
                        mImageView.setImageBitmap(head);
                        /**
                         * 上傳服務器代碼
                         */
                        RequestBody requestFile =
                                RequestBody.create(MediaType.parse("image/png"), cropFilePath);

                        // MultipartBody.Part is used to send also the actual file name
                        MultipartBody.Part Part =
                                MultipartBody.Part.createFormData("avatar", cropFilePath.getName(), requestFile);
                        Upload(Part);
                    }
                }
                break;
            default:
                break;

        }
    }

首先只看下面這個,其他的case后面會講到

   case PHOTO_REQUEST_TAKEPHOTO:
                if (resultCode == RESULT_OK) {
                    startPhotoZoom(targetUri, 200);// 裁剪圖片
                }
                break;
    private void startPhotoZoom(Uri uri, int size) {
        Intent intent = new Intent("com.android.camera.action.CROP");
        intent.setDataAndType(uri, "image/*");
        // crop為true是設置在開啟的intent中設置顯示的view可以剪裁
        intent.putExtra("crop", "true");
        // aspectX aspectY 是寬高的比例
        intent.putExtra("aspectX", 1);
        intent.putExtra("aspectY", 1);
        // outputX,outputY 是剪裁圖片的寬高
        intent.putExtra("outputX", size);
        intent.putExtra("outputY", size);
        intent.putExtra("return-data", true);
        cropFilePath = new File(photopath, getCroPhotoFileName());
        intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(cropFilePath));
        startActivityForResult(intent, PHOTO_REQUEST_CUT);
    }

前面說了 當拍照完后,再調用系統的裁剪
簡單的代碼上面我都標了注釋,我設置裁剪圖片的寬度是200,200,可以自行改變
通過 intent.setDataAndType(uri, "image/*");根據拍照后設置的圖片路徑拿到照片
最后設置裁剪后的圖片存放的位置,因為上傳服務器是上傳裁剪后的圖片

 cropFilePath = new File(photopath, getCroPhotoFileName());
        intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(cropFilePath));




--------------------------------
    private String getCroPhotoFileName() {
        Date date = new Date(System.currentTimeMillis());
        SimpleDateFormat dateFormat = new SimpleDateFormat("'CROIMG'_yyyyMMdd_HHmmss");
        return dateFormat.format(date) + ".jpg";
    }

這里說一下為什么要裁剪圖片,


這里寫圖片描述

前綴為croimg的是裁剪后的圖片
對比一下大小,差別很大,同時在手機上,控件加載并不需要這么大的圖片,所以裁剪看來是很必要的

最后拿到裁剪后的圖片后

case PHOTO_REQUEST_CUT:
                if (data != null && !"".equals(data)) {
                    Bundle extras = data.getExtras();
                    head = extras.getParcelable("data");
                    if (head != null) {
                        mImageView.setImageBitmap(head);
                        /**
                         * 上傳服務器代碼
                         */
                        RequestBody requestFile =
                                RequestBody.create(MediaType.parse("image/png"), cropFilePath);

                        // MultipartBody.Part is used to send also the actual file name
                        MultipartBody.Part Part =
                                MultipartBody.Part.createFormData("avatar", cropFilePath.getName(), requestFile);
                        Upload(Part);
                    }
                }
                break;

首先head是我定義的一個bitmap
Bitmap head;
可以直接通過
Bundle extras = data.getExtras();
head = extras.getParcelable("data");
拿到一張bitmap,同時這張bitmap也是裁剪過后的
然后根據裁剪后的圖片的路徑找到文件,并上傳到服務器

2,從相冊選擇照片

前面在第二個button定義了一個點擊事件

  
    public void getPhotoFromPhone(View view) {
        MPermissions.requestPermissions(MainActivity.this, 3, Manifest.permission.READ_EXTERNAL_STORAGE);
    }

請求讀取外部存儲卡的權限,請求成功后

  @PermissionGrant(3)
    public void requestContactSuccess() {
        String sdStatus = Environment.getExternalStorageState();
        if (!sdStatus.equals(Environment.MEDIA_MOUNTED)) { // 檢測sd是否可用
            Toast.makeText(this, "sd卡不可用", Toast.LENGTH_SHORT).show();
            return;
        }
        // 設置文件類型
        Intent intent = new Intent(Intent.ACTION_PICK, null);
        intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
        startActivityForResult(intent, PHOTO_REQUEST_GALLERY);
    }

    @PermissionDenied(3)
    public void requestContactFailed() {
        Toast.makeText(this, "沒有權限", Toast.LENGTH_SHORT).show();
    }

代碼很簡單,也是調用手機系統的選擇照片功能
拿到照片后,繼續裁剪

   case PHOTO_REQUEST_GALLERY:
                if (resultCode == RESULT_OK) {
                    startPhotoZoom(data.getData(), 200);// 裁剪圖片
                }
                break;

然后和上面開始走一樣的邏輯

最后簡單的說明retrofit2單文件上傳的參數說明
requestbody放置參數,比如我的接口需要一個token的參數

    RequestBody tokenString =
                                RequestBody.create(
                                        MediaType.parse("multipart/form-data"), token);

requestpart 放置文件,比如上面裁剪后得到的圖片,根據文件路徑拿到圖片位置

  RequestBody requestFile =
                                RequestBody.create(MediaType.parse("image/png"), cropFilePath);

                        // MultipartBody.Part is used to send also the actual file name
                        MultipartBody.Part Part =
                                MultipartBody.Part.createFormData("avatar", cropFilePath.getName(), requestFile);

上傳到服務器

最后看下效果圖,因為是模擬器跑的 所以沒有真實的鏡頭


這里寫圖片描述

完成代碼下載地址http://download.csdn.net/download/qq_15527709/9975422
現在csdn下載沒有0分了,至少1分。。這不能怪我。。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,368評論 25 708
  • 點擊查看原文 Web SDK 開發手冊 SDK 概述 網易云信 SDK 為 Web 應用提供一個完善的 IM 系統...
    layjoy閱讀 13,934評論 0 15
  • 如以上DEMO截圖所示效果,我們對于這種類似的功能肯定不算陌生,因為這可以說是實際開發中一類非常常見的功能需求了。...
    Machivellia閱讀 2,152評論 1 13
  • 發現 關注 消息 iOS 第三方庫、插件、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 12,245評論 4 61
  • 要活在真實中,不欺騙自己也不欺騙別人,除非與世隔絕。一旦有旁人見證我們的行為。不管我們樂意不樂意,都得適應旁觀我們...
    晨默閱讀 206評論 0 0