22 April 2013

การใช้งานกล้องเพื่อถ่ายภาพแบบง่ายๆด้วย Intent

Updated on


        ถึงแม้ว่าบทความนี้จะเป็นเรื่องกล้อง แต่ก็ยังไม่พ้นเรื่อง Intent อยู่ดี เพราะว่าคราวนี้จะเป็นการใช้งานกล้องจากแอพอื่นนั่นเอง เหมาะกับแอพที่ไม่ได้ใช้งานกล้องอะไรมากนัก ต้องการแค่ภาพถ่ายเพื่อที่จะได้ไม่ต้องจัดการอะไรเกี่ยวกับโค๊ดของกล้องให้มากนัก

        สำหรับการใช้งานกล้องด้วย Intent นั้น จะเป็นการใช้งานกล้อง โดยให้เปิดใช้งานกล้องด้วยแอปฯกล้องตัวไหนก็ได้ที่มีในเครื่อง เมื่อกดถ่ายภาพแล้วแอพนั้นๆก็จะส่งข้อมูลภาพกลับมาที่แอพ จากนั้นก็สามารถนำข้อมูลภาพไปใช้งานได้เลย ง่ายใช่มั้ยล่ะ!!

        เดิมทีนั้นการใช้ Intent ไปยัง Activity ใหม่จะใช้คำสั่ง startActivity แต่ในกรณีที่ Activity ปลายทางนั้นๆ มีการส่งค่ากลับมาให้ด้วย จะต้องใช้เป็น startActivityForResult เพื่อให้มีการรับค่าที่ส่งกลับ โดยค่าที่ส่งกลับมาก็จะถูกส่งมาที่ฟังก์ชัน onActivityResult

        สำหรับคำสั่งใช้งานกล้องด้วย Intent ก็จะใช้คำสั่งด้วยกันดังนี้

int REQUEST_CAMERA = 0;
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
String timeStamp = 
        new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "IMG_" + timeStamp + ".jpg";
File f = new File(Environment.getExternalStorageDirectory()
        , "DCIM/Camera/" + imageFileName);
uri = Uri.fromFile(f);
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);        
startActivityForResult(Intent.createChooser(intent
        , "Take a picture with"), REQUEST_CAMERA);

        การประกาศและกำหนดค่าให้กับตัวแปรของคลาส Intent จะมีการระบุโดยตรงเลยว่าเป็นการ Intent เพื่อใช้งานกล้อง

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

        ตัวแปร timeSramp เป็นตัวแปรสำหรับดึงวันที่และเวลา ณ ตอนนั้น เพื่อนำไปใช้บันทึกเป็นชื่อไฟล์ภาพถ่าย (ภาพถ่ายนิยมบันทึกตามเวลา) โดยชื่อไฟล์จะเก็บไว้ในตัวแปรที่ชื่อ imageFileName มีนามสกุลเป็น jpg

String timeStamp = 
        new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "IMG_" + timeStamp + ".jpg";

        ต่อมาเป็นการนำที่อยู่ของไฟล์ที่จะบันทึกมากำหนดค่าให้เป็น Uri

        โดยที่ Uri จะส่งไปพร้อมกับ Intent เพื่อให้แอพกล้องเซฟไฟล์ตามที่อยู่นั้นๆ

File f = new File(Environment.getExternalStorageDirectory()
        , "DCIM/Camera/" + imageFileName);
uri = Uri.fromFile(f);

        จากนั้นก็นำ Uri ที่ได้ไปกำหนดค่าให้กับ Intent แล้วใช้คำสั่ง startActivityForResult

intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
startActivityForResult(Intent.createChooser(intent
        , "Take a picture with"), 0);

        ให้สังเกตุตรงค่าที่กำหนดให้กับคำสั่ง startActivityResult จะมีอันนีงที่กำหนดเป็น 0 ซึ่งค่าตรงนั้นจะเรียกว่า Request Code ซึ่งกำหนดเป็นเลขอะไรก็ได้ เป็น Integer 

        สมมติว่ากำหนดเป็น 0 ไป แล้วพอถ่ายภาพเสร็จ กลับมาที่ onActivityResult ก็จะมีการส่งค่า Request Code นั้นๆกลับมา หรือก็คือส่งค่า 0 กลับมานั่นแหละ เพื่อเอาไว้เช็คว่าเวลาที่ onActivityResult ทำงาน จะได้รู้ว่ามาจากแอพใด 

        เพราะในการใช้งานจริง อาจจะมีการ Intent กับแอพภายนอกมากกว่าหนึ่งแอพ ดังนั้นในการใช้งานแต่ละแอพจะต้องมีการส่งค่า Request Code ที่ต่างกัน เพื่อที่เวลาค่าส่งกลับมาก็เช็คตัวแปรนั้นๆ ว่าเป็นค่าใด จะได้รู้ว่าเป็นแอพใด ส่วนตัวเลขที่กำหนดเป็น Request Code ก็ขึ้นอยู่กับผู้ที่หลงเข้ามาอ่านจะกำหนด


        ทีนี้มาดูการรับข้อมูลหลังจากที่ใช้งานแอพภายนอกเสร็จแล้ว อย่างที่บอกไป การรับข้อมูลที่ส่งกลับมาจะทำใน onActivityResult สิ่งที่ต้องทำก็คือประกาศฟังก์ชันนี้ไว้ เมื่อมีข้อมูลส่งกลับมา ฟังก์ชันนี้ก็จะทำงานเองโดยอัตโนมัติ (เหมือน Event Listener)

public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == 0) {
        // กรณีที่ข้อมูลมาจากแอพที่กำหนด Request Code เป็น 0 ไว้
    }
}

         ดังนั้นในกรณีที่มีการใช้ Intent กับแอพมากกว่าหนึ่งแอพขึ้นไป

public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == 0) {
        // ข้อมูลมาจากแอพที่กำหนด Request Code เป็น 0
    } else if (requestCode == 1) {
        // ข้อมูลมาจากแอพที่กำหนด Request Code เป็น 1
    }
}

        ทีนี้ให้สังเกตที่ฟังก์ชัน onActivityResult ต่อ จะเห็นว่ามีตัวแปร resultCode ด้วย ซึ่งตัวแปรดังกล่าวมีไว้สำหรับเช็คสถานะการทำงานของแอพภายนอก โดยมีอยู่สองค่าคือ ระหว่าง -1 กับ 0 ซึ่ง -1 คือ การทำงานได้สำเร็จปกติ และ 0 คือ การทำงานถูกยกเลิก อาจจะเกิดมาจากการผู้ใช้กดปิดเป็นต้น โดยที่ -1 กับ 0 แทนด้วยตัวแปร RESULT_OK กับ RESULT_CANCELED ได้ทันที โดยไม่ต้องประกาศตัวแปร เพราะคลาส Activity ได้ประกาศไว้แล้ว

        ดังนั้นเมื่อเช็คว่า Request Code มีค่าเท่าไร ก็ให้เช็คต่อด้วยว่า Result Code เท่าไร

public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == 0) {
        if (resultCode == RESULT_OK) {
            // ข้อมูลมาจากแอพที่กำหนด Request Code เป็น 0
            // และสถานะการทำงานถูกต้อง Result Code เป็น -1
        }
        
    } 
}

        หรือ

public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == 0 && resultCode == RESULT_OK) {
        // ข้อมูลมาจากแอพที่กำหนด Request Code เป็น 0
        // และสถานะการทำงานถูกต้อง Result Code เป็น -1
    } 
}

        และในฟังก์ชัน onAcitivtyResult สำหรับการ Intent เพื่อใช้งานกล้องถ่ายรูป ข้อมูลที่ส่งมาไม่จำเป็นซักเท่าไร เพราะว่าได้สั่งให้เซฟภาพลงในเครื่องแล้ว ดังนั้นจะทำการดึงข้อมูลภาพที่อยู่บนเครื่องแทนเลย แล้วแปลงเป็น Bitmap

