08豐富程序-調(diào)用攝像頭和相冊(cè)

調(diào)用攝像頭拍照

  1. 新建一個(gè)項(xiàng)目,修改activity中的代碼
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
    <Button
        android:id="@+id/take_photo"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Take Photo"
        />

    <ImageView
        android:id="@+id/picture"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        />
</LinearLayout>

  • 一個(gè)按鈕用于拍照,一個(gè)用于將拍到的圖片顯示出來
  1. 修改MainActivity中的代碼

public class MainActivity extends AppCompatActivity {

    public static final int TAKE_PHOTO = 1;
    private ImageView picture;
    private Uri imageUri;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button takePhoto = (Button)findViewById(R.id.take_photo);
        picture = (ImageView)findViewById(R.id.picture);
        takePhoto.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 創(chuàng)建一個(gè)File對(duì)象环揽,用于保存拍照后的照片
                File outputImage = new File(getExternalCacheDir(),"output_image.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.example.md.c1",outputImage);
                }else {
                    imageUri = Uri.fromFile(outputImage);
                }
                // 啟動(dòng)相機(jī)程序
                Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
                intent.putExtra(MediaStore.EXTRA_OUTPUT,imageUri);
                startActivityForResult(intent,TAKE_PHOTO);
            }
        });
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode){
            case TAKE_PHOTO:
                if (resultCode == RESULT_OK){
                    // 把拍攝的照片顯示出來
                    try {
                        Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));
                        picture.setImageBitmap(bitmap);
                    } catch (FileNotFoundException e) {
                        e.printStackTrace();
                    }
                }
                break;
            default:
                break;
        }
    }
}

  • 在Button的點(diǎn)擊邏輯中,首先創(chuàng)建一個(gè)FIle對(duì)象夏哭,用于存放攝像頭拍下的照片,圖片命名為output_image.jpg贸诚,并將它存放在手機(jī)的SD卡的應(yīng)用關(guān)聯(lián)緩存目錄中方庭,什么是應(yīng)用關(guān)聯(lián)緩存目錄呢?就是指在SD卡中專門用于存放當(dāng)前應(yīng)用緩存數(shù)據(jù)的位置酱固,調(diào)用getExternalCacheDir()方法就可以獲取到這個(gè)目錄,具體的路徑是/sdcard/Android/data/<package name>/cache头朱,那么為社么要使用這個(gè)關(guān)聯(lián)緩存目錄來存放圖片呢运悲,因?yàn)閺腁ndroid6.0開始,讀寫SD卡被列為危險(xiǎn)全鄉(xiāng)项钮,如果將圖片緩存在SD卡的其他目錄班眯,都是要進(jìn)行運(yùn)行時(shí)權(quán)限處理才行希停,而使用應(yīng)用關(guān)聯(lián)目錄就可以跳過這一步了
  • 接下來,判斷如果在android7.0版本下署隘,就調(diào)用Uri.fromFile()方法將一個(gè)FIle對(duì)象轉(zhuǎn)換為Uri對(duì)象宠能,這個(gè)Uri對(duì)象標(biāo)識(shí)這output_image.jpg這張圖片的真是路徑,否則就調(diào)用 FileProvider.getUriForFile()方法磁餐,將一個(gè)FIle對(duì)轉(zhuǎn)換為一個(gè)封裝過的Uri對(duì)象违崇,getUriForFile()方法接收3個(gè)參數(shù),第一個(gè)就是Context對(duì)象诊霹,第二個(gè)參數(shù)是任意的唯一的字符串羞延,第三個(gè)參數(shù)就是File對(duì)象,之所以這樣是因?yàn)閍ndroid7.0系統(tǒng)開始脾还,直接使用本地真實(shí)路徑的Uri被認(rèn)為是不安全的伴箩,會(huì)拋出異常,而FileProvider則是一種特殊的內(nèi)容提供器鄙漏,使用了它和內(nèi)容提供類似的機(jī)制來對(duì)數(shù)據(jù)進(jìn)行保護(hù)嗤谚,可以選擇將封裝過的Uri共享給外部,提高了應(yīng)用的安全性
  • 下面就是構(gòu)建一個(gè)Intent對(duì)象怔蚌,并將這個(gè)intent的action指定為android.media.action.IMAGE_CAPTURE呵恢,再調(diào)用Intent的putExtra()方法指定圖片的輸出地址,這里使用Uri對(duì)象媚创,最后使用 startActivityForResult()方法啟動(dòng)活動(dòng)渗钉,這是一個(gè)隱式的Intent,系統(tǒng)會(huì)找到能夠響應(yīng)這個(gè)Intent的活動(dòng)去啟動(dòng)钞钙,這樣照相機(jī)應(yīng)用就會(huì)被打開鳄橘,拍的照片就會(huì)輸出在output_image.jpg中
  • 用于使用的是startActivityForResult()方法啟動(dòng)的活動(dòng),那么拍照完的結(jié)果就會(huì)返回到onActivityResult()方法中芒炼,如果拍照成功就可以調(diào)用BitmapFactory.decodeStream()方法將output_image.jpg這張照片解析成Bitmap對(duì)象然后設(shè)置到ImageView中
  1. 由于我們使用到了內(nèi)容提供器瘫怜,需要在AndroidManifest.xml中進(jìn)行內(nèi)容提供器進(jìn)行注冊(cè)
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.md.c1">

    <application
    >
      ...
        <provider
            android:authorities="com.example.md.c1"
            android:name="android.support.v4.content.FileProvider"
            android:exported="false"
            android:grantUriPermissions="true"
            >
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths"
                />

        </provider>


    </application>

