پایگاه دانش مایکت

View Categories

آموزش کار با دوربین در برنامه‌نویسی اندروید

در این نوشته می‌خواهیم نحوه گرفتن عکس و ویدیو را با استفاده از برنامه‌های دوربین موجود در موبایل توضیح دهیم. فرض کنیم برنامه‌ای داریم که در آن کاربر می‌تواند عکس نمایه داشته باشد یا بتواند ویدیو ضبط کند، این کار یکی از ویژگی‌های کوچک برنامه شماست، پس لازم نیست که برنامه دوربین را دوباره از اول نوشته تا بتوانید با آن کار کنید. در حال حاضر اکثر دستگاه‌های اندرویدی حداقل یک برنامه دوربین را در خود دارند.

گرفتن عکس در برنامه

برای گرفتن عکس ابتدا بهتر است که که در Manifest برنامه بگوییم که نیاز به استفاده از دوربین داریم، برای این کار با استفاده تگ <uses-feature> به صورت زیر انجام می‌دهیم:

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

صفت android:required مشخص کننده این است که آیا برنامه برای انجام عملیات‌های خود نیازمند دوربین هست یا خیر. در صورتی که در زمان اجرا نیاز به چک کردن این ویژگی داریم می‌توانید از hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY) استفاده کنیم. تا در صورتی که دستگاه دوربین نداشت ویژگی مورد استفاده را غیر فعال کنید.

گرفتن عکس با برنامه دوربین گوشی

ابزار اندروید برای اینکه کارهای مختلف را به دیگر برنامه‌ها بسپارد استفاده از Intent است. عملیات گرفتن عکس شامل سه مرحله است، ساختن Intent، صدا زدن Intent برای اجرای Activity برنامه دوربین، مدیریت تصویر گرفته شده در هنگام برگشت به برنامه.

در اینجا Intent مورد نظر را برای گرفتن عکس به صورت زیر است:

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() که اولین Activityی را که شرایط اجرای Intent را دارد برمی‌گرداند، انجام می‌شود.

عکس بند انگشتی (thumbnail)

به احتمال زیاد برنامه شما پس از گرفتن عکس نیاز دارد که عملیاتی روی عکس گرفته شده انجام دهد. برای این کار اندروید در onActivityResult() یک Bitmap کوچک به برنامه شما برمی‌گرداند تا بتوانید عملیات مورد نظر را روی آن انجام دهید. کد زیر نمونه‌ی این عملیات است:

@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");
        imageView.setImageBitmap(imageBitmap);
    }
}
نکته: تصویر بندانگشتی بخاطر کوچک بودن مناسب برای آیکون است نه برای نمایش با ابعاد بزرگ.

ذخیره عکس گرفته شده

دوربین اندروید قابلیت ذخیره تصویر در سایز اصلی را دارد؛ برای این کار نیاز است یک آدرس فایل به آن معرفی کنید.

معمولا هر عکسی که کاربر با استفاده از دوربین دستگاه می‌گیرد در پوشه عمومی فضای حافظه خارجی ذخیره شود تا همه برنامه‌ها به آن دسترسی داشته باشند. پوشه مناسب برای تصاویر اشتراکی توسط getExternalStoragePublicDirectory() با پارمتر ورودی DIRECTORY_PICTURES فراهم می‌شود. بخاطر اینکه پوشه‌ای که توسط این متد فراهم می‌شود بین تمامی برنامه‌ها در اشتراک است، خواندن و نوشتن در آن نیازمند دسترسی‌های READ_EXTERNAL_STORAGE و WRITE_EXTERNAL_STORAGE می‌باشد. دسترسی نوشتن به طور ضمنی دسترسی خواندن را نیز در خود دارد، بنابراین اگه نیاز دارید که در حافظه بنویسید، تنها نیاز است که دسترسی زیر را اضافه کنید:

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

اما، اگر می‌خواد تصاویر مخصوص برنامه شما باشد، می‌توانید از پوشه‌ای که متد getExternalFilesDir() فراهم می‌کند استفاده کنید. از اندروید ۴.۳ به قبل، نوشتن در این پوشه نیازمند دسترسی WRITE_EXTERNAL_STORAGE است. با شروع از اندروید ۴.۴ این دسترسی دیگر نیاز نیست، زیرا این پوشه برای دیگر برنامه‌ها قابل دسترس نیست، پس می‌توانید این دسترسی را به صورت زیر تعریف کنید:

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

