16 กุมภาพันธ์ 2556

[Android Code] การติดต่อใช้งานกล้อง



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

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


        สำหรับการใช้งานกล้อง ก่อนอื่นให้เข้าใจก่อนเลยว่าภาพที่แสดงตอนเปิดหน้ากล้องเรียกว่า Surface View ดังนั้นใน Layout ก็จะต้องมี Surface View อยู่ด้วย เพื่อให้สามารถแสดงภาพจากหน้ากล้องให้ผู้ใช้เห็น (จะไม่ใช้ก็ได้ สำหรับกรณีที่เปิดกล้องแต่ไม่แสดงภาพ)


        จากภาพข้างบนก็เป็นภาพเวลาเปิดหน้ากล้อง ซึ่งก็คือ Surface View ที่วางอยู่บน Layout นั่นแหละ โดยที่ Surface View จะมีสถานะอยู่ด้วยกัน 3 สถานะ SurfaceCreated, SurfaceChanged และ SurfaceDestroyed ก็คงไม่ต้องอธิบายอะไรมาก เพราะแปลตรงตัวเลย

public void surfaceChanged(SurfaceHolder arg0
        , int arg1, int arg2, int arg3) {

}

public void surfaceCreated(SurfaceHolder arg0) {

}

public void surfaceDestroyed(SurfaceHolder arg0) { 

}

        โดยการใช้งานกล้องก็จะต้องมีการกำหนดค่าก่อน ตัวกล้องจะมีค่าต่างๆให้กำหนดมากพอควร เช่น ความละเอียดของภาพถ่ายและภาพเปิดหน้ากล้อง EV, White Balance คุณภาพของภาพถ่าย เป็นต้น แต่จะไม่อธิบายละเอียดมากนักนะ พูดถึงการใช้งานพอ

        และในบทความนี้จะมีการใช้งาน onResume กับ onPause ด้วย สำหรับกำหนดการใช้กล้อง เวลาที่ onResume ก็เปิดหน้ากล้อง ส่วน onPause ก็จะปิดหน้ากล้อง เพื่อที่แอพอื่นจะใช้งานกล้องได้

Camera mCamera;

public void onResume() {
    mCamera = Camera.open();
}
    
public void onPause() {
    mCamera.release();
}

        และที่สำคัญก็คือการขอ Permission เพื่อใช้งานกล้อง ซึ่งจะต้องประกาศใน AndroidManifest.xml ด้วยทุกครั้ง

<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />

        หลักๆ ก็มีเท่านี้แหละ ทีนี้มาดูการใช้งานจริงเลยดีกว่า เพราะคำสั่งข้างต้นแค่คร่าวๆเท่านั้น จริงๆมีปลีกย่อยอีก
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" >

    <SurfaceView
        android:id="@+id/preview"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" />

</RelativeLayout>

        พอใส่ Surface View ลงไปใน Layout ก็จะได้ออกมาดังนี้


        พื้นที่สีเทาทั้งหมดนี่คือ Surface View ที่ใช้แสดงภาพจากกล้อง

Main.java
package app.akexorcist.camerasample;

import java.util.List;

import android.hardware.Camera;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.Window;
import android.view.WindowManager;
import android.app.Activity;

public class Main extends Activity implements SurfaceHolder.Callback {
    Camera mCamera;
    SurfaceView mPreview;
    
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN 
                | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        setContentView(R.layout.main);
        