public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == REQUEST_CAMERA && resultCode == RESULT_OK) {
        getContentResolver().notifyChange(uri, null);
        ContentResolver cr = getContentResolver();
        try {
            Bitmap bitmap = Media.getBitmap(cr, uri);
            imageView.setImageBitmap(bitmap);
            Toast.makeText(getApplicationContext()
                    , uri.getPath(), Toast.LENGTH_SHORT).show();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

        สำหรับการแปลงเป็น Bitmap นั้นไม่จำเป็นเสมอไป เพราะขึ้นอยู่กับการใช้งาน ในตัวอย่างบทความนี้แค่เอาภาพที่ได้จากแอพถ่ายรูป มาแสดงบน Image View ทีนี้ก็มาดูโค๊ดตัวอย่างของบทความนี้เลยดีกว่า


main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <Button
        android:id="@+id/buttonIntent"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="30dp"
        android:text="Camera" />

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:layout_below="@+id/buttonIntent"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="30dp" />

</RelativeLayout>

Main.java
package app.akexorcist.intentcamera;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;

import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.provider.MediaStore.Images.Media;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.Intent;
import android.graphics.Bitmap;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;

public class Main extends Activity {
    public static final int REQUEST_CAMERA = 2;
    
    ImageView imageView;
    Uri uri;
    
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        imageView = (ImageView)findViewById(R.id.imageView);
        
        Button buttonIntent = (Button)findViewById(R.id.buttonIntent);
        buttonIntent.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                String timeStamp = 
                        new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
                String imageFileName = "IMG_" + timeStamp + ".jpg";
                File f = new File(Environment.getExternalStorageDirectory()
                        , "DCIM/Camera/" + imageFileName);
                uri = Uri.fromFile(f);
                intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
                startActivityForResult(Intent.createChooser(intent
                        , "Take a picture with"), REQUEST_CAMERA);
            }
        });
    }
    
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == REQUEST_CAMERA && resultCode == RESULT_OK) {
            getContentResolver().notifyChange(uri, null);
            ContentResolver cr = getContentResolver();
            try {
                Bitmap bitmap = Media.getBitmap(cr, uri);
                imageView.setImageBitmap(bitmap);
                Toast.makeText(getApplicationContext()
                        , uri.getPath(), Toast.LENGTH_SHORT).show();
            } catch (Exception e) {
                 e.printStackTrace();
            }
        }
    }
}


        1. ประกาศตัวแปร Integer แบบเป็นค่าคงที่ กำหนดชื่อว่า REQUEST_CAMERA ให้มีค่าเท่ากับ 2 ซึ่งตัวแปรนี้จะใช้เป็นค่าสำหรับ Request Code ในตอน Intent

        2. ประกาศตัวแปรของคลาส Image View กับ Uri ไว้ข้างนอกก่อน เพราะจะมีการเรียกใช้จากฟังก์ชัน onCreate และ onActivityResult

        3. กำหนดค่าให้กับ imageView เพื่อใช้สำหรับแสดงภาพที่จากแอพภายนอก

        4. ประกาศตัวแปรของคลาส Button และกำหนดค่า พร้อมกับสร้าง Listener

        5. ประกาศตัวแปรของคลาส Intent และกำหนดค่าเพื่อใช้ Intent ไปที่แอพถ่ายรูปแล้วกำหนดชื่อไฟล์ที่จะบันทึกลงในเครื่องแล้วเก็บไว้ในตัวแปรของคลาส File

        6. กำหนดค่าให้กับตัวแปรของคลาส Uri โดยอิงจากตัวแปรของคลาส File (เลข 5)

        7. กำหนดค่าให้กับ Intent ซึ่งเป็นค่าที่ระบุที่อยู่ของไฟล์ที่จะให้บันทึกลงเครื่อง แล้วทำการ Intent โดยกำหนดให้ Request Code เป็นตัวแปร REQUEST_CAMERA

        8. ฟังก์ชันสำหรับรอรับข้อมูลที่มาจากแอพภายนอก ที่ได้ Intent ไปยังแอพนั้นๆ โดยที่เมื่อฟังก์ชันนี้ทำงานจะมีการส่ง Request Code, Result Code และข้อมูลมาด้วย แต่ในการใช้งานแอพถ่ายรูปจากภายนอก ได้กำหนดให้บันทึกไฟล์ลงในเครื่องแล้ว จึงไม่ต้องรับข้อมูลจากฟังก์ชันนี้ จะสนใจแค่ Request Code กับ Result Code เท่านั้น

        9. เช็คว่าเป็นข้อมูลที่ส่งมาจากแอพที่ Request Code เป็น REQUEST_CAMERA หรือไม่ และเช็คว่า Result Code มีค่าเป็น -1 หรือไม่ (แอพทำงานสำเร็จ) ถ้าเงื่อนไขถูกทั้งคู่ก็จะให้ดึงไฟล์ภาพที่บันทึกลงเครื่องไว้ มาแปลงเป็น Bitmap เพื่อแสดงบน Image View ที่ได้สร้างเตรียมไว้แล้ว พร้อมกับบแสดง Toast เพื่อบอกว่าได้บันทึกภาพไว้ที่ใด

        ทีนี้ เผื่อผู้ที่หลงเข้ามาอ่านสงสัย ว่าไปไปดึงข้อมูลของไฟล์ที่เก็บไว้ยังไง ให้สังเกตุที่การทำงานของคลาส Uri เป็นหลัก เพราะคลาสนั้นใช้ดึงไฟล์


        เจ้าของบล็อกได้ประกาศไว้ที่ข้างนอกเพื่อใช้ระหว่างสองฟังก์ชัน เมื่อตัวแปรของคลาส Uri ถูกกำหนดค่าด้วยคำสั่ง Uri.fromFile  ก็จะถูกนำไปกำหนดให้แอพภายนอกบันทึกไฟล์ตามที่กำหนดไว้ 

        เมื่อแอพภายนอกทำงานเสร็จ ก็จะเข้าฟังก์ชันข้างล่างทันที ก็จะดึงข้อมูลจากไฟล์ดังกล่าวโดยอิงจากตัวแปรของคลาส Uri  ที่เคยกำหนดค่าไว้เรียบร้อยแล้วมาใช้ดึงไฟล์มาแสดงภาพนั่นเอง

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="app.akexorcist.intentcamera"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="8" />

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

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="app.akexorcist.intentcamera.Main"
            android:label="@string/app_name" 
            android:screenOrientation="portrait">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

        เมื่อทดสอบการทำงานก็ให้กดปุ่ม Button แล้วจะมีหน้าต่างที่แสดงรายชื่อแอพถ่ายรูปที่มีอยู่ในเครื่องขึ้นมาให้เลือก และเมื่อถ่ายรูปเสร็จแล้ว แอพนั้นๆก็จะปิดตัวลงแล้วกลับมา พร้อมทั้งส่งข้อมูลมาด้วย ก็จะนำภาพที่ถ่ายเมื่อครู่มาแสดง


        สำหรับผู้ที่หลงเข้ามาอ่านคนใดต้องการไฟล์ตัวอย่างสามารถดาวน์โหลดได้ที่ Intent Camera [Google Drive]



บทความที่เกี่ยวข้อง

        การใช้ Intent สำหรับแชร์ข้อความ String [Send]
        การใช้ Intent สำหรับแชร์ข้อความสำหรับ Email [Send]
        การใช้ Intent เพื่อเปิด URL [View]
        การใช้ Intent เพื่อเปิดแผนที่ [View]
        การใช้ Intent เพื่อเปิดไฟล์ใดๆ [View]
        การเรียกเปิดแอพฯอื่นๆ ด้วย Intent
        การใช้ Intent สำหรับแชร์ไฟล์ใดๆ [Send]
        การเลือกไฟล์ภาพจาก Gallery ด้วย Intent [Result]
        การใช้งานกล้องเพื่อถ่ายภาพแบบง่ายๆด้วย Intent [Result]
        การใช้งานกล้องเพื่อบันทึกวีดีโอแบบง่ายๆด้วย Intent [Result]
        การอ่าน QR Code และ Barcode ด้วย Intent [Result]
        การรับข้อมูล Intent จากแอพฯอื่นๆ [Get Content]
        การรับข้อมูล Intent จากแอพฯอื่นแล้วส่งข้อมูลกลับไป [Result Content]