Cámara Android: la intención de los datos devuelve nulo

132

Tengo una aplicación para Android que contiene múltiples actividades.

En uno de ellos estoy usando un botón que llamará a la cámara del dispositivo:

public void onClick(View view) {
    Intent photoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    startActivityForResult(photoIntent, IMAGE_CAPTURE);
}

En la misma actividad, llamo al OnActivityResultmétodo para el resultado de la imagen:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == IMAGE_CAPTURE) {
        if (resultCode == RESULT_OK) {
            Bitmap image = (Bitmap) data.getExtras().get("data");
            ImageView imageview = (ImageView) findViewById(R.id.pic);
            imageview.setImageBitmap(image);
        } else if (resultCode == RESULT_CANCELED) {
            Toast.makeText(this, "CANCELED ", Toast.LENGTH_LONG).show();
        }
    }
}

El problema es que la intención dataes nula y el OnActivityResultmétodo se dirige directamente a (resultCode == RESULT_CANCELED)y la aplicación vuelve a la actividad anterior.

¿Cómo puedo solucionar este problema y después de llamar a la cámara, la aplicación vuelve a la actividad actual que contiene una ImageViewque contiene la imagen tomada?

Gracias

soft_developer
fuente
eche un vistazo a esta respuesta stackoverflow.com/questions/18120775/…
Praveen Sharma

Respuestas:

218

La aplicación de cámara de Android predeterminada devuelve una intención no nula solo cuando devuelve una miniatura en la intención devuelta. Si pasa EXTRA_OUTPUTcon un URI para escribir, devolverá una nullintención y la imagen está en el URI que pasó.

Puede verificar esto mirando el código fuente de la aplicación de la cámara en GitHub:

Supongo que estás pasando de EXTRA_OUTPUTalguna manera, o la aplicación de la cámara en tu teléfono funciona de manera diferente.

Brian Slesinsky
fuente
12
¿Cómo solucionaría la funcionalidad put Extra, que da una intención no nula en la cámara enActivityResult
Abdul Wahab
Respuesta realmente útil Ojalá pudiera aceptarla para que otros encuentren la solución de manera más eficiente.
user1160020
1
Increíble ! Muchas gracias por la explicación :) +100
Muhammad Riyaz
El enlace que publicaste ya no es válido.
44
¿Podríamos proteger la clase de cámara y simplemente cambiar ese método para permitir ambas, o es más difícil que eso?
Tanner Summers el
16

Encontré una respuesta fácil. ¡¡funciona!!

private void openCameraForResult(int requestCode){
    Intent photo = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    Uri uri  = Uri.parse("file:///sdcard/photo.jpg");
    photo.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, uri);
    startActivityForResult(photo,requestCode);
}

if (requestCode == CAMERA_REQUEST_CODE) {
        if (resultCode == Activity.RESULT_OK) {
            File file = new File(Environment.getExternalStorageDirectory().getPath(), "photo.jpg");
            Uri uri = Uri.fromFile(file);
            Bitmap bitmap;
            try {
                bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), uri);
                bitmap = cropAndScale(bitmap, 300); // if you mind scaling
                profileImageView.setImageBitmap(bitmap);
            } catch (FileNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }
    }

si desea recortar y escalar esta imagen

public static  Bitmap cropAndScale (Bitmap source, int scale){
    int factor = source.getHeight() <= source.getWidth() ? source.getHeight(): source.getWidth();
    int longer = source.getHeight() >= source.getWidth() ? source.getHeight(): source.getWidth();
    int x = source.getHeight() >= source.getWidth() ?0:(longer-factor)/2;
    int y = source.getHeight() <= source.getWidth() ?0:(longer-factor)/2;
    source = Bitmap.createBitmap(source, x, y, factor, factor);
    source = Bitmap.createScaledBitmap(source, scale, scale, false);
    return source;
}
izakos
fuente
44
Le da FileUriExposedExceptiona las versiones modernas de SDK.
Souradeep Nanda
8

