Android拍照、照片選擇以及圖片裁剪完全解析

Android中頭像選擇宛琅,圖片上傳等功能幾乎是每一個APP必備的功能刻蟹,那么關于怎么使用相機,如何進行照片選擇嘿辟,以及選擇后的圖片裁剪舆瘪,這一系列的問題都需要逐一解決。這也是本篇文章的主要內(nèi)容红伦。

一英古、應用場景


微信朋友圈上傳圖片,頭像上傳等功能昙读,經(jīng)常就會用到以上功能召调。

二、業(yè)務邏輯

主要分為兩種業(yè)務邏輯:拍照蛮浑,選擇圖片唠叛。

拍照邏輯:

1.A 界面,點擊按鈕調用相機拍照沮稚;
2.拍照界面拍照后艺沼,點擊確認得到拍完照片,跳轉到 B 界面進行預覽蕴掏;
3.B 界面進行圖片裁剪障般,裁剪后確認,返回A界面進行圖片回顯盛杰;

選擇圖片邏輯:

1.A界面挽荡,點擊按鈕調用相冊選擇圖片;
2.相冊界面選擇圖片后饶唤,跳轉到B界面進行預覽徐伐;
3.B 界面進行圖片裁剪,裁剪后確認募狂,返回A界面進行圖片回顯办素;


從上面可以清楚地看出,兩種方式的主要區(qū)別在第一步上面祸穷,一種是選擇調用相機性穿,另一種選擇是調用相冊。

下面我們來介紹具體代碼邏輯雷滚。

三需曾、拍照具體實現(xiàn)

以如下使用場景為例:
<img src="http://upload-images.jianshu.io/upload_images/3985563-b82a260a49b89082.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" width = "250" height = "450" align=center />
頭像上傳的使用。

1.調用相機

Android 程序上實現(xiàn)拍照功能的方式分為兩種:第一種是利用相機的 API 來自定義相機,第二種是利用 Intent 調用系統(tǒng)指定的相機拍照呆万。下面講的內(nèi)容都是針對第二種實現(xiàn)方式的應用商源。

簡單使用

Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
Uri fileUri = Uri.fromFile(mPhotoFile);
captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
startActivityForResult(captureIntent, CAPTURE_PHOTO_REQUEST_CODE);

很簡單,通過上述四行代碼就實現(xiàn)了調用系統(tǒng)相機谋减。

加入MediaStore.EXTRA_OUTPUT牡彻,使得拍照后的圖片輸出到對應路徑下。

然而由于Android手機的碎片化出爹,我們之前調用系統(tǒng)指定的相機app來拍照庄吼,有些手機可能會沒有這個app,所以在使用之前要檢查是否有系統(tǒng)相機严就。

/**
     * 判斷系統(tǒng)中是否存在可以啟動的相機應用
     *
     * @return 存在返回true总寻,不存在返回false
     */
    public boolean hasCamera() {
        PackageManager packageManager = mActivity.getPackageManager();
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        List<ResolveInfo> list = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
        return list.size() > 0;
    }

2.照片預覽及圖片裁剪

由于之前使用startActivityForResult方式調用系統(tǒng)相機,那么在拍照完成后梢为,會返回到上述頁面渐行,這時候需要重寫onActivityResult方法,根據(jù)之前傳入的mPhotoFile路徑抖誉,就獲取了圖片所在地殊轴,因為拍照后的圖片就存在該路徑下。

然后我們只需要在開啟一個照片預覽Activity袒炉,進行后續(xù)裁剪就可以了。

因為這里我們調用系統(tǒng)裁剪樊零,所以就不設置預覽Activity我磁,直接跳轉到裁剪頁面就可以了。

  //拍照完成后 獲取目標文件 跳轉到裁剪頁面
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == CapturePhotoHelper.CAPTURE_PHOTO_REQUEST_CODE) {
            //獲取拍照后圖片路徑
            File photoFile = mCapturePhotoHelper.getPhoto();
            if (photoFile != null) {
                if (resultCode == RESULT_OK) {
                    Uri uri = Uri.fromFile(photoFile);
                    Intent intent = new Intent("com.android.camera.action.CROP");
                    intent.setDataAndType(uri, "image/*");
                    //intent.putExtra("crop", "true");
                    intent.putExtra("aspectX", 1);
                    intent.putExtra("aspectY", 1);
                    intent.putExtra("outputX", 300);
                    intent.putExtra("outputY", 300);
                    intent.putExtra("scale", true);
                    intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
                    intent.putExtra("return-data", false);
                    intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
                    intent.putExtra("noFaceDetection", true); // no face detection
                    intent = Intent.createChooser(intent, "裁剪圖片");
                    startActivityForResult(intent, REQUEST_PICKER_AND_CROP);
                } else {
                    if (photoFile.exists()) {
                        photoFile.delete();
                    }
                }
            }

        } else {
            super.onActivityResult(requestCode, resultCode, data);
        }
    }

