ছবি তোলা

দ্রষ্টব্য: এই পৃষ্ঠাটি ক্যামেরা শ্রেণীকে বোঝায়, যা অপ্রচলিত। আমরা CameraX বা, নির্দিষ্ট ব্যবহারের ক্ষেত্রে, Camera2 ব্যবহার করার পরামর্শ দিই। CameraX এবং Camera2 উভয়ই Android 5.0 (API স্তর 21) এবং উচ্চতর সমর্থন করে।

এই পাঠটি ডিভাইসের অন্য ক্যামেরা অ্যাপে কাজটি অর্পণ করে কীভাবে একটি ফটো ক্যাপচার করতে হয় তা শেখায়৷ (যদি আপনি নিজের ক্যামেরা কার্যকারিতা তৈরি করতে চান, ক্যামেরা নিয়ন্ত্রণ দেখুন।)

ধরুন আপনি একটি ক্রাউড-সোর্সড ওয়েদার সার্ভিস বাস্তবায়ন করছেন যা আপনার ক্লায়েন্ট অ্যাপ চালিত ডিভাইসগুলির দ্বারা তোলা আকাশের ছবিগুলিকে একত্রিত করে বিশ্বব্যাপী আবহাওয়ার মানচিত্র তৈরি করে। ফটো একত্রিত করা আপনার অ্যাপ্লিকেশনের একটি ছোট অংশ। আপনি ন্যূনতম ঝগড়ার সাথে ফটো তুলতে চান, ক্যামেরাটিকে নতুন করে আবিষ্কার করবেন না। আনন্দের বিষয়, বেশিরভাগ অ্যান্ড্রয়েড-চালিত ডিভাইসে ইতিমধ্যে অন্তত একটি ক্যামেরা অ্যাপ্লিকেশন ইনস্টল করা আছে। এই পাঠে, আপনি শিখবেন কিভাবে এটি আপনার জন্য একটি ছবি তুলতে হয়।

ক্যামেরা বৈশিষ্ট্য অনুরোধ

যদি আপনার অ্যাপ্লিকেশনের একটি অপরিহার্য ফাংশন ছবি তোলা হয়, তাহলে ক্যামেরা আছে এমন ডিভাইসগুলিতে Google Play-তে এর দৃশ্যমানতা সীমাবদ্ধ করুন। বিজ্ঞাপন দিতে যে আপনার অ্যাপ্লিকেশনটি একটি ক্যামেরা থাকার উপর নির্ভর করে, আপনার ম্যানিফেস্ট ফাইলে একটি <uses-feature> ট্যাগ রাখুন:

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

যদি আপনার অ্যাপ্লিকেশন ব্যবহার করে, কিন্তু কাজ করার জন্য ক্যামেরার প্রয়োজন না হয়, তাহলে পরিবর্তে android:required to false সেট করুন। এটি করার মাধ্যমে, Google Play ক্যামেরা ছাড়া ডিভাইসগুলিকে আপনার অ্যাপ্লিকেশন ডাউনলোড করার অনুমতি দেবে৷ তারপর hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY) কল করে রানটাইমে ক্যামেরার উপলব্ধতা পরীক্ষা করা আপনার দায়িত্ব৷ একটি ক্যামেরা উপলব্ধ না হলে, আপনি তারপর আপনার ক্যামেরা বৈশিষ্ট্য নিষ্ক্রিয় করা উচিত.

থাম্বনেইল পান

যদি একটি ফটো তোলার সহজ কৃতিত্ব আপনার অ্যাপের উচ্চাকাঙ্ক্ষার চূড়ান্ত না হয়, তাহলে আপনি সম্ভবত ক্যামেরা অ্যাপ্লিকেশন থেকে ছবিটি ফিরিয়ে আনতে চান এবং এটির সাথে কিছু করতে চান।

অ্যান্ড্রয়েড ক্যামেরা অ্যাপ্লিকেশানটি কী "data" অধীনে, অতিরিক্তগুলিতে একটি ছোট Bitmap হিসাবে onActivityResult() এ বিতরণ করা রিটার্ন Intent ফটোটিকে এনকোড করে। নিম্নলিখিত কোডটি এই চিত্রটি পুনরুদ্ধার করে এবং এটি একটি ImageView এ প্রদর্শন করে।

