Câmera Android: intenção de dados retorna nulo

132

Eu tenho um aplicativo Android que contém várias atividades.

Em um deles, estou usando um botão que chamará a câmera do dispositivo:

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

Na mesma atividade, chamo o OnActivityResultmétodo para o resultado da imagem:

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

O problema é que a intenção dataé nula e o OnActivityResultmétodo se volta diretamente para (resultCode == RESULT_CANCELED)o aplicativo e retorna à avtividade anterior.

Como posso corrigir esse problema e, depois de ligar para a câmera, o aplicativo retorna à atividade atual que contém uma ImageViewque contém a foto tirada?

obrigado

soft_developer
fonte
ter um olhar sobre esta resposta stackoverflow.com/questions/18120775/...
Praveen Sharma

Respostas:

218

O aplicativo de câmera padrão do Android retorna uma intenção não nula somente ao retornar uma miniatura na intenção retornada. Se você passar EXTRA_OUTPUTcom um URI para gravar, ele retornará uma nullintenção e a imagem estará no URI que você transmitiu.

Você pode verificar isso consultando o código-fonte do aplicativo da câmera no GitHub:

Eu acho que você está passando de EXTRA_OUTPUTalguma forma ou o aplicativo da câmera no telefone funciona de maneira diferente.

Brian Slesinsky
fonte
12
como eu ia corrigir a funcionalidade extra colocado, que dão não nula intenção em onActivityResult câmera
Abdul Wahab
Resposta realmente útil Eu gostaria de poder aceitá-la para que outras pessoas encontrem a solução com mais eficiência.
precisa saber é o seguinte
1
Impressionante ! Muito obrigado pela explicação :) +100
Muhammad Riyaz
O link que você postou não é mais válido.
4
Poderíamos iniertar a classe da câmera e apenas mudar esse método para permitir ambos, ou é mais difícil do que isso?
Tanner Summers
16

Eu encontrei uma resposta 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();
            }

        }
    }

se você deseja cortar e dimensionar esta imagem

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
fonte
4
Ele fornece FileUriExposedExceptionversões modernas do SDK.
Souradeep Nanda
8

Eu já tive esse problema, o intentnão é nulo, mas as informações enviadas por ele intentnão são recebidas emonActionActivit()

insira a descrição da imagem aqui

Esta é uma solução melhor 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);

  }

a onActivityResult()obter um bitmap armazenado 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();
            }

        }
    }

introduza a descrição da imagem aqui introduza a descrição da imagem aqui

Veja meu exemplo no github:

https://github.com/Jorgesys/TakePicture

Jorgesys
fonte
6

Aplicativo de câmera de trabalho simples, evitando o problema de intenção nula

- todo o código alterado incluído nesta resposta; perto do tutorial android

Como já gastei muito tempo com esse problema, decidi criar uma conta e compartilhar meus resultados com você.

O tutorial oficial para o Android "Tirar fotos com simplicidade" acabou não cumprindo o que prometia. O código fornecido não funcionava no meu dispositivo: um Samsung Galaxy S4 Mini GT-I9195 executando a versão android 4.4.2 / KitKat / API Level 19.

Eu descobri que o principal problema era a seguinte linha no método chamado ao capturar a foto ( dispatchTakePictureIntentno tutorial):

takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);

Resultou na intenção posteriormente capturada por onActivityResult ser nula.

Para resolver esse problema, tirei muita inspiração das respostas anteriores aqui e de alguns posts úteis no github (principalmente este do deepwinter - muito obrigado a ele; você também pode conferir a resposta dele em um post estreitamente relacionado ).

Seguindo esses conselhos agradáveis, escolhi a estratégia de excluir a putExtralinha mencionada e fazer o correspondente para recuperar a foto tirada da câmera no método onActivityResult (). As linhas de código decisivas para recuperar o bitmap associado à imagem são:

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

Eu criei um aplicativo exemplar que apenas tem a capacidade de tirar uma foto, salvá-lo no cartão SD e exibi-lo. Acho que isso pode ser útil para pessoas na mesma situação que eu quando me deparei com esse problema, já que as sugestões de ajuda atuais se referem principalmente a postagens bastante extensas no github que fazem a coisa em questão, mas não são fáceis de supervisionar para iniciantes como mim. Com relação ao sistema de arquivos que o Android Studio cria por padrão ao criar um novo projeto, eu apenas precisei alterar três arquivos para o meu 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>

