先上结果:

图1


图2:

图3:

activity_main 布局代码:

<?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:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical"    tools:context=".MainActivity">    <Button        android:id="@+id/btn_photo"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_marginTop="25dp"        android:text="拍照"/>    <ImageView        android:id="@+id/img_icon"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_gravity="center_horizontal"/></LinearLayout>

MainActivity 代码

package ccv.turbosnail.photo_demo;import android.content.Intent;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.net.Uri;import android.os.Build;import android.provider.MediaStore;import android.support.annotation.Nullable;import android.support.v4.content.FileProvider;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.widget.Button;import android.widget.ImageView;import java.io.File;import java.io.FileNotFoundException;import java.io.IOException;import java.text.SimpleDateFormat;import java.util.Date;public class MainActivity extends AppCompatActivity {    public static final int TAKE_PHOTO =1;  //  TAKE_PHOTO来作为case处理图片的标识    private ImageView imgIcon;  //  显示拍照后的图片    private Button btnPhoto;    //  拍照    private Uri imageUri;   //  通用资源标志符    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        initView(); //初始化控件        btnPhoto.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                /**                 *      问:   首先为什么这里要创建时间对象?                 *      答:   因为,如果我们不用当前本地的时间作为图片的名字,当然这里图片的名字可以设为一个死值                 *      例如:Monday.jpg    —  那么对于这个死值作为图片的名字,当我们需要拍多张照片时,就会一直覆盖                 *      这张照片,也就是说,我们就无法实现多张照片的保存,之前的照片就会被新的照片覆盖掉。                 *      所以说,这里用本地的当前时间作为照片的名字,就解决了这个问题                 *      简单的来说就是,利用本地时间 — 解决图片名冲突问题                 */                SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日HH:mm:ss");                Date curDate = new Date(System.currentTimeMillis());                String str = format.format(curDate);                /**                 *          创建File对象,用于存储拍照后的照片                 *          第一个参数:  是这张照片存放在手机SD卡的对应关联缓存应用                 *          第二个参数:  这张图片的命名                 */                File outputImage = new File(getExternalCacheDir(),str+".jpg");                try {                    if (outputImage.exists()){          //  检查与File对象相连接的文件和目录是否存在于磁盘中                        outputImage.delete();           //  删除与File对象相连接的文件和目录                    }                    outputImage.createNewFile();        //  如果与File对象相连接的文件不存在,则创建一个空文件                } catch (IOException e) {                    e.printStackTrace();                }                if (Build.VERSION.SDK_INT >= 24){       //  如果运行设备的系统版本高于 Android7.0                    /**                     *          将File对象转换成一个封装过的Uri对象                     *          第一个参数:  要求传入Context参数                     *          第二个参数:  可以是任意唯一的字符串                     *          第三个参数:  我们刚刚创建的File对象                     */                    imageUri = FileProvider.getUriForFile(MainActivity.this,"ccv.turbosnail.photo_demo.fileprovider",outputImage);                }else{                  //  如果运行设备的系统版本低于 Android7.0                    //  将File对象转换成Uri对象,这个Uri对象表示着 str + ".jpg" 这张图片的本地真实路径                    imageUri = Uri.fromFile(outputImage);                }                /**                 *      启动相机程序                 */                //  将Intent的action指定为 拍照到指定目录 —— android.media.action.IMAGE_CAPTURE                Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");                //  指定图片的输出地址                intent.putExtra(MediaStore.EXTRA_OUTPUT,imageUri);                /**                 *      在通过startActivityForResult(),来启动活动,因此拍完照后会有结果返回到 onActivityResult()方法中                 */                startActivityForResult(intent,TAKE_PHOTO);            }        });    }    /**     *     * @param requestCode       第一个是请求码,可以进行传递数据前的一些操作,比如根据不同的请求码,设置不同的传递内容。     * @param resultCode        第二个是返回码,也就是在B中设置的int的数值,这个是得到返回的内容的标识。     * @param data              第三个是Intent的数据,比如在B中的setResult方法中传递了一些数据,在A中就可以通过解析Intent的内容来获得传递过来的数据。     */    @Override    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {        switch (requestCode){            case TAKE_PHOTO:                if (resultCode == RESULT_OK){       //  当拍照成功后,会返回一个返回码,这个值为 -1 — RESULT_OK                    try{                        //  根据Uri找到这张照片的资源位置,将它解析成Bitmap对象,然后将把它设置到imageView中显示出来                        Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));                        imgIcon.setImageBitmap(bitmap);                    } catch (FileNotFoundException e) {                        e.printStackTrace();                    }                }                break;        }    }    private void initView() {        btnPhoto = findViewById(R.id.btn_photo);        imgIcon = findViewById(R.id.img_icon);    }}

AndroidManifest 代码:

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="ccv.turbosnail.photo_demo">    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />    <application        android:allowBackup="true"        android:icon="@mipmap/ic_launcher"        android:label="@string/app_name"        android:roundIcon="@mipmap/ic_launcher_round"        android:supportsRtl="true"        android:theme="@style/AppTheme">        <activity android:name=".MainActivity">            <intent-filter>                <action android:name="android.intent.action.MAIN" />                <category android:name="android.intent.category.LAUNCHER" />            </intent-filter>        </activity>        <provider            android:name="android.support.v4.content.FileProvider"            android:authorities="ccv.turbosnail.photo_demo.fileprovider"            android:exported="false"            android:grantUriPermissions="true">            <meta-data                android:name="android.support.FILE_PROVIDER_PATHS"                android:resource="@xml/file_paths" />        </provider>    </application></manifest>

接着:

最后,仔细看下注释,代码在上面,但是我还是希望大家能认真理解一下它们的意思

点击运行即可!

总结:
相机的逻辑还是比较简单的,我想仔细阅读过注释的你一定不会觉得有什么困难,但我还是要总结一下我认为比较重要的三点:

  1. 应用缓存目录:
    自从 Android6.0 系统开始,读写SD卡被列为了危险权限,如果将图片存放在SD卡的任何其他目录,都要进行运行时权限处理才行,而使用应用关联缓存目录可以跳过这一步 ,那么我们直接在创建File对象时,第一个参数填入 getExternalCacheDir()方法就可以得到这个目录

  2. 内容提供器的注册:
    自从 Android7.0系统开始,直接使用本地真实路径被认为是不安全的,会抛出一个FileUriExPosedException 异常,而FileProvider 是一种特殊的内容提供其,它使用了内容提供器类似的机制来对数据进行保护,可以选择性地将封装过的Uri 共享给外部,从而提高了应用的安全性。

  3. 版本问题:
    如果运行设备的系统版本低于 Android7.0,就调用Uri的 fromFile()方法将File 对象转换成Uri对象,这个对象标识着拍照的图片的本地真实路径。如果低于7.0版本,就调用FileProvider的getUriForFile() 方法将File对象转换成一个封装过的Uri 对象。

    最后感谢你们的收看,如果可以的话,给我一个赞和评论证明你们曾经来过,我会很欣慰的,你们支持这样用心的一位作者吗?

更多相关文章

  1. 类和 Json对象
  2. Android(安卓)matrix 控制图片的旋转、缩放、移动
  3. android拍照与读取相册
  4. Android(安卓)报错:Caused by: android.os.FileUriExposedExcepti
  5. Android中文API(144) —— JsonWriter
  6. Android之Handler用法总结
  7. android通过ksoap2对webservice的解析
  8. android解决坚屏拍照和保存图片旋转90度的问题,并兼容4.0
  9. Android(安卓)View的介绍和使用

随机推荐

  1. Android(安卓)生成keystore,两种方式 【包
  2. 系出名门Android(4) - 活动(Activity),
  3. android XMl 解析神奇xstream 三: 把复杂
  4. 如何扩展Android富文本之Html标签
  5. 如何将svn上checkout下来的android 程序
  6. 【OSC手机App技术解析】- Android完全退
  7. android应用程序的安装位置
  8. Android(安卓)IPC 通讯机制源码分析
  9. Android(安卓)SDCard Filesystem
  10. android selector 背景选择器