Take photos

这节课教我们如何通过将工作委托给设备上的另一个摄像头应用来拍摄一张照片。(如果你想构建自己的相机功能,请参见控制相机。)

如果整合照片只是你应用的小部分时,你想要最低成本拍照,而不是重新实现一个拍照功能。令人高兴的是,大多数android设备已经安装了至少一个摄像头应用程序。在这节课中,你要学习如何让它为你拍照。

请求相机功能


如果拍照是你应用程序的一个基本功能,那么将其在谷歌应用市场上的可见性限制是,有摄像头的设备。为了宣传您的应用程序依赖于有一个摄像头,在您的清单文件中放置一个< uss -feature>标记:

<manifest ...>

<uses-feature android:name="android.hardware.camera"android:required="true"/>

</manifest >


如果您的应用程序使用,但不需要摄像头来运行,则设置android:required="false"。通过这样做,谷歌Play将允许没有摄像头的设备下载应用程序。然后,您有责任通过调用hasSystemFeature(packagemanagemanager . feature_camera)检查相机在运行时的可用性。如果没有摄像头,你应该禁用你的摄像头功能。


用相机应用拍照


Android中将操作委托给其他应用程序的方式是调用一个意图(意图:用来描述您想要做的事情)。这个过程包括三个部分:意图本身、启动外部活动的调用,以及当焦点返回到活动时处理图像数据的代码。

这是一个调用捕获照片意图的函数。

static final int REQUEST_IMAGE_CAPTURE = 1;

private void dispatchTakePictureIntent() {Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);if (takePictureIntent.resolveActivity(getPackageManager()) != null) {

startActivityForResult

(takePictureIntent, REQUEST_IMAGE_CAPTURE);}}

请注意,startActivityForResult()方法受到调用resolveActivity()条件的保护(条件限制保护),该条件返回能够处理意图的第一个活动组件。执行这个检查是很重要的,因为如果您调用startActivityForResult()使用了任何应用程序都无法处理的意图,您的应用程序将崩溃。所以只要结果不是空的,使用意图是安全的。


获取缩略图


如果拍照这个简单的壮举并不是你的应用野心的顶点,那么你可能想要从相机应用中取回照片,然后用它做点什么。

Android Camera应用程序将照片编码为onActivityResult()返回的意图的Extra信息中的一个小位图,对应键为“data”。下面的代码检索此映像并将其显示在ImageView中。

@Override

protected void onActivityResult(int requestCode, int resultCode, Intent data) {

    if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {

        Bundle extras = data.getExtras();

        Bitmap imageBitmap = (Bitmap) extras.get("data");

        mImageView.setImageBitmap(imageBitmap);

    }

}

注意:这个来自“data”的缩略图当做一个图标icon可能ok,但不能再多要求了。处理一个完整的图像需要更多的工作。


保存全尺寸照片 


keywords:full-size

如果你给它一个文件来保存,Android Camera应用程序会保存全屏照片。你必须提供一个完全合格的文件名称,以便相机应用程序保存照片。

通常,用户用设备摄像头拍摄的任何照片都应该保存在设备的公共外部存储中,以便所有应用程序都能访问这些照片。共享照片的适当目录由getExternalStoragePublicDirectory()提供,并带有DIRECTORY_PICTURES参数。由于此方法提供的目录在所有应用程序之间共享,因此对其进行读写需要分别具有READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE权限。写入权限隐式地允许读取,所以如果您需要写入外部存储,那么您只需要请求一个权限:

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

但是,如果希望照片只对应用程序私有,可以使用getExternalFilesDir()提供的目录。在Android 4.3和更低的版本中,写入这个目录也需要WRITE_EXTERNAL_STORAGE权限。从Android 4.4开始,不再需要权限,因为其他应用程序无法访问该目录,所以您可以通过添加maxSdkVersion属性,只在Android的较低版本上请求权限:

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

注意:在用户卸载应用程序时,将删除保存在getExternalFilesDir()或getFilesDir()所提供目录中的文件。

确定文件的目录后,需要创建一个抗冲突的文件名。您可能还希望将路径保存到成员变量中以供以后使用。这里有一个方法的示例解决方案,该方法使用日期-时间戳为新照片返回唯一的文件名:

String mCurrentPhotoPath;

private File createImageFile() throws IOException {

    // Create an image file name

    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());

    String imageFileName = "JPEG_" + timeStamp + "_";

    File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);

    File image = File.createTempFile(

        imageFileName,  /* prefix */

        ".jpg",        /* suffix */

        storageDir      /* directory */

    );

    // Save a file: path for use with ACTION_VIEW intents

    mCurrentPhotoPath = image.getAbsolutePath();

    return image;

}

