简单说说Android中如何使用摄像头和相册

很多 APP 应用都有用户头像功能,用户既可以调用摄像头马上拍一张美美的自拍,也可以打开相册选取一张心仪的照片作为头像。

1 调用摄像头

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/activity_main"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical">

  <!--拍照按钮-->
  <Button android:id="@+id/open_photo"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="拍照"
    />

  <!--照片展示-->
  <ImageView
    android:id="@+id/img"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"
    />

</LinearLayout>

活动类代码:

public class MainActivity extends AppCompatActivity {

  private static final String TAG = "MainActivity";

  public static final int OPEN_PHOTO_REQUEST_CODE = 1;
  private Uri imgUrl = null;
  private ImageView imageView;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    //为按钮添加【打开摄像头】事件
    findViewById(R.id.open_photo).setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        File file = new File(getExternalCacheDir(), "imageView.jpg");//存储照片
        if (file.exists()) {
          file.delete();
        }
        try {
          file.createNewFile();
        } catch (IOException e) {
          e.printStackTrace();
        }

        if (Build.VERSION.SDK_INT >= 24) {//版本高于 7.0
          imgUrl = FileProvider.getUriForFile(MainActivity.this, "net.deniro.camera.fileProvider", file);
        } else {
          imgUrl = Uri.fromFile(file);
        }

        //打开摄像头
        Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
        intent.putExtra(MediaStore.EXTRA_OUTPUT, imgUrl);//指定图片输出地址
        startActivityForResult(intent, OPEN_PHOTO_REQUEST_CODE);
      }
    });

    //显示拍摄的照片
    imageView = (ImageView) findViewById(R.id.img);

  }

  @Override
  protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    Log.d(TAG, "requestCode: " + requestCode);
    Log.d(TAG, "imgUrl: " + imgUrl);
    switch (requestCode) {
      case OPEN_PHOTO_REQUEST_CODE:
        if (resultCode == RESULT_OK) {
          try {//解析图片并显示
            Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imgUrl));
            imageView.setImageBitmap(bitmap);
          } catch (FileNotFoundException e) {
            e.printStackTrace();
          }
        }
        break;
      default:
        break;
    }
  }
}

getExternalCacheDir() 可以获取 SD 卡中专门用于存放当前应用缓存数据的位置。Android6.0+ 开始,读取存放在 SD 卡中的任何其它目录都被列为危险权限,因此需要设定运行时权限才可以操作,这里使用了与应用关联的目录,所以就可以跳过这一步。

getUriForFile() 方法接收三个参数:Context对象、任意唯一的字符串与 File对象。从 android 7.0+ 系统开始,直接使用本地真实的路径被认为是不安全的,会抛出一个 FileExposedException 异常,而 FileProvider 是一种特殊的内容提供器,它使用与内容提供器类似的机制对数据进行保护。

在 AndroidManifest.xml 中注册刚才定义的内容提供器:

<provider
  android:name="android.support.v4.content.FileProvider"
  android:authorities="net.deniro.camera.fileProvider"
  android:exported="false"
  android:grantUriPermissions="true">
  <!--指定 Uri 的共享路径-->
  <meta-data
    android:name="android.support.FILE_PROVIDER_PATHS"
    android:resource="@xml/file_paths" />
</provider>

android:authorities 就是我们在 FileProvider.getUriForFile() 方法中传入的第二个参数。

使用 <meta-data> 指定了 Uri 的共享路径,在此引用了 xml 资源。

在 IDEA 中可以通过快捷键 ctrl + enter 直接在 xml 文件夹下创建文件:

快捷创建

默认为 xml 文件夹

file_paths.xml:

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
  <!--指定共享的 Uri -->
  <!--name:名称(任意)-->
  <!--path:共享的路径,空值表示共享整个 SD 卡-->
  <external-path name="img" path=""/>
</PreferenceScreen>

Android 4.4 之前,还需要设置访问 SD 卡应用的关联目录权限:

 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

拍照后效果:

2 从相册中选取照片

直接从相册中选取一张现有的照片比打开摄像头拍一张照片更加常用,因此,一个好的 app,应该将这两种方式都实现。

修改布局文件,加入【打开相册】按钮:

<!--打开相册选取照片-->
<Button
  android:id="@+id/choose_photo"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:text="打开相册"
  />

在活动类中加入打开相册选取照片的处理逻辑:

/**
 * 打开相册请求码
 */
public static final int CHOOSE_PHOTO_REQUEST_CODE = 2;
...
@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  ...
  //打开相册
  findViewById(R.id.choose_photo).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
      if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
            CHOOSE_PHOTO_REQUEST_CODE);
      } else {
        openAlbum();
      }
    }
});
/**
 * 打开相册
 */