কোটলিন

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
        val imageBitmap = data.extras.get("data") as Bitmap
        imageView.setImageBitmap(imageBitmap)
    }
}

জাভা

@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);
    }
}

দ্রষ্টব্য: "data" থেকে এই থাম্বনেইল চিত্রটি একটি আইকনের জন্য ভাল হতে পারে, তবে আরও বেশি কিছু নয়৷ একটি পূর্ণ আকারের চিত্রের সাথে মোকাবিলা করতে একটু বেশি কাজ লাগে।

পূর্ণ আকারের ছবি সংরক্ষণ করুন

অ্যান্ড্রয়েড ক্যামেরা অ্যাপ্লিকেশনটি একটি পূর্ণ আকারের ফটো সংরক্ষণ করে যদি আপনি এটিকে সংরক্ষণ করার জন্য একটি ফাইল দেন৷ আপনাকে অবশ্যই একটি সম্পূর্ণ যোগ্য ফাইলের নাম প্রদান করতে হবে যেখানে ক্যামেরা অ্যাপের ফটো সংরক্ষণ করা উচিত।

সাধারণত, ব্যবহারকারী ডিভাইসের ক্যামেরা দিয়ে ধারণ করে এমন যেকোনো ছবি ডিভাইসে পাবলিক এক্সটার্নাল স্টোরেজে সেভ করা উচিত যাতে সেগুলি সব অ্যাপের মাধ্যমে অ্যাক্সেস করা যায়। DIRECTORY_PICTURES যুক্তি সহ, ভাগ করা ফটোগুলির জন্য সঠিক ডিরেক্টরি getExternalStoragePublicDirectory() দ্বারা সরবরাহ করা হয়েছে৷ এই পদ্ধতি দ্বারা প্রদত্ত ডিরেক্টরিটি সমস্ত অ্যাপের মধ্যে ভাগ করা হয়। অ্যান্ড্রয়েড 9 (এপিআই স্তর 28) এবং নীচে, এই ডিরেক্টরিতে পড়া এবং লেখার জন্য যথাক্রমে READ_EXTERNAL_STORAGE এবং WRITE_EXTERNAL_STORAGE অনুমতি প্রয়োজন:

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

Android 10 (API লেভেল 29) এবং উচ্চতর, ফটো শেয়ার করার জন্য সঠিক ডিরেক্টরি হল MediaStore.Images টেবিল। আপনার কোনো স্টোরেজ অনুমতি ঘোষণা করতে হবে না, যতক্ষণ না আপনার অ্যাপের শুধুমাত্র সেই ফটোগুলি অ্যাক্সেস করতে হবে যা ব্যবহারকারী আপনার অ্যাপ ব্যবহার করে নিয়েছেন।

যাইহোক, আপনি যদি চান যে ফটোগুলি শুধুমাত্র আপনার অ্যাপে ব্যক্তিগত থাকুক, আপনি পরিবর্তে Context.getExternalFilesDir() দ্বারা প্রদত্ত ডিরেক্টরিটি ব্যবহার করতে পারেন। অ্যান্ড্রয়েড 4.3 এবং তার নিচের সংস্করণে, এই ডিরেক্টরিতে লেখার জন্যও WRITE_EXTERNAL_STORAGE অনুমতি প্রয়োজন৷ অ্যান্ড্রয়েড 4.4 থেকে শুরু করে, অনুমতির আর প্রয়োজন নেই কারণ ডিরেক্টরিটি অন্যান্য অ্যাপের দ্বারা অ্যাক্সেসযোগ্য নয়, তাই আপনি ঘোষণা করতে পারেন যে অনুমতিটি শুধুমাত্র Android এর নিম্ন সংস্করণগুলিতে maxSdkVersion বৈশিষ্ট্য যোগ করে অনুরোধ করা উচিত:

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

দ্রষ্টব্য: আপনি getExternalFilesDir() বা getFilesDir() দ্বারা প্রদত্ত ডিরেক্টরিগুলিতে সংরক্ষণ করেন এমন ফাইলগুলি মুছে ফেলা হয় যখন ব্যবহারকারী আপনার অ্যাপ আনইনস্টল করে।

