Android Camera: намерение данных возвращает null

132

У меня есть приложение для Android, которое содержит несколько действий.

В одном из них я использую кнопку вызова камеры устройства:

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

В том же действии я вызываю OnActivityResultметод для результата изображения:

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

Проблема в том, что намерение dataимеет значение null, OnActivityResultметод переключается непосредственно на (resultCode == RESULT_CANCELED)объект, а приложение возвращается к предыдущему состоянию.

Как я могу исправить эту проблему, и после вызова камеры приложение вернется к текущему действию, содержащему объект ImageView, содержащий сделанный снимок?

Спасибо

soft_developer
источник
взгляните на этот ответ stackoverflow.com/questions/18120775/…
Правин Шарма

Ответы:

218

Приложение камеры Android по умолчанию возвращает ненулевое намерение только при передаче эскиза в возвращенном намерении. Если вы передадите EXTRA_OUTPUTURI для записи, он вернетnull намерение, и изображение будет в URI, который вы передали.

Вы можете проверить это, посмотрев исходный код приложения камеры на GitHub:

Я предполагаю, что либо вы как-то проходите мимо EXTRA_OUTPUT, либо приложение камеры на вашем телефоне работает иначе.

Брайан Слезинский
источник
12
Как мне исправить функциональность put Extra, которая дает ненулевое намерение в камере onActivityResult
Абдул Вахаб
Действительно полезный ответ. Хотел бы я принять его, чтобы другие могли найти решение более эффективно.
user1160020 03
1
Потрясающие ! Большое спасибо за объяснение :) +100
Мухаммад Рияз
Ссылка, которую вы разместили, больше не действительна.
4
Можем ли мы наследовать класс камеры и просто изменить этот метод, чтобы разрешить оба, или это сложнее?
Таннер Саммерс,
16

Я нашел простой ответ. оно работает!!

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

        }
    }

если вы хотите обрезать и масштабировать это изображение

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
источник
4
Он дает представление FileUriExposedExceptionо современных версиях SDK.
Сурадип Нанда
8

Я столкнулся с этой проблемой, значение intentне равно нулю, но информация, отправленная через intentнего, не получена вonActionActivit()

введите описание изображения здесь

Это лучшее решение с использованием 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);

  }

onActivityResult()получить растровое изображение , хранящееся на 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();
            }

        }
    }

введение к описанию водных изображений введение к описанию водных изображений

Посмотрите мой пример в github:

https://github.com/Jorgesys/TakePicture

Jorgesys
источник
6

Простое приложение для рабочей камеры, позволяющее избежать проблемы с нулевым намерением

- весь измененный код включен в этот ответ; близко к Android учебник

Я потратил много времени на эту проблему, поэтому решил создать учетную запись и поделиться с вами своими результатами.

Официальный учебник по Android «Фотографирование просто» оказался не совсем тем, что обещал. Предоставленный там код не работал на моем устройстве: Samsung Galaxy S4 Mini GT-I9195 под управлением Android версии 4.4.2 / KitKat / API Level 19.

Я понял, что основная проблема заключалась в следующей строке в методе, вызываемом при захвате фотографии ( dispatchTakePictureIntentв учебнике):

takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);

Это привело к тому, что намерение впоследствии было уловлено onActivityResult как нулевое.

Чтобы решить эту проблему, я почерпнул много вдохновения из более ранних ответов здесь и некоторых полезных сообщений на github (в основном это от deepwinter - большое ему спасибо; вы также можете проверить его ответ в тесно связанном посте ).

Следуя этим приятным советам, я выбрал стратегию удаления упомянутой putExtraстроки и выполнения соответствующих действий по возвращению сделанного снимка с камеры в методе onActivityResult (). Решающие строки кода для возврата растрового изображения, связанного с изображением, следующие:

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

Я создал образцовое приложение, в котором просто есть возможность делать снимки, сохранять их на SD-карте и отображать. Я думаю, что это может быть полезно для людей в той же ситуации, что и я, когда я наткнулся на эту проблему, поскольку текущие предложения помощи в основном относятся к довольно обширным сообщениям на github, которые делают то, о чем идет речь, но их не слишком легко контролировать для новичков, таких как меня. Что касается файловой системы, которую Android Studio создает по умолчанию при создании нового проекта, мне просто нужно было изменить три файла для моей цели:

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>

Обратите внимание, что решение, которое я нашел для проблемы, также привело к упрощению файла манифеста Android: изменения, предложенные в руководстве по Android с точки зрения добавления поставщика, больше не нужны, поскольку я не использую их в своем Java-коде. Следовательно, в файл манифеста нужно было добавить только несколько стандартных строк, в основном касающихся разрешений.