</manifest>

  • android:name是固定的, android:authorities屬性值必須和剛才FileProvider.getUriForFile()第二個(gè)參數(shù)一樣本刽,
  • meta-data標(biāo)簽用來指定Uri的共享路徑鲸湃,并引用了@xml/file_paths資源,這個(gè)創(chuàng)建一個(gè)
  1. 創(chuàng)建@xml/file_paths資源子寓,右擊res-->new-->Directory,創(chuàng)建一個(gè)目錄,接著右擊xml目錄-->new-->file創(chuàng)建一個(gè)file_paths.xml文件暗挑,修改代碼
<?xml version="1.0" encoding="utf-8"?>
<paths
    xmlns:android="http://schemas.android.com/apk/res/android" >
    <external-path
        name="my_images"
        path=""/>
</paths>
  • 這里的external-path就是用來指定Uri共享的,name屬性是可以隨便寫斜友,path屬性值表示共享的具體路徑炸裆,這個(gè)設(shè)置為空值就表示將整個(gè)SD卡進(jìn)行共享,當(dāng)然也可以共享存放圖片的路徑
  1. 還有一點(diǎn)就是在android4.4之前鲜屏,訪問SD卡應(yīng)用關(guān)聯(lián)目錄要成名權(quán)限烹看,之后的版本就不用了聲明了国拇,這里為了兼容,就在AndroidManifest.xml聲明了
 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

  1. 運(yùn)行程序點(diǎn)擊按鈕惯殊,出現(xiàn)了
    [圖片上傳失敗...(image-50c427-1522159274532)]


    最后的界面.png

從相冊(cè)中選取圖片

  1. 修改上面的代碼酱吝,在activity.xml頁面中
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

    <Button
        android:id="@+id/take_photo"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Take Photo"
        />

    <Button
        android:id="@+id/choose_from_album"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Choose From Album"/>
    
    <ImageView
        android:id="@+id/picture"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        />


</LinearLayout>


  • 這里新增一個(gè)按鈕,用于上傳圖片
  1. 修改點(diǎn)擊后的邏輯
public class MainActivity extends AppCompatActivity {

    public static final int TAKE_PHOTO = 1;
    private ImageView picture;
    private Uri imageUri;