একবার আপনি ফাইলের জন্য ডিরেক্টরি নির্ধারণ করার পরে, আপনাকে একটি সংঘর্ষ-প্রতিরোধী ফাইলের নাম তৈরি করতে হবে। আপনি পরবর্তী ব্যবহারের জন্য সদস্য ভেরিয়েবলে পাথ সংরক্ষণ করতেও ইচ্ছুক হতে পারেন। এখানে একটি পদ্ধতির একটি উদাহরণ সমাধান রয়েছে যা একটি তারিখ-সময় স্ট্যাম্প ব্যবহার করে একটি নতুন ছবির জন্য একটি অনন্য ফাইলের নাম প্রদান করে৷ (এই উদাহরণটি অনুমান করে যে আপনি একটি Context থেকে পদ্ধতিটি কল করছেন।)

কোটলিন

lateinit var currentPhotoPath: String

@Throws(IOException::class)
private fun createImageFile(): File {
    // Create an image file name
    val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
    val storageDir: File = getExternalFilesDir(Environment.DIRECTORY_PICTURES)
    return File.createTempFile(
            "JPEG_${timeStamp}_", /* prefix */
            ".jpg", /* suffix */
            storageDir /* directory */
    ).apply {
        // Save a file: path for use with ACTION_VIEW intents
        currentPhotoPath = absolutePath
    }
}

জাভা

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 তৈরি করতে এবং আহ্বান করতে পারেন:

কোটলিন

private fun dispatchTakePictureIntent() {
    Intent(MediaStore.ACTION_IMAGE_CAPTURE).also { takePictureIntent ->
        // Ensure that there's a camera activity to handle the intent
        takePictureIntent.resolveActivity(packageManager)?.also {
            // Create the File where the photo should go
            val photoFile: File? = try {
                createImageFile()
            } catch (ex: IOException) {
                // Error occurred while creating the File
                ...
                null
            }
            // Continue only if the File was successfully created
            photoFile?.also {
                val photoURI: Uri = FileProvider.getUriForFile(
                        this,
                        "com.example.android.fileprovider",
                        it
                )
                takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI)
                startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE)
            }
        }
    }
}

জাভা

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_IMAGE_CAPTURE);
        }
    }
}

দ্রষ্টব্য: আমরা getUriForFile(Context, String, File) ব্যবহার করছি যা একটি content:// URI প্রদান করে। Android 7.0 (API লেভেল 24) এবং উচ্চতর টার্গেট করা আরও সাম্প্রতিক অ্যাপগুলির জন্য, প্যাকেজের সীমানা জুড়ে একটি file:// URI পাস করলে একটি FileUriExposedException হয়৷ অতএব, আমরা এখন একটি FileProvider ব্যবহার করে ছবি সংরক্ষণের একটি আরও সাধারণ উপায় উপস্থাপন করছি।

এখন, আপনাকে FileProvider কনফিগার করতে হবে। আপনার অ্যাপের ম্যানিফেস্টে, আপনার অ্যাপ্লিকেশনে একটি প্রদানকারী যোগ করুন:

<application>
   ...
   <provider
        android:name="androidx.core.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>

নিশ্চিত করুন যে কর্তৃপক্ষের স্ট্রিংটি getUriForFile(Context, String, File) এর সাথে দ্বিতীয় আর্গুমেন্টের সাথে মেলে। প্রদানকারীর সংজ্ঞার মেটা-ডেটা বিভাগে, আপনি দেখতে পারেন যে প্রদানকারী একটি উত্সর্গীকৃত সম্পদ ফাইলে যোগ্য পাথগুলি কনফিগার করা আশা করে, 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="Pictures" />
</paths>

পাথ উপাদানটি সেই পথের সাথে মিলে যায় যা getExternalFilesDir() দ্বারা ফেরত আসে যখন Environment.DIRECTORY_PICTURES সাথে কল করা হয়।DIRECTORY_PICTURES। নিশ্চিত করুন যে আপনি আপনার অ্যাপের প্রকৃত প্যাকেজ নামের সাথে com.example.package.name প্রতিস্থাপন করেছেন। এছাড়াও, external-path ছাড়াও আপনি ব্যবহার করতে পারেন এমন পাথ স্পেসিফায়ারগুলির একটি বিস্তৃত বিবরণের জন্য FileProvider ডকুমেন্টেশন চেকআউট করুন।

একটি গ্যালারী ফটো যোগ করুন