private void openAlbum() {
  Intent intent = new Intent("android.intent.action.GET_CONTENT");
  intent.setType("image/*");
  startActivityForResult(intent, CHOOSE_PHOTO_REQUEST_CODE);
}

/**
 * 请求 WRITE_EXTERNAL_STORAGE 权限
 *
 * 相册中的照片一般是存放在 SD 卡上的,所以从 SD 卡中读取照片需要申请权限
 *
 * WRITE_EXTERNAL_STORAGE 表示读写 SD 卡的能力权限
 * @param requestCode
 * @param permissions
 * @param grantResults
 */
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
  switch (requestCode) {
    case CHOOSE_PHOTO_REQUEST_CODE:
      if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
        openAlbum();
      } else {
        Toast.makeText(this, "被拒绝啦", Toast.LENGTH_SHORT).show();
      }
      break;
    default:
      break;
  }
  super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  Log.d(TAG, "requestCode: " + requestCode);
  Log.d(TAG, "imgUrl: " + imgUrl);
  switch (requestCode) {
    case OPEN_PHOTO_REQUEST_CODE:
      if (resultCode == RESULT_OK) {
        try {//解析图片并显示
          Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imgUrl));
          imageView.setImageBitmap(bitmap);
        } catch (FileNotFoundException e) {
          e.printStackTrace();
        }
      }
      break;
    case CHOOSE_PHOTO_REQUEST_CODE:
      if (Build.VERSION.SDK_INT >= 19) {//4.4 及以上系统
        handleFor4_4(data);
      } else {
        handleForBefore4_4(data);
      }
      break;
    default:
      break;
  }
}

/**
 * 处理相片
 * android 4.4-
 *
 * @param data
 */
private void handleForBefore4_4(Intent data) {
  display(getImagePath(data.getData(),null));
}

/**
 * 处理相片
 * android 4.4+
 *
 * @param data
 */
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
private void handleFor4_4(Intent data) {
  String imagePath = null;
  Uri uri = data.getData();//需要解析
  if (DocumentsContract.isDocumentUri(this, uri)) {//是 Document 类型,使用 document id 来处理
    String documentId = DocumentsContract.getDocumentId(uri);
    String mediaDocumentAuthority = "com.android.providers.media.documents";
    String downloadDocumentAuthority = "com.android.providers.downloads.documents";
    if (mediaDocumentAuthority.equals(uri.getAuthority())) {//media 格式
      String id = documentId.split(":")[1];//数字格式的 ID
      String selection = MediaStore.Images.Media._ID + "=" + id;
      imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection);
    } else if (downloadDocumentAuthority.equals(uri.getAuthority())) {
      Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(documentId));
      imagePath = getImagePath(contentUri, null);
    }
  } else if ("content".equalsIgnoreCase(uri.getScheme())) {//content 类型 uri
    imagePath = getImagePath(uri, null);
  } else if ("file".equalsIgnoreCase(uri.getScheme())) {//file 类型,则直接获取路径
    imagePath = uri.getPath();
  }
  display(imagePath);
}

/**
 * 显示
 * @param path
 */
private void display(String path) {
  if(path==null){
    Toast.makeText(this, "无法获取图片", Toast.LENGTH_SHORT).show();
  }else{
    imageView.setImageBitmap(BitmapFactory.decodeFile(path));
  }

}

/**
 * 获取图片路径
 *
 * @param uri
 * @param selection
 * @return
 */
private String getImagePath(Uri uri, String selection) {
  String path = null;
  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;
}

这里请求了 WRITE_EXTERNAL_STORAGE 权限,以为相册中的照片一般是存放在 SD 卡上的,所以从 SD 卡中读取照片需要申请权限。WRITE_EXTERNAL_STORAGE 表示读写 SD 卡的能力权限。

为了兼容新老版本的手机(以 Android 4.4 为分水岭),因为 Android 4.4+ 的版本返回的 Uri 需要解析才可以使用。

点击【打开相册】按钮,会弹出读取 SD 卡的权限申请:

选取照片后的效果:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android实现调用摄像头和相册的方法

    Android调用摄像头是很方便的.先看一下界面 布局文件activity_main.xml源码 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:l

  • 简单说说Android中如何使用摄像头和相册

    很多 APP 应用都有用户头像功能,用户既可以调用摄像头马上拍一张美美的自拍,也可以打开相册选取一张心仪的照片作为头像. 1 调用摄像头 布局文件: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/activit

  • 简单介绍Android中Activity的四种启动模式

    在Android中每个界面都是一个Activity,切换界面操作其实是多个不同Activity之间的实例化操作.在Android中Activity的启动模式决定了Activity的启动运行方式. Activity有四种启动模式: 1. standard,默认的启动模式,只要激活Activity,就会创建一个新的实例,并放入任务栈中,这样任务栈中可能同时有一个Activity的多个实例. 2. singleTop,激活Activity时,如果栈顶是这个Activity,就不会创建新的实例:如果栈顶

  • android中打开相机、打开相册进行图片的获取示例

    这里介绍在Android中实现相机调取.拍照片.获取照片.存储新路径等已经打开相册.选择照片等功能 首先看一下界面,很简单 配置读取内存卡和调用照相头的功能 <!-- 使用网络权限 --> <uses-permission android:name="android.permission.INTERNET"/> <!-- 写sd卡的权限 --> <uses-permission android:name="android.permis

  • 简单谈谈Android中SP与DP的区别

    从一开始写Android程序,就被告知这些常识 一.dp(或者dip device independent pixels) 一种基于屏幕密度的抽象单位.在每英寸160点的显示器上,1dp=1px.不同设备有不同的显示效果,这个和设备硬件有关. 二.sp(Scaled Pixels) 主要用于字体显示,与刻度无关的一种像素,与dp类似,但是可以根据用户的字体大小首选项进行缩放. 简单的说 长度宽度的数值要使用dp作为单位放入dimens.xml文件中 字体大小的数值要使用sp作为单位,也放入dim

  • Android中Progress的简单实例

    Android中Progress的简单实例 Android中Progress网上的demo都是瞎扯淡,当然,你们也可以认为我的demo是瞎扯淡,因为,毕竟要理解别人的思路,很头疼,主要还是知道思路,然后一步一步慢慢来.今天我讲的是Progress的实现,如果看了我之前的博客,应该多少有些了解.话不多说,来看实例: xml东西不多,自己定义把,因为Progress包括了一级读取和二级读取,所以主要是根据按钮来实现,下面就是一个点击事件: public void onClick(View v) {

  • 简单介绍Android开发中的Activity控件的基本概念

    Activity是最基本的模块,一般称之为"活动",在应用程序中,一个Activity通常就是一个单独的屏幕.简单理解,Activity代表一个用户所能看到的屏幕,主要用于处理应用程序的整体性工作,例如监听系统事件,为用户显示指定的View,启动其他Activity等.所有应用的Activity都继承于android.app.Activity类,该类是Android提供的基层类,其他的Activity继承该父类后,通过父类的方法来实现各种功能. Activity 生命周期图如下: 在a

  • 简单讲解Android开发中触摸和点击事件的相关编程方法

    在Android上,不止一个途径来侦听用户和应用程序之间交互的事件.对于用户界面里的事件,侦听方法就是从与用户交互的特定视图对象截获这些事件.视图类提供了相应的手段. 在各种用来组建布局的视图类里面,你可能会注意到一些公共的回调方法看起来对用户界面事件有用.这些方法在该对象的相关动作发生时被Android框架调用.比如,当一个视图(如一个按钮)被触摸时,该对象上的onTouchEvent()方法会被调用.不过,为了侦听这个事件,你必须扩展这个类并重写该方法.很明显,扩展每个你想使用的视图对象(只

  • Android中引用其他程序的文本资源超简单方法

    在Android中引用其他程序的文本资源并不是很常见,但是有时候还是很是有需要的,通常引用的多半是系统的程序的文本资源. 下面以一个超简单的例子,来展示以下如何实现. 复制代码 代码如下: public void testUseAndroidString() { Context context = getContext();     Resources res = null;     try {         //I want to use the clear_activities strin

  • Android中SharedPreference详解及简单实例

     Android中SharedPreference详解 SharedPreference是Android提供的一种轻量级的数据存储方式,主要用来存储一些简单的配置信息,例如,默认欢迎语,登录用户名和密码等.其以键值对的方式存储,使得我们能很方便进行读取和存入. SharedPreference 文件保存在/data/data/<package name>/shared_prefs 路径下(如/data/data/com.android.alarmclock/shared_prefs/com.a

  • Android 中Seekbar详解及简单实例

    Android 中Seekbar详解及简单实例 做到音频播放和音乐播放时,大多数都要用到Seekbar.现在我先简单介绍下Seekbar的几个重要属性. android:max 设置值的大小 . android:thumb="@drawable/" 显示的那个可拖动图标,如果没有设置该参数则为系统默认,如果自己需要重新定义,则将自己需要的图标存放在资源目录 /res/drawable下,然后调用即可. android:thumbOffset 拖动图标的偏量值,可以让拖动图标超过bar的

随机推荐