Простое приложение для рабочей камеры, позволяющее избежать проблемы с нулевым намерением
- весь измененный код включен в этот ответ; близко к 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
. Мне пришлось импортировать их оба вручную.