He experimentado este problema, intentno es nulo pero la información enviada a través de este intentno se recibe enonActionActivit()

ingrese la descripción de la imagen aquí

Esta es una mejor solución usando getContentResolver () :

    private Uri imageUri;
    private ImageView myImageView;
    private Bitmap thumbnail;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

      ...
      ...    
      ...
      myImageview = (ImageView) findViewById(R.id.pic); 

      values = new ContentValues();
      values.put(MediaStore.Images.Media.TITLE, "MyPicture");
      values.put(MediaStore.Images.Media.DESCRIPTION, "Photo taken on " + System.currentTimeMillis());
      imageUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
      Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
      intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
      startActivityForResult(intent, PICTURE_RESULT);

  }

el onActivityResult()conseguir un mapa de bits almacenado por getContentResolver () :

 @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == REQUEST_CODE_TAKE_PHOTO && resultCode == RESULT_OK) {

            Bitmap bitmap;
            try {
                bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), imageUri);
                myImageView.setImageBitmap(bitmap);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

introducir la descripción de la imagen aquí introducir la descripción de la imagen aquí

Mira mi ejemplo en github:

https://github.com/Jorgesys/TakePicture

Jorgesys
fuente
6

Aplicación de cámara de trabajo simple que evita el problema de intención nula

- todo el código modificado incluido en esta respuesta; cerca del tutorial de Android

He pasado mucho tiempo en este tema, así que decidí crear una cuenta y compartir mis resultados con usted.

El tutorial oficial de Android "Taking Photos Simply" resultó no cumplir lo que prometía. El código provisto allí no funcionaba en mi dispositivo: un Samsung Galaxy S4 Mini GT-I9195 con la versión de Android 4.4.2 / KitKat / API Nivel 19.

Descubrí que el problema principal era la siguiente línea en el método invocado al capturar la foto ( dispatchTakePictureIntenten el tutorial):

takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);

Resultó en la intención posteriormente atrapada por onActivityResultser nula.

Para resolver este problema, obtuve mucha inspiración de las respuestas anteriores aquí y algunas publicaciones útiles en github (principalmente esta de deepwinter , muchas gracias a él; es posible que desee ver su respuesta en una publicación estrechamente relacionada ).

Siguiendo estos consejos agradables, elegí la estrategia de eliminar la putExtralínea mencionada y hacer lo correspondiente para recuperar la imagen tomada de la cámara dentro del método onActivityResult (). Las líneas de código decisivas para recuperar el mapa de bits asociado con la imagen son:

        Uri uri = intent.getData();
        Bitmap bitmap = null;
        try {
            bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), uri);
        } catch (IOException e) {
            e.printStackTrace();
        }

Creé una aplicación ejemplar que solo tiene la capacidad de tomar una foto, guardarla en la tarjeta SD y mostrarla. Creo que esto podría ser útil para las personas en la misma situación que yo cuando me topé con este problema, ya que las sugerencias de ayuda actuales se refieren principalmente a publicaciones de Github bastante extensas que hacen lo que se trata pero que no son demasiado fáciles de supervisar para novatos como yo. Con respecto al sistema de archivos que Android Studio crea por defecto al crear un nuevo proyecto, solo tuve que cambiar tres archivos para mi propósito:

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.android.simpleworkingcameraapp.MainActivity">

<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:onClick="takePicAndDisplayIt"
    android:text="Take a pic and display it." />

<ImageView
    android:id="@+id/image1"
    android:layout_width="match_parent"
    android:layout_height="200dp" />

</LinearLayout>

MainActivity.java:

package com.example.android.simpleworkingcameraapp;

import android.content.Intent;
import android.graphics.Bitmap;
import android.media.Image;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;

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

public class MainActivity extends AppCompatActivity {

private ImageView image;
static final int REQUEST_TAKE_PHOTO = 1;
String mCurrentPhotoPath;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    image = (ImageView) findViewById(R.id.image1);
}