    //
    public static final int CHOOSE_PHOTO = 2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button takePhoto = (Button)findViewById(R.id.take_photo);
        picture = (ImageView)findViewById(R.id.picture);
        takePhoto.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 創(chuàng)建一個(gè)File對(duì)象土思,用于保存拍照后的照片
                File outputImage = new File(getExternalCacheDir(),"output_image.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.example.md.c1",outputImage);
                }else {
                    imageUri = Uri.fromFile(outputImage);
                }
                // 啟動(dòng)相機(jī)程序
                Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
                intent.putExtra(MediaStore.EXTRA_OUTPUT,imageUri);
                startActivityForResult(intent,TAKE_PHOTO);
            }
        });


        //調(diào)用相冊(cè)
        Button chooseFromAlbum = (Button)findViewById(R.id.choose_from_album);
        chooseFromAlbum.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){
                    ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1);
                }else{
                    openAlbun();
                }
            }
        });
        
    }
    
    private void openAlbun(){
        Intent intent = new Intent("android.intent.action.GET_CONTENT");
        intent.setType("image/*");
        // 打開相冊(cè)
        startActivityForResult(intent,CHOOSE_PHOTO);
    }

    // 動(dòng)態(tài)申請(qǐng)權(quán)限
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        switch (requestCode){
            case 1:
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
                    openAlbun();
                }else {
                    Toast.makeText(this,"you denied the permission",Toast.LENGTH_SHORT).show();
                }
            break;
            default:
        }
    }


    // 處理照片完執(zhí)行的方法
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode){
            case TAKE_PHOTO:
                if (resultCode == RESULT_OK){
                    // 把拍攝的照片顯示出來
                    try {
                        Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));
                        picture.setImageBitmap(bitmap);
                    } catch (FileNotFoundException e) {
                        e.printStackTrace();
                    }
                }
                break;
            case CHOOSE_PHOTO:
                if (resultCode == RESULT_OK){
                    // 判斷手機(jī)系統(tǒng)的版本號(hào)
                    if (Build.VERSION.SDK_INT >=19){
                        // 4.4以上版本使用這個(gè)方法
                        handleImageOnKitKat(data);
                    }else {
                        // 4.4一下版本使用這個(gè)方法
                        handleImageBeforeKitKat(data);
                    }
                }

                break;
            default:
                break;
        }
    }

    // 4.4以上版本使用的方法
    @RequiresApi(api = Build.VERSION_CODES.KITKAT)
    private void handleImageOnKitKat(Intent data){
        String imagePath = null;
        Uri uri = data.getData();
        if(DocumentsContract.isDocumentUri(this,uri)){
            //如果是document類型的Uri务热,則通過document id處理
            String docId = DocumentsContract.getDocumentId(uri);
            if ("com.android.providers.media.documents".equals(uri.getAuthority())){
                String id = docId.split(":")[1];
                String selection = MediaStore.Images.Media._ID + "=" +id;
                imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,selection);
            }else if("com.android.providers.downloads.documents".equals(uri.getAuthority())){
                Uri contentUri = ContentUris.withAppendedId(Uri.parse("content:" +
                        "http://downloads/public_downloads"),Long.valueOf(docId));
                imagePath = getImagePath(contentUri,null);
            }
        }else if ("content".equalsIgnoreCase(uri.getScheme())){
            // 如果是content類型的Uri,則使用普通方式處理
            imagePath = getImagePath(uri,null);

        }else if("file".equalsIgnoreCase(uri.getScheme())){
            //如果file的類型是Uri浪漠,直接獲取圖片的路徑即可
            imagePath = uri.getPath();
        }

        // 根據(jù)圖片的路徑顯示圖片
        displayImage(imagePath);
    }

    // 4.4一下版本使用的方法
    private void handleImageBeforeKitKat(Intent data){
        Uri uri = data.getData();
        String imagePath = getImagePath(uri,null);
        displayImage(imagePath);
    }


    // 獲取照片的路徑
    private String getImagePath(Uri uri,String selection){
        String path = null;
        // 通過Uri和selection來獲取真實(shí)圖片路徑
        Cursor cursor = getContentResolver().query(uri,null,selection,null,null);
        if (cursor != null ){
            if (cursor.moveToFirst()){
                path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
            }
            cursor.close();
        }
        return path;
    }

    // 最后根據(jù)照片的路徑用于顯示圖片
    private void displayImage(String imagePath){
        if (imagePath != null){
            Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
            picture.setImageBitmap(bitmap);
        }else {
            Toast.makeText(this,"failed to get image",Toast.LENGTH_SHORT).show();
        }

    }

}


  • 首先點(diǎn)擊按鈕事件先進(jìn)行了一個(gè)運(yùn)行時(shí)權(quán)限處理陕习,動(dòng)態(tài)申請(qǐng)WRITE_EXTERNAL_STORAGE這個(gè)危險(xiǎn)權(quán)限,為什么申請(qǐng)這個(gè)權(quán)限址愿?因?yàn)橛辛诉@個(gè)權(quán)限才可以對(duì)SD卡的讀取和寫入的操作该镣,代碼和前面的差不多
  • 當(dāng)用戶授權(quán)申請(qǐng)之才會(huì)調(diào)用openAlbun()方法,先構(gòu)建一個(gè)Intent對(duì)象响谓,action指定為android.intent.action.GET_CONTENT,然后設(shè)置參數(shù)损合,調(diào)用startActivityForResult()就可以打開相冊(cè)程序選照片了,注意娘纷,在調(diào)用startActivityForResult()這個(gè)方法的時(shí)候嫁审,傳入的的第二個(gè)參數(shù),這樣當(dāng)從相冊(cè)選擇完照片后回到onActivityResult()時(shí)赖晶,就會(huì)進(jìn)入第二個(gè)參數(shù)的case來處理圖片
  • 接下來為了版本兼容問題寫了兩個(gè)方法律适,因?yàn)?.4版本之后,選取相冊(cè)中的圖片不再返回圖片真實(shí)的Uri遏插,而是封裝Uri捂贿,4.4以上的還得對(duì)Uri進(jìn)行解析
  • handleImageOnKitKat()方法中的邏輯就是如何解析這個(gè)Uri,如果返回的Uri是documnet類型的話胳嘲,那就取出document id進(jìn)行處理厂僧,如果不是的話,就用普通方式處理了牛,如果Uri是authortity是media格式的話颜屠,document還需要進(jìn)一步解析,要通過字符串分割的方式取出后半部分才能得到真正的數(shù)字id鹰祸,取出的id用于構(gòu)建Uri和條件語句甫窟,把這些參數(shù)傳入到getImagePath()方法中,就可以獲取到了圖片的真實(shí)路徑福荸,拿到圖片的路徑蕴坪,再調(diào)用displayImage()方法把圖片顯示
  • handleImageBeforeKitKat()這個(gè)就簡單了,因?yàn)樗腢ri沒有封裝敬锐,直接傳入到getImagePath()獲取到了真實(shí)路徑背传,最后也是調(diào)用displayImage()方法顯示圖片
  1. 運(yùn)行程序,點(diǎn)擊按鈕,首先彈出權(quán)限申請(qǐng)台夺,點(diǎn)擊同意


    打開相冊(cè).png

    最終效果.png

    當(dāng)然了径玖,這只是基本用法,當(dāng)某些圖片體積大的時(shí)候颤介,加載到內(nèi)存中梳星,程序會(huì)直接崩潰的,這個(gè)時(shí)候就要對(duì)用戶上傳的照片進(jìn)行壓縮滚朵,再加載到內(nèi)存中冤灾,至于代碼該如何實(shí)現(xiàn),慢慢研究

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末辕近,一起剝皮案震驚了整個(gè)濱河市韵吨,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌移宅,老刑警劉巖归粉,帶你破解...
    沈念sama閱讀 222,807評(píng)論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異漏峰,居然都是意外死亡糠悼,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,284評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門浅乔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來倔喂,“玉大人,你說我怎么就攤上這事靖苇∠” “怎么了?”我有些...
    開封第一講書人閱讀 169,589評(píng)論 0 363
  • 文/不壞的土叔 我叫張陵顾复,是天一觀的道長班挖。 經(jīng)常有香客問我,道長芯砸,這世上最難降的妖魔是什么萧芙? 我笑而不...
    開封第一講書人閱讀 60,188評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮假丧,結(jié)果婚禮上双揪,老公的妹妹穿的比我還像新娘。我一直安慰自己包帚,他們只是感情好渔期,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,185評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般疯趟。 火紅的嫁衣襯著肌膚如雪拘哨。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,785評(píng)論 1 314
  • 那天信峻,我揣著相機(jī)與錄音倦青,去河邊找鬼。 笑死盹舞,一個(gè)胖子當(dāng)著我的面吹牛产镐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播踢步,決...
    沈念sama閱讀 41,220評(píng)論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼癣亚,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了获印?” 一聲冷哼從身側(cè)響起述雾,我...
    開封第一講書人閱讀 40,167評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蓬豁,沒想到半個(gè)月后绰咽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,698評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡地粪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,767評(píng)論 3 343
  • 正文 我和宋清朗相戀三年取募,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蟆技。...
    茶點(diǎn)故事閱讀 40,912評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡玩敏,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出质礼,到底是詐尸還是另有隱情旺聚,我是刑警寧澤,帶...
    沈念sama閱讀 36,572評(píng)論 5 351
  • 正文 年R本政府宣布眶蕉,位于F島的核電站砰粹,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏造挽。R本人自食惡果不足惜碱璃,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,254評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望饭入。 院中可真熱鬧嵌器,春花似錦、人聲如沸谐丢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,746評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至讥珍,卻和暖如春历极,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背串述。 一陣腳步聲響...
    開封第一講書人閱讀 33,859評(píng)論 1 274
  • 我被黑心中介騙來泰國打工执解, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留寞肖,地道東北人纲酗。 一個(gè)月前我還...
    沈念sama閱讀 49,359評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像新蟆,于是被迫代替她去往敵國和親觅赊。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,922評(píng)論 2 361

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