Также было бы полезно отметить, что автоимпорт Android Studio может не обрабатывать файлы java.text.SimpleDateFormatи java.util.Date. Мне пришлось импортировать их оба вручную.

HGM
источник
для этого решения требуется разрешение на ХРАНЕНИЕ, что не очень хороший подход.
shanraisshan
4
В вопросе четко указано, что параметр Intent с именем «data», переданный в onActivityResult (), имеет значение null, тогда как ваше решение продолжает действовать и пытается его использовать.
Макс
2

Наверное, потому что у вас было что-то подобное?

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

Однако вы не должны помещать дополнительный вывод в намерение, потому что тогда данные поступают в URI вместо переменной данных. По этой причине вы должны взять две линии посередине, чтобы у вас было

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

Вот почему у меня возникла проблема, надеюсь, это помогло.

Мариус Веселый
источник
12
Этот ответ вводит в заблуждение. Вы получите миниатюру вместо полноразмерной фотографии, не помещая MediaStore.EXTRA_OUTPUT. Это выделено в документах и ​​в других ответах.
user2417480
2
Абсолютно вводящий в заблуждение этот ответ.
IndexOutOfDevelopersException
это вводящий в заблуждение ответ.
Абдулла Танси
0

Следующий код работает для меня:

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

И вот результат:

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();
        }
    }
}
Манитоба
источник
Я тестировал его, но проблема та же :(
soft_developer
1
я получаю imageReturnedIntent.getData()значение null. Я создаю такое же намерение, как и ваше. Никаких лишних параметров не ставит.
Jaydip Kalkani
0

Чтобы получить доступ к камере, сделать снимки и установить ImageView на Android

Вы должны использовать Uri file = Uri.fromFile(getOutputMediaFile());зефир.

Используйте ссылку ниже, чтобы получить путь

https://androidkennel.org/android-camera-access-tutorial/

Эшвин Х
источник
0

Код Kotlin, который у меня работает:

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

И получаем Результат:

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

    }

}

и не забудьте указать код запроса:

companion object {
 const val PERMISSIONS_REQUEST_TAKE_PICTURE_CAMERA = 300
  }
Хамед Джалилиани
источник
0

После долгих попыток и изучения я смог понять это. Во-первых, переменные данные из Intent всегда будут иметь значение NULL, поэтому проверка на !nullприведет к сбою вашего приложения, пока вы передаете URI в startActivityForResult. Следуйте приведенному ниже примеру. Я буду использовать Котлин.

  1. Откройте намерение камеры

    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. Создайте createImageFile (). Но вы ДОЛЖНЫ сделать переменную imageFilePath глобальной. Пример того, как его создать, есть в официальной документации Android и довольно прост.

  3. Получить намерение

     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. Метод getBytesFromBitmap

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

Надеюсь, это поможет.

Gstuntz
источник
0

Когда мы будем захватывать изображение с камеры в Android, Uri или data.getdata()становится нулевым. у нас есть два решения для решения этой проблемы.

  1. Мы можем получить путь Uri из растрового изображения
  2. Мы можем получить путь Uri от курсора.

Я буду реализовывать здесь все методы, пожалуйста, внимательно посмотрите и прочтите это: -

Сначала я расскажу, как получить Uri из растрового изображения: Полный код:

Сначала мы захватим изображение через Intent, которое будет одинаковым для обоих методов, поэтому этот код я напишу только один раз здесь:

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

    }
});  

Теперь мы реализуем OnActivityResult :-( Это будет одинаково для обоих вышеупомянутых двух методов): -

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

Теперь мы создадим все вышеперечисленные методы для создания Uri из методов Image и Cursor через классы:

Теперь путь URI из растрового изображения

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 из Реальный путь сохраненного изображения

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);
}
Прадип Кумар
источник
-1

Когда мы захватываем изображение с камеры в Android, тогда Uriили data.getdata()становится нулевым. У нас есть два решения для решения этой проблемы.

  1. Получить путь Uri из растрового изображения
  2. Получить путь Uri от курсора.

Вот как получить Uri из растрового изображения. Сначала захватите изображение через Intent, которое будет одинаковым для обоих методов:

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

Теперь реализуйте OnActivityResult, что будет одинаково для обоих методов:

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

Теперь создайте все вышеперечисленные методы для создания Uri из методов Image и Cursor:

Путь Uri из растрового изображения:

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 из реального пути сохраненного изображения:

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);
}
Прадип Шеоран
источник
Кто-нибудь смог решить это? Пожалуйста, опубликуйте свое решение.
Документ