نکته: فایل‌هایی که با استفاده از getExternalFilesDir() یا getFilesDir() ذخیره می‌شوند پس از حذف برنامه، حذف می‌شوند.

پس از انتخاب پوشه فایل مورد نظر باید یک نام تداخل‌ناپذیر برای فایل انتخاب کنید. این نام رو ممکن است در مکانی ذخیره کنید تا بعدا از آن استفاده کنید. به عنوان مثال می‌توانید با استفاده از زمان نام فایل خود را تولید نمایید:

String currentPhotoPath;

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
    currentPhotoPath = image.getAbsolutePath();
    return image;
}

بعد از اینکه فایل را برای تصویر تولید کردید، زمان آن فرارسیده‌است که آن را به Intent بدهید:

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) استفاده می‌کنیم که یک content:// Uri برمی‌گرداند. برای بیشتر برنامه‌هایی که اندروید ۷.۰ به بالا را هدف قرار می‌دهند، پاس دادن file:// Uri بیرون از محدوده پکیج موجب FileUriExposedException می‌شود. بنابراین راه فعلی که مورد استفاده قرار می‌گیرد یا همان FileProvider را استفاده می‌کنیم.

برای راه‌اندازی FileProvider لازم است در manifest این تکه کد را اضافه کنیم:

<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"></meta-data>
    </provider>
    ...
</application>

اطمینان حاصل کنید که فیلد authorities با پارامتر دوم در getUriForFIle(Context, String, File) مطابقت داشته باشد. همانطور که در قسمت meta-data در تعریف provider مشاهده می‌کنید، provider نیاز دارد تا مسیرهای مشخص فایل را بداند. این مسیرها را می‌توان در فایلی مشابه res/xml/file_paths.xml قرار داد:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-files-path name="my_images" path="Android/data/com.example.package.name/files/Pictures" />
</paths>

جزء path با مسیری که توسط getExternalFilesDir() با پارمتر Enviroment.DIRECTORY_PICTURE برگردانده می‌شود مطابقت دارد. اطمینان حاصل کنید که com.example.package.name را به PackageName برنامه خود جابجا می‌کنید.

ضبط ویدیو در برنامه

برای ضبط ویدیو ابتدا بهتر است که که در Manifest برنامه بگوییم که نیاز به استفاده از دوربین داریم، برای این کار با استفاده تگ <uses-feature> به صورت زیر انجام می‌دهیم

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

صفت android:required مشخص کننده این است که آیا برنامه برای انجام عملیات‌های خود نیازمند دوربین هست یا خیر. در صورتی که در زمان اجرا نیاز به چک کردن این ویژگی داریم می‌توانید از hasSystemFeature(PackageManager.FEATURE_CAMERA) استفاده کنیم. تا در صورتی که دستگاه دوربین نداشت ویژگی مورد استفاده را غیر فعال کنید.

ضبط ویدیو با استفاده از برنامه دوربین گوشی

ابزار اندروید برای اینکه کارهای مختلف را به دیگر برنامه‌ها بسپارد استفاده از Intent است. عملیات ضبط ویدیو شامل سه مرحله است، ساختن Intent، صدا زدن Intent برای اجرای Activity برنامه دوربین، و مدیریت ویدیوی ضبط‌شده در هنگام برگشت به برنامه.

در اینجا Intent مورد نظر را برای ضبط ویدیو به صورت زیر است:

static final int REQUEST_VIDEO_CAPTURE = 1;

private void dispatchTakeVideoIntent() {
    Intent takeVideoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
    if (takeVideoIntent.resolveActivity(getPackageManager()) != null) {
        startActivityForResult(takeVideoIntent, REQUEST_VIDEO_CAPTURE);
    }
}

لازم به ذکر است که فراخوانی startActivityForResult() حتما باید بررسی شود که آیا دستگاه قادر به انجام ضبط ویدیو هست یا خیر. این کار با استفاده از resolveActivity() که اولین Activityی را که شرایط اجرای Intent را دارد برمی‌گرداند، انجام می‌شود.

نمایش ویدئو

برنامه دوربین اندروید ویدیو را با استفاده از Intent در onActivityResult() به صورت یک Uri که به محل ذخیره ویدیو اشاره دارد، ویدیو را به ما بازمی‌گرداند. کد زیر ویدیو را در یک VideoView نمایش می‌دهد:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
    if (requestCode == REQUEST_VIDEO_CAPTURE && resultCode == RESULT_OK) {
        Uri videoUri = intent.getData();
        videoView.setVideoURI(videoUri);
    }
}