其中要注意幾個 extra 字段:


注意:return-data: 設為 true 的時候驻襟,在 onActivityResult() 中可以直接通過 data.getParcelableExtra("data") 得到裁剪后的 Bitmap 對象夺艰。但是當 Bitmap 過大時,就不能使用這種方法了沉衣,容易出現(xiàn)OOM現(xiàn)象郁副,需要通過獲取文件,然后先縮放豌习,再加載存谎。

如下圖所示:


3.回顯圖片

如上如所示,修剪圖片完成后肥隆,點擊確定既荚,然后就可以編寫回顯邏輯。

同樣在onActivityReuslt方法中

if(requestCode ==REQUEST_PICKER_AND_CROP){
            File photoFile = mCapturePhotoHelper.getPhoto();
            //存放到相冊
            BitmapUtils.displayToGallery(this, photoFile);
            //更新UI 顯示圖像
            InformationBean informationBean = mList.get(0);
            informationBean.setContent(photoFile.getAbsoluteFile().toString());
            informationBean.setSet(true);
            mAdapter.notifyItemChanged(0);

        }

上述采用了RecyclerView,具體更新頭像邏輯在Adapter中栋艳。

不過同樣由于Android碎片化問題恰聘,圖片會產(chǎn)生各種各樣的問題,比如:拍出來的照片“歪了”,拍完照怎么閃退了晴叨,圖片無法顯示凿宾。

以上這些問題都比較坑,不過不要緊兼蕊,已經(jīng)有前人為我們趟出一條血路初厚,全部都處理好了,詳細代碼及具體實現(xiàn)參考:你需要知道的Android拍照適配方案遍略。

這里我們只負責應用就好了:

                String content = bean.getContent();
                final File file = new File(content);
                ((SpecialViewHolder) holder).mImageView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                    @Override
                    public void onGlobalLayout() {
                        ((SpecialViewHolder) holder).mImageView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                        mWidth = ((SpecialViewHolder) holder).mImageView.getMeasuredWidth();
                        mHeight = ((SpecialViewHolder) holder).mImageView.getMeasuredHeight();
                        Bitmap bitmap = BitmapUtils.decodeBitmapFromFile(file, mWidth, mHeight);
                        if (bitmap != null) {
                            //檢查是否有被旋轉惧所,并進行糾正
                            System.out.println("文件所占空間:"+"file.getTotalSpace()");
                            int degree = BitmapUtils.getBitmapDegree(file.getAbsolutePath());
                            if (degree != 0) {
                                bitmap = BitmapUtils.rotateBitmapByDegree(bitmap, degree);
                            }
                            ((SpecialViewHolder) holder).mImageView.setImageBitmap(bitmap);
                        }

                    }
                });

先獲取控件的寬高,進行壓縮绪杏,避免圖片無法顯示的問題下愈。

然后檢查有沒有被旋轉,如果旋轉蕾久,那么通過矩陣矯正势似,避免照片"歪了"的問題。

至于拍完照片后閃退僧著,可以通過重寫onSaveInstanceState 和 onRestoreInstanceState 來實現(xiàn)履因。

回顯結果:

四、選擇圖片具體實現(xiàn)

由于和上述方式只是第一步有區(qū)別盹愚,我們就具體看看第一步的實現(xiàn)栅迄。

 Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
        intent.putExtra(MediaStore.EXTRA_OUTPUT,uri);
        startActivityForResult(intent, REQUEST_PICK_IMAGE);

直接開啟系統(tǒng)圖片選擇應用即可,不用額外設置MediaStore.EXTRA_OUTPUT皆怕,因為圖片已經(jīng)保存在數(shù)據(jù)庫內(nèi)了毅舆,可直接獲取,如下所示愈腾。

