1. 程式人生 > >Android 頭像選擇(拍照、相簿裁剪),含7.0的坑

Android 頭像選擇(拍照、相簿裁剪),含7.0的坑

首先,好規則,看看自己的實現效果:

當然,這個github 各種開源庫,這裡只講 Android 自帶的功能。

其實這個也不難,關鍵點無非就2個:

  • 7.0 之後相機的 uri 獲取
  • 裁剪時的 uri 獲取

這裡可以放一下底部 popupwindow 的佈局,另外可以看我的 popupwindow封裝:
拒絕無用功,封裝一個通用的 PopupWindow

<?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" android:id="@+id/pop_root_ly" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="5dp" android:orientation="vertical">
<android.support.v7.widget.CardView android:layout_width
="match_parent" android:layout_height="wrap_content" app:cardCornerRadius="5dp">
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:id
="@+id/pop_pic" android:layout_width="match_parent" android:layout_height="40dp" android:gravity="center" android:text="@string/pic" android:textColor="@color/black" android:textSize="18sp" />
<View android:layout_width="match_parent" android:layout_height="1dp" android:background="?android:attr/listDivider" android:padding="2dp" /> <TextView android:id="@+id/pop_camera" android:layout_width="match_parent" android:layout_height="40dp" android:gravity="center" android:text="@string/camera" android:textColor="@color/black" android:textSize="18sp" /> </LinearLayout> </android.support.v7.widget.CardView> <android.support.v7.widget.CardView android:layout_width="match_parent" android:layout_marginTop="5dp" android:layout_marginBottom="5dp" android:layout_height="wrap_content"> <TextView android:id="@+id/pop_cancel" android:layout_width="match_parent" android:layout_height="40dp" android:gravity="center" android:text="取消" android:textColor="@color/black" android:textSize="18sp" /> </android.support.v7.widget.CardView> </LinearLayout>

而頭像,我用的時開源框架,circleiamgeview,這個用來處理圓形頭像就可以了,這裡就不貼出來了,連結如下:

compile 'de.hdodenhof:circleimageview:2.1.0'

其中點選事件如下:
圖片:

/*Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image*//**//*");
startActivityForResult(intent,ToolUtils.SCAN_OPEN_PHONE);*/
   //由於模擬器相簿的重新整理問題,採用如下開啟方式,實際開發請採用上面這種
    Intent intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
    startActivityForResult(intent, ToolUtils.SCAN_OPEN_PHONE);

相機:

 /**
     * 開啟相簿
     */
    private void cameraPic() {
        //建立一個file,用來儲存拍照後的照片
        File outputfile = new File(mActivity.getExternalCacheDir(),"output.png");
        try {
            if (outputfile.exists()){
                outputfile.delete();//刪除
            }
            outputfile.createNewFile();
        } catch (Exception e) {
            e.printStackTrace();
        }
        Uri imageuri ;
        if (Build.VERSION.SDK_INT >= 24){
            imageuri = FileProvider.getUriForFile(mActivity,
                    "com.rachel.studyapp.fileprovider", //可以是任意字串
                    outputfile);
        }else{
            imageuri = Uri.fromFile(outputfile);
        }
        //啟動相機程式
        Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
        intent.putExtra(MediaStore.EXTRA_OUTPUT,imageuri);
        startActivityForResult(intent,ToolUtils.PHONE_CAMERA);
    }

在拍照這裡,7.0 是需要做處理的,google 在7.0 之後,考慮到安全性的問題,用 fileProvider 來封裝 uri了,所以,這裡我們也處理一下,mCameraUri 是用來儲存拍照後,照片的 uri,可以簡單理解成該圖片的索引。

注意!!!如果是6.0的手機,請新增許可權申請,我這裡已經封裝好,就不貼出來干擾大家了。

既然上面填寫了 fileprovider 了,那麼用過content provider的都知道,我們得配置 provider 屬性了,如下,在你的 Androidmanifest.xml 下新增:

<provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.rachel.studyapp.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <!--提供共享路徑-->
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths"/>
        </provider>

上面得 authorities 必須跟你剛才在 getUriForFile 得authorities 一致,exported 這裡填寫 false,不然又得來許可權問題了,grantUriPermissions 表示授予臨時訪問,然後再在下面新增一個 mete-data 標籤,用來提供 fileprovider 的共享路徑。
file_paths 名字隨便取,然後在新建一個 xml 資料夾,席間 file_paths 檔案填寫如下程式碼:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <paths>
        <external-path path="" name="camera_photos" />
    </paths>
</resources>

