您如何获得适用于 Android 的简单相机程序?

Posted

技术标签:

【中文标题】您如何获得适用于 Android 的简单相机程序?【英文标题】:How do you get a simple camera program working for Android? 【发布时间】:2011-11-28 04:28:38 【问题描述】:

我刚开始用 Java 编程,我需要建立一个简单的应用程序来显示相机、拍照并将图片数据发送到某个地方。

我一直在网上搜索,试图找到一个按预期工作的好的相机教程,但显然它们都需要一些我还没有的内在知识。

在this 页面上,commonsWare 指出其中包含一些用于使用相机的示例代码的代码。我已经获取了 PictureDemo 代码并让它运行没有错误。但是,它只会带来黑屏。我认为这是因为程序实际上并没有在主功能中激活预览或相机,但是每次我尝试添加我认为需要的代码时,我都会遇到异常。

所以我的问题是,我需要在主函数中添加什么才能让相机正常运行?或者有没有更好的教程可以让我看到简单的启动相机的基本代码?

package assist.core;

import android.app.Activity;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.KeyEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.Toast;
import java.io.File;
import java.io.FileOutputStream;

import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

public class MainActivity extends Activity

    private SurfaceView preview = null;
    private SurfaceHolder previewHolder = null;
    private Camera camera = null;
    private boolean inPreview = false;

    /**
     * 
     */
    @Override
    public void onCreate(Bundle savedInstanceState)
    
        //Call the parent class
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        preview = (SurfaceView) findViewById(R.id.preview);
        previewHolder = preview.getHolder();
        previewHolder.addCallback(surfaceCallback);
        previewHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    

    @Override
    public void onResume() 
        super.onResume();
        //Get the camera instance
        camera = CameraFinder.INSTANCE.open();
    

    @Override
    public void onPause() 
        if (inPreview) 
            camera.stopPreview();
        

        camera.release();
        camera = null;
        inPreview = false;

        super.onPause();
    

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) 
        if (keyCode == KeyEvent.KEYCODE_CAMERA || keyCode == KeyEvent.KEYCODE_SEARCH) 
            if (inPreview) 
                camera.takePicture(null, null, photoCallback);
                inPreview = false;
            

            return(true);
        

        return(super.onKeyDown(keyCode, event));
    

    private Camera.Size getBestPreviewSize(int width, int height, Camera.Parameters parameters) 
        Camera.Size result = null;

        for (Camera.Size size : parameters.getSupportedPreviewSizes()) 
            if (size.width <= width && size.height <= height) 
                if (result == null) 
                  result=size;
                
                else 
                    int resultArea = result.width * result.height;
                    int newArea = size.width * size.height;

                    if (newArea > resultArea) 
                        result = size;
                    
                
            
        

        return(result);
    

    SurfaceHolder.Callback surfaceCallback = new SurfaceHolder.Callback() 
        public void surfaceCreated(SurfaceHolder holder) 
            try 
                camera.setPreviewDisplay(previewHolder);
            
            catch (Throwable t) 
                Log.e("MainActivity-surfaceCallback", "Exception in setPreviewDisplay()", t);
                Toast.makeText(MainActivity.this, t.getMessage(), Toast.LENGTH_LONG).show();
            
        

        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) 
            Camera.Parameters parameters = camera.getParameters();
            Camera.Size size = getBestPreviewSize(width, height, parameters);

            if (size != null) 
                parameters.setPreviewSize(size.width, size.height);
                parameters.setPictureFormat(PixelFormat.JPEG);

                camera.setParameters(parameters);
                camera.startPreview();
                inPreview = true;
            
        

        public void surfaceDestroyed(SurfaceHolder holder) 
            // no-op
        
    ;

    Camera.PictureCallback photoCallback = new Camera.PictureCallback() 
        public void onPictureTaken(byte[] data, Camera camera) 
            new SavePhotoTask().execute(data);
            camera.startPreview();
            inPreview = true;
        
    ;

    class SavePhotoTask extends AsyncTask<byte[], String, String> 
        @Override
        protected String doInBackground(byte[]... jpeg) 
            File photo = new File(Environment.getExternalStorageDirectory(), "photo.jpg");
            if(photo.exists()) 
                photo.delete();
            

            try 
                FileOutputStream fos = new FileOutputStream(photo.getPath());
                fos.write(jpeg[0]);
                fos.close();
            
            catch (java.io.IOException e) 
                Log.e("MainActivity", "Exception in photoCallback", e);
            

            return(null);
        
    

更新

至于我得到的异常,如果我尝试像下面的代码那样制作 main 函数,

public void onCreate(Bundle savedInstanceState)

    //Call the parent class
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    preview = (SurfaceView) findViewById(R.id.preview);
    previewHolder = preview.getHolder();
    previewHolder.addCallback(surfaceCallback);
    previewHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

    Camera.Parameters parameters = camera.getParameters();
    parameters.setPictureFormat(PixelFormat.JPEG);
    camera.setParameters(parameters);

    try 
        //Start the camera preview display
        camera.setPreviewDisplay(previewHolder);
        camera.startPreview();
     
    catch (IOException ex) 
        Logger.getLogger(MainActivity.class.getName()).log(Level.SEVERE, null, ex);
    

我收到“应用程序意外停止。请重试。”我基本上是在尝试遵循android documention 指定的步骤。我还尝试在检索到相机对象后将此代码放入 onResume 函数中,然后调用 this.onResume。