然后在onActivityResult中憋活,獲取圖片存儲路徑,跳轉到裁剪頁面虱黄。

if (requestCode == REQUEST_PICK_IMAGE) {
            //獲取選擇圖片后圖片路徑

            if (resultCode == RESULT_OK) {
                Uri uri =  data.getData();
                Intent intent = new Intent("com.android.camera.action.CROP");
                intent.setDataAndType(uri, "image/*");
                intent.putExtra("aspectX", 1);
                intent.putExtra("aspectY", 1);
                intent.putExtra("outputX", 200);
                intent.putExtra("outputY", 200);
                intent.putExtra("scale", true);
                intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(tempFile));
                intent.putExtra("return-data", false);
                intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
                intent.putExtra("noFaceDetection", true); // no face detection
                intent = Intent.createChooser(intent, "裁剪圖片");
                startActivityForResult(intent, REQUEST_PICKER_AND_CROP_2);
            }
        }

后面回顯步驟和上述一致悦即。

五、總結

上述介紹了拍照橱乱,照片選擇以及圖片剪裁的使用辜梳,由于使用的是系統(tǒng)自帶的應用,所以可能出現(xiàn)一些意想不到的適配問題仅醇,還有待解決冗美。另外,由于是系統(tǒng)自帶析二,功能比較單一粉洼,且無法使用個性化节预,如有這方面的需求,可以使用一些流行的第三方庫属韧,Github上有很多優(yōu)秀的實現(xiàn)安拟。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市宵喂,隨后出現(xiàn)的幾起案子糠赦,更是在濱河造成了極大的恐慌,老刑警劉巖锅棕,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拙泽,死亡現(xiàn)場離奇詭異,居然都是意外死亡裸燎,警方通過查閱死者的電腦和手機顾瞻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來德绿,“玉大人荷荤,你說我怎么就攤上這事∫莆龋” “怎么了蕴纳?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長个粱。 經(jīng)常有香客問我古毛,道長,這世上最難降的妖魔是什么都许? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任喇潘,我火速辦了婚禮,結果婚禮上梭稚,老公的妹妹穿的比我還像新娘。我一直安慰自己絮吵,他們只是感情好弧烤,可當我...
    茶點故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蹬敲,像睡著了一般暇昂。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上伴嗡,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天急波,我揣著相機與錄音,去河邊找鬼瘪校。 笑死澄暮,一個胖子當著我的面吹牛名段,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播泣懊,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼伸辟,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了馍刮?” 一聲冷哼從身側響起信夫,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎卡啰,沒想到半個月后静稻,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡匈辱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年振湾,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片梅誓。...
    茶點故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡恰梢,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出梗掰,到底是詐尸還是另有隱情嵌言,我是刑警寧澤,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布及穗,位于F島的核電站摧茴,受9級特大地震影響,放射性物質發(fā)生泄漏埂陆。R本人自食惡果不足惜苛白,卻給世界環(huán)境...
    茶點故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望焚虱。 院中可真熱鬧购裙,春花似錦、人聲如沸鹃栽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽民鼓。三九已至薇芝,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間丰嘉,已是汗流浹背夯到。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留饮亏,地道東北人耍贾。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓阅爽,卻偏偏與公主長得像,于是被迫代替她去往敵國和親逼争。 傳聞我的和親對象是個殘疾皇子优床,可洞房花燭夜當晚...
    茶點故事閱讀 44,864評論 2 354

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,116評論 25 707
  • 發(fā)現(xiàn) 關注 消息 iOS 第三方庫、插件誓焦、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 12,103評論 4 62
  • 朦朧的快要看不見時胆敞,才知道最近的遠方是故鄉(xiāng)。
    夏末MOMO閱讀 131評論 0 0
  • 我們每個人都是互相影響的杂伟,比如:孩子會受父母影響移层,老公會受老婆影響,員工會所領導影響赫粥,學生會受老師影響观话。除了家人我...
    營養(yǎng)私教西西閱讀 442評論 0 1
  • 知曉真相的我 從那一刻起频蛔,才發(fā)現(xiàn) 我對于你,僅僅是利益 如今秦叛,為了那一點小小的利益 你毫不保留地將我舍棄 可能不是...
    梓曄漫漫閱讀 213評論 0 2