使用此方法为照片创建文件,您现在可以创建和调用这样的意图:

static final int REQUEST_TAKE_PHOTO = 1;

private void dispatchTakePictureIntent() {

    Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

    // Ensure that there's a camera activity to handle the intent

    if (takePictureIntent.resolveActivity(getPackageManager()) != null) {

        // Create the File where the photo should go

        File photoFile = null;

        try {

            photoFile = createImageFile();

        } catch (IOException ex) {

            // Error occurred while creating the File

            ...

        }

        // Continue only if the File was successfully created

        if (photoFile != null) {

            Uri photoURI = FileProvider.getUriForFile(this,

                                                  "com.example.android.fileprovider",

                                                  photoFile);

            takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);

            startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO);

        }

    }

}

注意:getUriForFile(context ,string,file)会返回内容:// URI,我们使用 getUriForFile时会导致异常。对于最近针对Android 7.0 (API级别24)和更高的应用程序,跨包边界传递文件:// / URI会导致FileUriExposedException。因此,我们现在提供了一种使用FileProvider存储图像的更通用的方法。

现在,您需要配置FileProvider。在您的应用程序清单中,向应用程序添加一个提供者:

<application>

<provider

        android:name="android.support.v4.content.FileProvider"

        android:authorities="com.example.android.fileprovider"

        android:exported="false"

        android:grantUriPermissions="true">

<meta-data

            android:name="android.support.FILE_PROVIDER_PATHS"

            android:resource="@xml/file_paths"/>

</provider>

</application>


自定义file_paths指定可访问文件

路径组件对应于getExternalFilesDir()在调用Environment.DIRECTORY_PICTURES时返回的路径。确保您将com.example.package.name替换为应用程序的实际包名。此外,还要检查FileProvider的文档,以获得除external-path之外可以使用的路径说明符的详细描述。


将照片添加到一个画廊


当您通过一个意图创建一个照片时,您应该知道您的图像位于何处,因为您首先指定了在何处保存它。对于其他人来说,可能让你的照片可访问的最简单的方法是从系统的媒体提供商(相册)那里访问它。

注意:如果您将照片保存到getExternalFilesDir()提供的目录中,媒体扫描器无法访问这些文件,因为它们是应用程序的私有文件。

下面的示例方法演示了如何调用系统的媒体扫描器将照片添加到媒体提供者的数据库中,使其在Android Gallery应用程序和其他应用程序中可用。

private void galleryAddPic() {

    Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);

    File f = new File(mCurrentPhotoPath);

    Uri contentUri = Uri.fromFile(f);

    mediaScanIntent.setData(contentUri);

    this.sendBroadcast(mediaScanIntent);

}

解码一个按比例缩小的图片


在内存有限的情况下,管理多个全尺寸图像是很困难的。如果您发现您的应用程序在仅仅显示了几个映像之后就耗尽了内存,那么可以通过将JPEG扩展到一个内存数组中,使其与目标视图的大小相匹配,从而大大减少使用的动态堆的数量。下面的示例方法演示了这种技术。

private void setPic() {// Get the dimensions of the Viewint targetW = mImageView.getWidth();int targetH = mImageView.getHeight();

// Get the dimensions of the bitmapBitmapFactory.Options bmOptions = new BitmapFactory.Options();

bmOptions

.inJustDecodeBounds = true;BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);int photoW = bmOptions.outWidth;int photoH = bmOptions.outHeight;

// Determine how much to scale down the imageint scaleFactor = Math.min(photoW/targetW, photoH/targetH);

// Decode the image file into a Bitmap sized to fill the View

bmOptions

.inJustDecodeBounds = false;

bmOptions

.inSampleSize = scaleFactor;

bmOptions

.inPurgeable = true;

Bitmap bitmap = BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);

mImageView

.setImageBitmap(bitmap);}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,163评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,301评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,089评论 0 352
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,093评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,110评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,079评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,005评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,840评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,278评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,497评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,394评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,980评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,628评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,649评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,548评论 2 352

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,060评论 25 707
  • afinalAfinal是一个android的ioc,orm框架 https://github.com/yangf...
    wgl0419阅读 6,277评论 1 9
  • mean to add the formatted="false" attribute?.[ 46% 47325/...
    ProZoom阅读 2,695评论 0 3
  • 一 陌沄终是回来了南方,带着有些沧桑的脸…… 那天是2014年的正月初四,一同约定去了高中的学校。见到陌沄的那刻,...
    大写的鱼阅读 197评论 0 0
  • print 'Hello world!'
    SensorFlow阅读 128评论 0 0