আপনি যখন একটি অভিপ্রায়ের মাধ্যমে একটি ফটো তৈরি করেন, তখন আপনার ছবিটি কোথায় অবস্থিত তা আপনার জানা উচিত, কারণ আপনি প্রথমে এটিকে কোথায় সংরক্ষণ করবেন তা বলেছিলেন৷ অন্য সবার জন্য, সম্ভবত আপনার ফটো অ্যাক্সেসযোগ্য করার সবচেয়ে সহজ উপায় হল সিস্টেমের মিডিয়া প্রদানকারী থেকে এটি অ্যাক্সেসযোগ্য করা৷

দ্রষ্টব্য: যদি আপনি getExternalFilesDir() দ্বারা প্রদত্ত ডিরেক্টরিতে আপনার ফটো সংরক্ষণ করেন, তাহলে মিডিয়া স্ক্যানার ফাইলগুলি অ্যাক্সেস করতে পারবে না কারণ সেগুলি আপনার অ্যাপের ব্যক্তিগত।

নিম্নলিখিত উদাহরণ পদ্ধতিটি দেখায় যে কীভাবে আপনার ছবি মিডিয়া প্রদানকারীর ডাটাবেসে যুক্ত করতে সিস্টেমের মিডিয়া স্ক্যানারকে আহ্বান করতে হয়, এটিকে Android গ্যালারি অ্যাপ্লিকেশনে এবং অন্যান্য অ্যাপে উপলব্ধ করে।

কোটলিন

private fun galleryAddPic() {
    Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE).also { mediaScanIntent ->
        val f = File(currentPhotoPath)
        mediaScanIntent.data = Uri.fromFile(f)
        sendBroadcast(mediaScanIntent)
    }
}

জাভা

private void galleryAddPic() {
    Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
    File f = new File(currentPhotoPath);
    Uri contentUri = Uri.fromFile(f);
    mediaScanIntent.setData(contentUri);
    this.sendBroadcast(mediaScanIntent);
}

একটি স্কেল করা ছবি ডিকোড করুন

সীমিত মেমরির সাথে একাধিক পূর্ণ আকারের ছবি পরিচালনা করা কঠিন হতে পারে। আপনি যদি দেখেন যে শুধুমাত্র কয়েকটি চিত্র প্রদর্শন করার পরে আপনার অ্যাপ্লিকেশনটি মেমরির ফুরিয়ে যাচ্ছে, আপনি নাটকীয়ভাবে JPEG-কে একটি মেমরি অ্যারেতে প্রসারিত করে ব্যবহার করা গতিশীল হিপের পরিমাণ হ্রাস করতে পারেন যা ইতিমধ্যেই গন্তব্য দৃশ্যের আকারের সাথে মেলে। নিম্নলিখিত উদাহরণ পদ্ধতি এই কৌশল প্রদর্শন করে.

কোটলিন

private fun setPic() {
    // Get the dimensions of the View
    val targetW: Int = imageView.width
    val targetH: Int = imageView.height

    val bmOptions = BitmapFactory.Options().apply {
        // Get the dimensions of the bitmap
        inJustDecodeBounds = true

        BitmapFactory.decodeFile(currentPhotoPath, bmOptions)

        val photoW: Int = outWidth
        val photoH: Int = outHeight

        // Determine how much to scale down the image
        val scaleFactor: Int = Math.max(1, Math.min(photoW / targetW, photoH / targetH))

        // Decode the image file into a Bitmap sized to fill the View
        inJustDecodeBounds = false
        inSampleSize = scaleFactor
        inPurgeable = true
    }
    BitmapFactory.decodeFile(currentPhotoPath, bmOptions)?.also { bitmap ->
        imageView.setImageBitmap(bitmap)
    }
}

জাভা

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

    // Get the dimensions of the bitmap
    BitmapFactory.Options bmOptions = new BitmapFactory.Options();
    bmOptions.inJustDecodeBounds = true;

    BitmapFactory.decodeFile(currentPhotoPath, bmOptions);

    int photoW = bmOptions.outWidth;
    int photoH = bmOptions.outHeight;

    // Determine how much to scale down the image
    int scaleFactor = Math.max(1, 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(currentPhotoPath, bmOptions);
    imageView.setImageBitmap(bitmap);
}