【问题讨论】:

【参考方案1】:

这是一个与 Intents 配合使用的功能齐全的相机(我会立即调用相机,您显然会包含一个按钮或菜单项)。它将图像保存到图库和根目录为 Pic.jpg,然后显示捕获的图像。

这个答案是this EXCELLENT snippet by Aleksander O.的扩展和完整版本

import java.io.File;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;

public class CallCameraActivity extends Activity 

    final int TAKE_PICTURE = 1;
    private Uri imageUri;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        takePhoto();
    



    public void takePhoto() 
        Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
        File photo = new File(Environment.getExternalStorageDirectory(),  "Pic.jpg");
        intent.putExtra(MediaStore.EXTRA_OUTPUT,
                Uri.fromFile(photo));
        imageUri = Uri.fromFile(photo);
        startActivityForResult(intent, TAKE_PICTURE);
    
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) 

    super.onActivityResult(requestCode, resultCode, data);
    switch (requestCode) 
    case TAKE_PICTURE:
        if (resultCode == Activity.RESULT_OK) 
            //Uri imageUri;
            Uri selectedImage = imageUri;
            getContentResolver().notifyChange(selectedImage, null);
            ImageView imageView = (ImageView) findViewById(R.id.IMAGE);
            ContentResolver cr = getContentResolver();
            Bitmap bitmap;
            try 
                 bitmap = android.provider.MediaStore.Images.Media
                 .getBitmap(cr, selectedImage);

                imageView.setImageBitmap(bitmap);
                Toast.makeText(this, "This file: "+selectedImage.toString(),
                        Toast.LENGTH_LONG).show();
             catch (Exception e) 
                Toast.makeText(this, "Failed to load", Toast.LENGTH_SHORT)
                        .show();
                Log.e("Camera", e.toString());
            
        
    


使用它作为你的 main.xml 布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_
    android:layout_
    android:orientation="vertical" >

    <TextView
        android:layout_
        android:layout_
        android:text="@string/hello" />


    <ImageView
        android:id="@+id/IMAGE"
        android:layout_
        android:layout_
        android:src="@drawable/ic_launcher" />

</LinearLayout>

字符串.xml

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<resources>
  <string name="hello">Hello World, TonyPicturesInc!</string>
  <string name="app_name">TGCam</string>
</resources>

【讨论】:

太棒了!感谢您发布这个 tony,在互联网上获得更多工作相机的东西会很棒,因为我很难解决当前示例的所有问题。我终于也得到了一个工作版本,但是有一堆例子也没有什么坏处。【参考方案2】:

如果您需要的只是打开相机、拍照并获取图像。 那么你可以这样做: launch Camera using Intents

您上面的代码是集成版本。这会将相机集成到您的应用程序中。 我不确定你是否有真正的安卓设备来测试。 模拟器在替换真实相机时会显示黑色或“android icon”屏幕。

如果不是,请在真实设备上进行测试。

您能否更详细地说明您遇到的异常情况?

【讨论】:

Hmmmm...我看到其他人也使用 Intents 来执行此操作,但我不明白 Intents 的用途,或者我为什么要在此功能上使用它。我确实需要图像中的字节数据,而不是图像本身,如果这有什么不同的话。另外,我正在 Droid Incredible 上对其进行测试,这就是我得到黑屏的地方。 我相信我确实需要将相机集成到我的应用程序中。如果我不能用 Intents 做到这一点,那么我将不得不坚持下去。 意图就像“要求某人为你做某事”。如果您想了解更多信息,link。回到您的问题,简而言之,这是一种集成相机的简单方法。 link。试试看你是否明白。如果没有,请回到这里。 这实际上是我已经尝试过的教程之一。但是让我再试一次,看看我能不能让它工作。我现在比那时知道的更多:p 只是不要忘记权限 :)【参考方案3】:

嘿,你必须看看这个答案,它可能会对你有所帮助。

Click here

或者您也可以使用 Intent 启动内置摄像头,如下所示

Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(i, CAMERA_RESULT);

你必须像下面的代码一样从相机活动中获得结果

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) 
        // TODO Auto-generated method stub
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == RESULT_OK && requestCode == CAMERA_RESULT) 
            Bundle extras = data.getExtras();
            if(extras.containsKey("data")) 
                Bitmap bmp = (Bitmap) extras.get("data");
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                bmp.compress(Bitmap.CompressFormat.PNG, 100, baos);
                byte[] image = baos.toByteArray();
                if(image != null) 
        BitmapFactory.Options options=new BitmapFactory.Options();
        options.inSampleSize = 5;
        Bitmap myImage = BitmapFactory.decodeByteArray(image , 0, image.length, options);
        imageView.setImageBitmap(myImage);

                
            else 
                Toast.makeText(getBaseContext(), "Please capture again", Toast.LENGTH_LONG).show();
            


        
    

【讨论】:

以上是关于您如何获得适用于 Android 的简单相机程序?的主要内容,如果未能解决你的问题,请参考以下文章

如何获得以下适用于 Android 的弧形动态变色动画?

适用于 Android/iOS/WinPhone 的 Xamarin 通用层

android.hardware.Camera 是不是仍然适用于所有 API 级别?

Android如何显示前置摄像头(仅适用于镜子)

Android:如何通过回调显示相机预览?

使用适用于 Android 7.1.1 的 FileProvider 使用相机拍照后,我无法将文件保存到图库