解釋如下:

  • external-path 表示用來指定共享路徑的
  • name 隨便取,只是一個標籤,買個關子,下面會解釋
  • path 這個比較重要,如果不設定,則表示將整個 SD 卡進行共享,然後制定了,比如 path=”Pictrues”那麼,就只共享 sd卡下的 Pictures 資料夾

既然都配置好了,我們把上面的 mCameraUri 列印一下,如下所示:

cameraPic: content://com.rachel.studyapp.fileprovider/camera_photos/Android/data/com.rachel.studyapp/cache/output.png

看到這個 camera_photos 了吧,其實它就是一個 虛擬目錄,可以不管的。這個就是 7.0 之後 uri 經過封裝後的樣子。

拍照之後,就是要處理獲取的圖片了,在 onActivityReslut 中的處理也非常簡單,既然不管是從相簿,還是從相簿,我們都獲取到了正確的 uri,那麼,在獲取到 這個 uri 之後,啟動裁剪的 activity 就可以了,如下:

@Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == RESULT_OK){
            switch (requestCode){
                case ToolUtils.SCAN_OPEN_PHONE: //從相簿圖片後返回的uri
                    //啟動裁剪
                    startActivityForResult(CutForPhoto(data.getData()),ToolUtils.PHONE_CROP);
                    break;
                case ToolUtils.PHONE_CAMERA: //相機返回的 uri
                    //啟動裁剪
                    String path = mActivity.getExternalCacheDir().getPath();
                    String name = "output.png";
                    startActivityForResult(CutForCamera(path,name),ToolUtils.PHONE_CROP);
                    break;
                case ToolUtils.PHONE_CROP:
                    try {
                        //獲取裁剪後的圖片,並顯示出來
                        Bitmap bitmap = BitmapFactory.decodeStream(
                                mActivity.getContentResolver().openInputStream(mCutUri));
                        mUserLogoIcon.setImageBitmap(bitmap);
                    } catch (FileNotFoundException e) {
                        e.printStackTrace();
                    }
                    break;
            }
        }
    }

可以看到,裁剪分為兩個,一個是從相簿中獲取,另一個則是拍照之後,再來裁剪的。不一樣的地方,往下看:
相簿裁剪:

/**
     * 圖片裁剪
     * @param uri
     * @return
     */
    @NonNull
    private Intent CutForPhoto(Uri uri) {
        try {
            //直接裁剪
            Intent intent = new Intent("com.android.camera.action.CROP");
            //設定裁剪之後的圖片路徑檔案
            File cutfile = new File(Environment.getExternalStorageDirectory().getPath(),
                    "cutcamera.png"); //隨便命名一個
            if (cutfile.exists()){ //如果已經存在,則先刪除,這裡應該是上傳到伺服器,然後再刪除本地的,沒伺服器,只能這樣了
                cutfile.delete();
            }
            cutfile.createNewFile();
            //初始化 uri
            Uri imageUri = uri; //返回來的 uri
            Uri outputUri = null; //真實的 uri
            Log.d(TAG, "CutForPhoto: "+cutfile);
            outputUri = Uri.fromFile(cutfile);
            mCutUri = outputUri;
            Log.d(TAG, "mCameraUri: "+mCutUri);
            // crop為true是設定在開啟的intent中設定顯示的view可以剪裁
            intent.putExtra("crop",true);
            // aspectX,aspectY 是寬高的比例,這裡設定正方形
            intent.putExtra("aspectX",1);
            intent.putExtra("aspectY",1);
            //設定要裁剪的寬高
            intent.putExtra("outputX", ToolUtils.dip2px(mActivity,200)); //200dp
            intent.putExtra("outputY",ToolUtils.dip2px(mActivity,200));
            intent.putExtra("scale",true);
            //如果圖片過大,會導致oom,這裡設定為false
            intent.putExtra("return-data",false);
            if (imageUri != null) {
                intent.setDataAndType(imageUri, "image/*");
            }
            if (outputUri != null) {
                intent.putExtra(MediaStore.EXTRA_OUTPUT, outputUri);
            }
            intent.putExtra("noFaceDetection", true);
            //壓縮圖片
            intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
            return intent;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

相機裁剪:

 /**
     * 拍照之後,啟動裁剪
     * @param camerapath 路徑
     * @param imgname img 的名字
     * @return
     */
    @NonNull
    private Intent CutForCamera(String camerapath,String imgname) {
        try {

            //設定裁剪之後的圖片路徑檔案
            File cutfile = new File(Environment.getExternalStorageDirectory().getPath(),
                    "cutcamera.png"); //隨便命名一個
            if (cutfile.exists()){ //如果已經存在,則先刪除,這裡應該是上傳到伺服器,然後再刪除本地的,沒伺服器,只能這樣了
                cutfile.delete();
            }
            cutfile.createNewFile();
            //初始化 uri
            Uri imageUri = null; //返回來的 uri
            Uri outputUri = null; //真實的 uri
            Intent intent = new Intent("com.android.camera.action.CROP");
            //拍照留下的圖片
            File camerafile = new File(camerapath,imgname);
            if (Build.VERSION.SDK_INT >= 24) {
                intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                imageUri = FileProvider.getUriForFile(mActivity,
                        "com.rachel.studyapp.fileprovider",
                        camerafile);
            } else {
                imageUri = Uri.fromFile(camerafile);
            }
            outputUri = Uri.fromFile(cutfile);
            //把這個 uri 提供出去,就可以解析成 bitmap了
            mCutUri = outputUri;
            // crop為true是設定在開啟的intent中設定顯示的view可以剪裁
            intent.putExtra("crop",true);
            // aspectX,aspectY 是寬高的比例,這裡設定正方形
            intent.putExtra("aspectX",1);
            intent.putExtra("aspectY",1);
            //設定要裁剪的寬高
            intent.putExtra("outputX", ToolUtils.dip2px(mActivity,200));
            intent.putExtra("outputY",ToolUtils.dip2px(mActivity,200));
            intent.putExtra("scale",true);
            //如果圖片過大,會導致oom,這裡設定為false
            intent.putExtra("return-data",false);
            if (imageUri != null) {
                intent.setDataAndType(imageUri, "image/*");
            }
            if (outputUri != null) {
                intent.putExtra(MediaStore.EXTRA_OUTPUT, outputUri);
            }
            intent.putExtra("noFaceDetection", true);
            //壓縮圖片
            intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
            return intent;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

上面兩個,相似度極高,不同的是,相簿是直接獲取 uri,而相機,則是通過拍照之後,獲取的 uri,你這裡可以合併一下,只是引數不一樣而已,這裡我為了讓大家有個對比的效果,就分開出來了。

上面一個,注意到用了 intent.putExtra(“return-data”,false);如果這裡設定為 true,那麼,返回是就是一個 btimap,但這樣做在記憶體不足的情況下,會報 OOM 的,所以,像這種大圖片的,我們一般是在本地生成一個裁剪之後的圖片,然後獲取到這個裁剪之後的 uri,再解析出來,才是正確的做法,所以這裡設定為false,並用 mcuturi 來作為這個本地儲存的 uri。

這樣就講完了。
這裡推薦一下,github,我覺得還不錯的兩個裁剪開源庫,SmartCropper,連結如下:

https://github.com/pqpo/SmartCropper

TakePhoto :

https://github.com/crazycodeboy/TakePhoto/

相關推薦

Android 頭像選擇(拍照相簿裁剪),7.0

首先,好規則,看看自己的實現效果: 當然,這個github 各種開源庫,這裡只講 Android 自帶的功能。 其實這個也不難,關鍵點無非就2個: 7.0 之後相機的 uri 獲取 裁剪時的 uri 獲取 這裡可以放一下底部 popupwin

android 手機拍照相簿選擇照片並顯示

關鍵程式碼: public class MainActivity extends AppCompatActivity implements View.OnClickListener { private Button btn1; private Uri imageUri;

Android開發:仿微信和QQ空間發說說相簿讀取拍照圖片裁剪和圖片上傳伺服器等功能的實現

第一步:新增依賴包: dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support:appcompat-v7:23.2.1' compile project('

Android拍照相簿 獲取圖片後,裁剪圖片

最近在做的B2B的專案,圖片大部分來源於使用者自己上傳; 由於android尺寸的不一,使用者相機,相簿的圖片也是奇形怪狀; 所以在上傳之前對圖片做一次裁剪是很有必要的! 下面是按比例裁剪圖片的demo 資原始檔activity_main.xml

Android開發:相簿讀取拍照圖片裁剪和圖片上傳伺服器等功能的實現

修改日誌 2016.05.12 之前的程式存在兩個問題: 1)從相簿選擇的圖片如果比較大,會失敗; 2)無法拍照上傳照片。 修改了這兩個bug,之前的程式碼已經被覆蓋掉了,留著太誤人子弟了。同時修改了一下標題和文章的文字描述 拍照示意 相簿示

Android實現仿微信朋友圈釋出動態(拍照相簿選擇照片壓縮顯示儲存縮圖點選縮圖刪除對應檔案等)附原始碼

         原創作品,轉載請註明出處:http://blog.csdn.net/zhang3776813/article/details/52092591 最近專案需求中要用到類似微信朋友圈釋出動態選擇圖片的UI效果,研究了一下,特來分享成果,多的不說來看程式碼。

理解Android影象處理-拍照單/多圖選擇器及影象優化

如以上DEMO截圖所示效果,我們對於這種類似的功能肯定不算陌生,因為這可以說是實際開發中一類非常常見的功能需求了。而關於它們的實現,其實主要涉及到的知識面應該就是 Android當中的影象處理了。簡單來說就比如:影象獲取(例如常見的設定頭像(獲取單

android 提示“無法返回該圖片”有些機型比如:sony 手機resultCode = 0或者照相相簿裁剪時候onActivityResult的Intent返回null

這個弄了一下的時間糊里糊塗解決的。 問題就是不管怎麼呼叫相簿還是相機都是返回 “無法返回該圖片”,debug到startActivityForResult都有資料,但在onActivityResult接收資料的時候resultCode = 0了,,本該返回-1怎麼就變成0了,,一直想不明白,

Phonegap jQueryMobile 線上應用拍照相簿選擇上傳+預覽

     拍照上傳以及從相簿中選擇圖片上傳是大多數手機應用的典型功能,這幾天在做一個移動端的專案,其中就用到該功能。      專案技術選擇的是Phonegap+jQueryMobile,因為jQuery自己比較熟悉,用jQueryMobile沒有什麼大的難點。      介面用jQueryMobile搭的

Android 用MultiImageSelector實現上傳頭像拍照相簿

最近在公司做一個APP,想要實現拍照或則相簿裁剪圖片的功能 上網查閱了很多資料,然後對其一一做了測試,可是後來發現在小米手機上的裁剪方面沒有起作用,而且還閃退.後來終於發現了原來是沒有新增:  intent.putExtra(MediaStore.EXTRA_OUTPUT,

iOS 相機拍照相簿獲取照片(仿微信) 一一 拍照圖片裁剪

先來兩張效果圖 1.使用相機拍照 匯入需要的框架 #import <AVFoundation/AVFoundation.h> 建立相機 /** 捕獲裝置,通常是前置攝像頭,後置攝像頭,麥克風(音訊輸入) */ @property (strong, n

android 呼叫系統拍照選擇本地照片

一直以來也沒寫過拍照和選擇本地照片的功能,最近專案中有這個功能,就寫下來,網上看了很多,但很多都有問題。 本來是想再當前頁面直接自定義dialog的,這樣的話就少寫一次setResult(),但怎麼寫都出錯,只能跳另一個activity,在設定其透明。沒辦法,下面是acti

Android呼叫系統相機相簿裁剪圖片並壓縮上傳(適配7.0

作者:八怪不姓醜 連結:http://www.jianshu.com/p/e11a34e2ea4f 著作權歸作者所有,本文經作者授權推送。 一、前言 最近在開發中遇到了一個比較棘手的問題 由於在之前使用的版本-targetSdkVersion小於24也就是小於7.

Android7.0 頭像 拍照照片裁剪

首先,好規則,看看自己的實現效果: 當然,這個github 各種開源庫,這裡只講 Android 自帶的功能。 其實這個也不難,關鍵點無非就2個: 7.0 之後相機的 uri 獲取裁剪時的 uri 獲取這裡可以放一下底部 popupwindow 的佈局,另外可以看我的 popupwindow封裝: 拒

相簿裁剪

1.activity_main.xml: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"     xmlns:tools="http://schemas.android

Android 頭像上傳 相機+相簿 繪製圓形頭像

老話常談,在開始之前來幾張圖,看看效果: 主介面: activity_main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://

Android與H5相機相簿筆記

公司專案中android內嵌H5頁面,H5頁面需要選擇上傳圖片功能。H5的標籤無法調起android照相機和相簿功能,所以只能android進行處理。本人第一次做,借鑑網上諸多的程式碼,總結如下:

HTML5呼叫手機攝像拍照相簿等功能樣式美化及demo

最近用MUI做了個移動端專案,設計中涉及到呼叫手機攝像頭拍照等功能需求,然而在PLUS環境下才能直接呼叫,這就讓人有點頭疼了,後經查詢資料學習瞭解到有一個很簡單的方法就是input:file標籤,不需要複雜程式碼操作就能實現呼叫拍照、相簿等功能。 //capture -- 設定選擇需

Android 日期選擇日期範圍選擇器:MaterialDateRangePicker

一個Android Material 日期範圍選擇器,基於wdullaers MaterialDateTimePicker。  Date Picker Time Picker 步入正題:日期範圍選擇器 MaterialDateRangePicker Update 

呼叫系統相機拍照選擇系統相簿-適配7.0以上系統

現在很多apk都有呼叫系統相機進行拍照和選擇系統相簿,GitHub和各大網站都有很多封裝的,這裡就不zb了! 直接上程式碼吧:拍照和選擇系統相簿都適配了7.0以上系統,程式碼都有註釋 “ private static final int TAKE_PHON