Observe que a solução que encontrei para o problema também levou a uma simplificação do arquivo de manifesto do Android: as alterações sugeridas pelo tutorial do Android em termos de adição de um provedor não são mais necessárias, pois não estou usando nenhum código do meu java. Portanto, apenas algumas linhas padrão - principalmente em relação às permissões - tiveram que ser adicionadas ao arquivo de manifesto.

Além disso, pode ser valioso ressaltar que a importação automática do Android Studio pode não ser capaz de lidar java.text.SimpleDateFormate java.util.Date. Eu tive que importar os dois manualmente.

HGM
fonte
essas soluções requerem permissão de armazenamento, o que não é uma abordagem muito boa.
precisa saber é o seguinte
4
A pergunta afirma claramente que o parâmetro Intent chamado "data" transmitido para onActivityResult () é nulo, enquanto sua solução prossegue e tenta usá-lo.
Maks
2

Provavelmente porque você tinha algo assim?

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

No entanto, você não deve colocar a saída extra na intenção, porque os dados serão inseridos no URI em vez da variável de dados. Por esse motivo, você deve seguir as duas linhas no meio, para ter

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

Foi isso que causou o problema para mim, espero que tenha ajudado.

Marius Hilarious
fonte
12
Esta resposta é enganosa. Você obterá uma miniatura em vez de uma foto em tamanho normal sem colocar o MediaStore.EXTRA_OUTPUT. Está destacado nos documentos e em outras respostas.
user2417480
2
Absolutamente essa resposta é enganosa.
IndexOutOfDevelopersException
Esta é uma resposta enganosa.
Abdulla Thanseeh
0

O código a seguir funciona para mim:

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

E aqui está o 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
fonte
Eu testei, mas o mesmo problema :(
soft_developer
1
Estou recebendo imageReturnedIntent.getData()valor nulo. Estou criando a mesma intenção que a sua. Não colocou nenhum parâmetro extra.
Jaydip Kalkani
0

Código Kotlin que funciona para mim:

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

E obtenha 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 ")
        }

    }

}

e não se esqueça de declarar o código de solicitação:

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

Depois de muitas tentativas e estudos, consegui descobrir. Primeiro, os dados variáveis ​​do Intent sempre serão nulos; portanto, a verificação !nulltravará seu aplicativo enquanto você passar um URI para startActivityForResult. Siga o exemplo abaixo. Eu vou estar usando Kotlin.

  1. Abra a intenção da câmera

    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. Crie o createImageFile (), mas você DEVE tornar a variável imageFilePath global. Exemplo de como criá-lo está na documentação oficial do Android e é bastante direto

  3. Obter intenção

     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. O método getBytesFromBitmap

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

Eu espero que isso ajude.

Gstuntz
fonte
0

Quando vamos capturar a imagem da câmera no Android, em seguida, Uri ou data.getdata()vem nulo. Temos duas soluções para resolver esse problema.

  1. Podemos obter o caminho Uri a partir da imagem de bitmap
  2. Podemos pegar o caminho Uri a partir do cursor.

Implementarei todos os métodos aqui. Observe e leia com atenção:

Primeiro vou dizer como obter o Uri da imagem de bitmap: O código completo é:

Primeiro capturaremos a imagem através do Intent que será igual para os dois métodos, portanto, este código eu escreverei uma vez apenas aqui:

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

    }
});  

Agora vamos implementar OnActivityResult :-( Isso será o mesmo para os dois métodos acima): -

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

Agora, criaremos todo o método acima para criar Uri a partir dos métodos Image e Cursor por meio de classes:

Agora caminho URI da imagem de bitmap

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 do caminho real da imagem salva

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
fonte
-1

Quando capturar a imagem da câmera no Android, em seguida, Uriou data.getdata()torna-se nulo. Temos duas soluções para resolver esse problema.

  1. Recupere o caminho do Uri da imagem de bitmap
  2. Recupere o caminho Uri do cursor.

Isto é como recuperar o Uri da imagem de bitmap. Primeiro capture a imagem através do Intent que será a mesma para os dois 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);
        }
    }
});

Agora implemente OnActivityResult, que será o mesmo para os dois 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();
    }
}

Agora crie todos os métodos acima para criar os métodos Uri a partir de imagem e cursor:

Caminho de Uri da imagem de bitmap:

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 do caminho real da imagem salva:

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
fonte
Alguém foi capaz de resolver isso? Por favor, publique sua solução. Doc Android, obviamente, não tem sido capaz de resolver este
Gstuntz