        mPreview = (SurfaceView)findViewById(R.id.preview);
        mPreview.getHolder().addCallback(this);
        mPreview.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }
    
    public void onResume() {
        Log.d("System","onResume");
        super.onResume();
        mCamera = Camera.open();
    }
    
    public void onPause() {
        Log.d("System","onPause");
        super.onPause();
        mCamera.release();
    }

    public void surfaceChanged(SurfaceHolder arg0
            , int arg1, int arg2, int arg3) {
        Log.d("CameraSystem","surfaceChanged");
        Camera.Parameters params = mCamera.getParameters();
        List<Camera.Size> previewSize = params.getSupportedPreviewSizes();
        List<Camera.Size> pictureSize = params.getSupportedPictureSizes();
        params.setPictureSize(pictureSize.get(0).width,pictureSize.get(0).height);
        params.setPreviewSize(previewSize.get(0).width,previewSize.get(0).height);
        params.setJpegQuality(100);
        mCamera.setParameters(params);
        
        try {
            mCamera.setPreviewDisplay(mPreview.getHolder());
            mCamera.startPreview();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void surfaceCreated(SurfaceHolder arg0) {
        Log.d("CameraSystem","surfaceCreated");
        try {
            mCamera.setPreviewDisplay(mPreview.getHolder());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void surfaceDestroyed(SurfaceHolder arg0) { }
}


        1. ประกาศตัวแปรที่เรียกใช้คลาส Camera กับ SurfaceView อย่างละตัว โดย mCamera สำหรับควบคุมกล้อง mPreview สำหรับแสดงภาพหน้ากล้อง

        2. จะต้องมีการกำหนดให้แอปฯ เป็น Fullscreen และหน้าจอเปิดตลอดเวลา เพื่อป้องกันเวลาเปิดหน้ากล้องนานๆ ไม่ได้แตะจอ แล้วเครื่องจะปิดหน้าจอ


        3. กำหนดค่าให้กับ mPreview แล้วประกาศ Callback ให้กับ mPreview โดย Callback ที่ว่าก็คือฟังก์ชันทั้ง 3 ฟังก์ชันของ Surface View ก็คือ surfaceCreated, surfaceChanged และ surfaceDestroyed นั่นเอง 

        โดยวิธีนี้คือการ Implement ให้กับ Callback หรือ Event Listener ซึ่งจะต้องมีการประกาศใช้ Implement ที่ข้างบน ดังภาพนี้

        4. ฟังก์ชัน onResume ทำงานเมื่อแอปฯ ถูกเปิดหรือกลับมาทำงานอีกครั้ง

        5. เปิดการใช้งานกล้อง ซึ่งคำสั่ง Camera.open() เป็นคำสั่งเก่าแล้ว สำหรับพวกแอนดรอยด์สมัย 2.2 ลงไป เพราะสมัยนั้นยังไม่มีกล้องหน้า ซึ่งใน 2.3 ขึ้นมา อุปกรณ์แอนดรอยด์จะเริ่มมีกล้องหน้าเข้ามาแล้ว ก็จะมีการเพิ่มคำสั่งเข้าไปเป็น Camera.open(cameraID) เพื่อเลือกกล้อง เช่น Camera.open(0) แต่ขอใช้เป็นคำสั่งเก่าไปก่อนเพื่อไม่ให้สับสน

        6. ฟังก์ชัน onPause ทำงานเมื่อแอปฯ ถูกปิดหรือย่อไว้ชั่วคราว

        7. ปิดการใช้งานกล้อง เพื่อแอปฯ อื่นสามารถใช้งานได้

        8. ฟังก์ชันที่จะทำงานเมื่อ Surface มีการเปลี่ยนแปลง โดยฟังก์ชันนี้เจ้าของบล็อกก็จะใช้กำหนดค่าให้กับกล้อง ซึ่งจริงๆจะใช้ค่าเริ่มต้นก็ได้ แต่ว่าภาพหน้ากล้องไม่คมนัก เลยจะกำหนดให้ภาพถ่ายและภาพหน้ากล้องละเอียดที่สุด (ถ้าภาพที่ได้ความละเอียดต่ำให้เปลี่ยนจาก 0 เป็น Size ของ List นั้นๆ - 1)

        9. อ่านค่า Parameter จากกล้องก่อน ซึ่งเก็บไว้ในตัวแปร params แล้วดึงค่าความละเอียดของภาพเปิดหน้ากล้องที่ตั้งค่าได้ทั้งหมด มาเก็บไว้ในตัวแปร previewSize และความละเอียดของภาพถ่าย เก็บไว้ในตัวแปร pictureSize ซึ่งตัวแปรทั้งสองเป็น Array แบบหนึ่ง ซึ่งจะมีลักษณะการใช้งานคล้ายๆกับ ArrayList นั่นแหละ เท่านี้ก็จะได้ค่าความละเอียดของกล้องที่สามารถตั้งค่าได้แล้ว

        (ไม่สามารถกำหนดค่าสะเปะสะปะได้ ไม่งั้นเด้งเออเรอแน่)

        10. กำหนดความละเอียดของภาพถ่ายโดยดึงค่าจาก Array ตัวแรกสุด ซึ่งก็คือ Array ลำดับที่ 0 นั่นเอง จะเห็นว่าการดึงค่าก็เหมือน ArrayList คือใช้คำสั่ง .get(position) เพื่อระบุลำดับของ Array ที่จะดึงค่ามาใช้ โดยการกำหนดความละเอียดของภาพจากหน้ากล้องก็เช่นกัน และกำหนดคุณภาพของภาพถ่ายเป็น 100% คือไม่มีการลดคุณภาพ แล้วกำหนดค่า params ให้กับ mCamera ก็เสร็จสิ้นการกำหนดค่ากล้อง

        11. ทำการกำหนดให้กล้องแสดงภาพหน้ากล้องไปที่ mPreview แล้วทำการแสดงภาพจากหน้ากล้องไปแสดงที่ mPreview ทันที

        12. ฟังก์ชันที่ทำงานเมื่อ Surface ถูกสร้างขึ้น ซึ่งจะไม่มีคำสั่งอะไร เพราะเจ้าของบล็อกรวมคำสั่งไว้ในฟังก์ชัน surfaceChanged แล้ว

        13. ฟังก์ชันที่ทำงานเมื่อ Surface ถูกลบทิ้ง ซึ่งก็ไม่มีคำสั่งอะไร เพราะเจ้าของบล็อกปิดการใช้งานกล้องใน onPause แล้ว


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

    <uses-sdk
        android:minSdkVersion="10"
        android:targetSdkVersion="10" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-feature android:name="android.hardware.camera" />
    <uses-feature android:name="android.hardware.camera.autofocus" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher" >
        <activity
            android:name="Main"
            android:label="@string/app_name" 
            android:screenOrientation="landscape">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

</manifest>

        สำหรับ AndroidManifest.xml ก็อย่าลืมที่บอกไปนะ ต้องประกาศ Permission เพื่อขอใช้งานกล้องทุกครั้ง

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

        เพราะงั้นเรื่องกล้องจะยังไม่จบนะ รอบทความต่อไป

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


เรื่องราวเก่าๆสำหรับกล้อง กล้อง และกล้อง

        • [Android Code] การติดต่อใช้งานกล้อง
        • [Android Code] การถ่ายภาพด้วยกล้อง
        • [Android Code] เรียกใช้งาน Auto Focus ของกล้อง
        • [Android Code] การทำให้กล้อง Auto Focus แบบอัตโนมัติ
        • [Android Code] การดึงภาพจากหน้า Preview Frame มาใช้งาน



เหล่าพันธมิตรแอนดรอยด์

Devahoy Layer Net NuuNeoI The Cheese Factory Somkiat CC Mart Routine Artit-K Arnondora Kamonway Try to be android developer Oatrice Benz Nest Studios Kotchaphan@Medium Jirawatee@Medium Travispea@Medium