// copied from the android development pages; just added a Toast to show the storage location
private File createImageFile() throws IOException {
    // Create an image file name
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmm").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
    mCurrentPhotoPath = image.getAbsolutePath();
    Toast.makeText(this, mCurrentPhotoPath, Toast.LENGTH_LONG).show();
    return image;
}

public void takePicAndDisplayIt(View view) {
    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    if (intent.resolveActivity(getPackageManager()) != null) {
        File file = null;
        try {
            file = createImageFile();
        } catch (IOException ex) {
            // Error occurred while creating the File
        }

        startActivityForResult(intent, REQUEST_TAKE_PHOTO);
    }
}

@Override
protected void onActivityResult(int requestCode, int resultcode, Intent intent) {
    if (requestCode == REQUEST_TAKE_PHOTO && resultcode == RESULT_OK) {
        Uri uri = intent.getData();
        Bitmap bitmap = null;
        try {
            bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), uri);
        } catch (IOException e) {
            e.printStackTrace();
        }
        image.setImageBitmap(bitmap);
    }
}
}

AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android.simpleworkingcameraapp">


<!--only added paragraph-->
<uses-feature
    android:name="android.hardware.camera"
    android:required="true" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />  <!-- only crucial line to add; for me it still worked without the other lines in this paragraph -->
<uses-permission android:name="android.permission.CAMERA" />


<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

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

</manifest>

Tenga en cuenta que la solución que encontré para el problema también condujo a una simplificación del archivo de manifiesto de Android: los cambios sugeridos por el tutorial de Android en términos de agregar un proveedor ya no son necesarios, ya que no estoy utilizando ninguno en mi código de Java. Por lo tanto, solo unas pocas líneas estándar, principalmente en relación con los permisos, tuvieron que agregarse al archivo de manifiesto.

También podría ser valioso señalar que la importación automática de Android Studio puede no ser capaz de manejar java.text.SimpleDateFormaty java.util.Date. Tuve que importarlos a ambos manualmente.

HGM
fuente
Estas soluciones requieren permiso de ALMACENAMIENTO, que no es un enfoque muy bueno.
shanraisshan
44
La pregunta establece claramente que el parámetro Intent denominado "datos" pasado a onActivityResult () es nulo, mientras que su solución continúa y trata de usar eso.
Maks
2

¿Probablemente porque tenías algo como esto?

Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);                        
Uri fileUri =  CommonUtilities.getTBCameraOutputMediaFileUri();                  
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);                        
startActivityForResult(takePictureIntent, 2);

Sin embargo, no debe poner la salida adicional en la intención, porque entonces los datos van al URI en lugar de la variable de datos. Por esa razón, tienes que tomar las dos líneas en el medio, para que tengas

Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(takePictureIntent, 2);

Eso es lo que me causó el problema, espero que haya ayudado.

Marius Hilarante
fuente
12
Esta respuesta es engañosa. Obtendrá una miniatura en lugar de una foto a tamaño completo sin poner MediaStore.EXTRA_OUTPUT. Se destaca en los documentos y en otras respuestas.
user2417480
2
Absolutamente esta respuesta es engañosa.
IndexOutOfDevelopersException
Esta es una respuesta engañosa.
Abdulla Thanseeh
0

El siguiente código funciona para mí:

Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(cameraIntent, 2);

Y aqui esta el resultado:

protected void onActivityResult(int requestCode, int resultCode, Intent imageReturnedIntent)
{ 
    super.onActivityResult(requestCode, resultCode, imageReturnedIntent);

    if(resultCode == RESULT_OK)
    {
        Uri selectedImage = imageReturnedIntent.getData();
        ImageView photo = (ImageView) findViewById(R.id.add_contact_label_photo);
        Bitmap mBitmap = null;
        try
        {
            mBitmap = Media.getBitmap(this.getContentResolver(), selectedImage);
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }
}
Manitoba
fuente
Lo probé pero es el mismo problema :(
soft_developer
1
Estoy obteniendo un imageReturnedIntent.getData()valor nulo. Estoy creando la misma intención que la tuya. No ha puesto ningún parámetro adicional.
Jaydip Kalkani
0

Código de Kotlin que funciona para mí:

 private fun takePhotoFromCamera() {
          val intent = Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE)
        startActivityForResult(intent, PERMISSIONS_REQUEST_TAKE_PICTURE_CAMERA)
      }

Y obtener Resultado:

 override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
 if (requestCode == PERMISSIONS_REQUEST_TAKE_PICTURE_CAMERA) {
         if (resultCode == Activity.RESULT_OK) {
           val photo: Bitmap? =  MediaStore.Images.Media.getBitmap(this.contentResolver, Uri.parse( data!!.dataString)   )
            // Do something here : set image to an ImageView or save it ..   
              imgV_pic.imageBitmap = photo 
        } else if (resultCode == Activity.RESULT_CANCELED) {
            Log.i(TAG, "Camera  , RESULT_CANCELED ")
        }

    }

}

y no olvides declarar el código de solicitud:

companion object {
 const val PERMISSIONS_REQUEST_TAKE_PICTURE_CAMERA = 300
  }
Hamed Jaliliani
fuente
0

Después de mucho intentarlo y estudiar, pude resolverlo. Primero, los datos variables de Intent siempre serán nulos, por lo tanto, la comprobación !nullbloqueará la aplicación durante tanto tiempo que pase un URI para iniciarActivityForResult. Siga el ejemplo a continuación. Usaré Kotlin.

  1. Abre la intención de la cámara

    fun addBathroomPhoto(){
    addbathroomphoto.setOnClickListener{
    
        request_capture_image=2
    
        var takePictureIntent:Intent?
        takePictureIntent =Intent(MediaStore.ACTION_IMAGE_CAPTURE)
        if(takePictureIntent.resolveActivity(activity?.getPackageManager()) != null){
    
            val photoFile: File? = try {
                createImageFile()
            } catch (ex: IOException) {
                // Error occurred while creating the File
    
                null
            }
    
            if (photoFile != null) {
                val photoURI: Uri = FileProvider.getUriForFile(
                    activity!!,
                    "ogavenue.ng.hotelroomkeeping.fileprovider",photoFile)
                takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
                    photoURI);
                startActivityForResult(takePictureIntent,
                    request_capture_image);
            }
    
    
        }
    
    }

    } `

  2. Cree createImageFile (). Pero DEBE hacer que la variable imageFilePath sea global. Ejemplo de cómo crearlo está en la documentación oficial de Android y es bastante sencillo

  3. Obtener intención

     override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    
    if (requestCode == 1 && resultCode == RESULT_OK) {
        add_room_photo_txt.text=""
        var myBitmap=BitmapFactory.decodeFile(imageFilePath)
        addroomphoto.setImageBitmap(myBitmap)
        var file=File(imageFilePath)
        var fis=FileInputStream(file)
        var bm = BitmapFactory.decodeStream(fis);
        roomphoto=getBytesFromBitmap(bm) }}
  4. El método getBytesFromBitmap

      fun getBytesFromBitmap(bitmap:Bitmap):ByteArray{
    
      var stream=ByteArrayOutputStream()
      bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
      return stream.toByteArray();
      }

Espero que esto ayude.

Gstuntz
fuente
0

Cuando capturaremos la imagen de la cámara en Android, entonces Uri o data.getdata()se anula. Tenemos dos soluciones para resolver este problema.

  1. Podemos obtener la ruta Uri de la imagen de mapa de bits
  2. Podemos obtener la ruta Uri desde el cursor.

Implementaré todos los métodos aquí. Observe y lea atentamente estos:

Primero diré cómo obtener Uri de la imagen de mapa de bits: el código completo es:

Primero capturaremos la imagen a través de Intent que será igual para ambos métodos, por lo que este código lo escribiré solo una vez aquí:

// Capture Image
captureImg.setOnClickListener(new View.OnClickListener(){
    @Override public void onClick(View view){
        Intent intent=new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        if(intent.resolveActivity(getPackageManager())!=null){
            startActivityForResult(intent,reqcode);
        }

    }
});  

Ahora implementaremos OnActivityResult :-( Esto será igual para los dos métodos anteriores): -

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == reqcode && resultCode == RESULT_OK)
    {

        Bitmap photo = (Bitmap) data.getExtras().get("data");
        ImageView.setImageBitmap(photo);

        // CALL THIS METHOD TO GET THE URI FROM THE BITMAP
        Uri tempUri = getImageUri(getApplicationContext(), photo);

        //Show Uri path based on Image
        Toast.makeText(LiveImage.this, "Here " + tempUri, Toast.LENGTH_LONG).show();

        //Show Uri path based on Cursor Content Resolver
        Toast.makeText(this, "Real path for URI : " + getRealPathFromURI(tempUri), Toast.LENGTH_SHORT).show();
    }
    else
    {
        Toast.makeText(this, "Failed To Capture Image", Toast.LENGTH_SHORT).show();
    }
}     

Ahora crearemos todo el método anterior para crear Uri a partir de métodos Image y Cursor a través de clases:

Ahora ruta URI desde la imagen de mapa de bits

private Uri getImageUri(Context applicationContext, Bitmap photo)
{
    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    photo.compress(Bitmap.CompressFormat.JPEG, 100, bytes);
    String path = MediaStore.Images.Media.insertImage(LiveImage.this.getContentResolver(), photo, "Title", null);
    return Uri.parse(path);
}

\ Uri desde la ruta real de la imagen guardada

public String getRealPathFromURI(Uri uri)
{
    Cursor cursor = getContentResolver().query(uri, null, null, null, null);
    cursor.moveToFirst();
    int idx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
    return cursor.getString(idx);
}
Pradeep Kumar
fuente
-1

Cuando capturamos la imagen de la cámara en Android entonces Urio se data.getdata()vuelve nula. Tenemos dos soluciones para resolver este problema.

  1. Recupere la ruta Uri de la imagen de mapa de bits
  2. Recupere la ruta Uri del cursor.

Así es como recuperar el Uri de la imagen de mapa de bits. Primero capture la imagen a través de Intent que será igual para ambos métodos:

// Capture Image
captureImg.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        if (intent.resolveActivity(getPackageManager()) != null) {
            startActivityForResult(intent, reqcode);
        }
    }
});

Ahora implemente OnActivityResult, que será el mismo para ambos métodos:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if(requestCode==reqcode && resultCode==RESULT_OK)
    {
        Bitmap photo = (Bitmap) data.getExtras().get("data");
        ImageView.setImageBitmap(photo);

        // CALL THIS METHOD TO GET THE URI FROM THE BITMAP
        Uri tempUri = getImageUri(getApplicationContext(), photo);

        // Show Uri path based on Image
        Toast.makeText(LiveImage.this,"Here "+ tempUri, Toast.LENGTH_LONG).show();

        // Show Uri path based on Cursor Content Resolver
        Toast.makeText(this, "Real path for URI : "+getRealPathFromURI(tempUri), Toast.LENGTH_SHORT).show();
    }
    else
    {
        Toast.makeText(this, "Failed To Capture Image", Toast.LENGTH_SHORT).show();
    }
}

Ahora cree todos los métodos anteriores para crear el Uri a partir de los métodos de Imagen y Cursor:

Ruta Uri desde la imagen de mapa de bits:

private Uri getImageUri(Context applicationContext, Bitmap photo) {
    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    photo.compress(Bitmap.CompressFormat.JPEG, 100, bytes);
    String path = MediaStore.Images.Media.insertImage(LiveImage.this.getContentResolver(), photo, "Title", null);
    return Uri.parse(path);
}

Uri del camino real de la imagen guardada:

public String getRealPathFromURI(Uri uri) {
    Cursor cursor = getContentResolver().query(uri, null, null, null, null);
    cursor.moveToFirst();
    int idx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
    return cursor.getString(idx);
}
Pradeep Sheoran
fuente
¿Alguien ha podido resolver esto? Amablemente publique su solución. El documento de Android obviamente no ha podido resolver esto
Gstuntz