App fotocamera funzionante semplice che evita il problema di intenti nulli
- tutto il codice modificato incluso in questa risposta; vicino al tutorial Android
Ho trascorso molto tempo su questo problema, quindi ho deciso di creare un account e condividere i miei risultati con te.
Il tutorial Android ufficiale "Scattare foto semplicemente" si è rivelato non del tutto promesso. Il codice fornito non funzionava sul mio dispositivo: un Samsung Galaxy S4 Mini GT-I9195 con Android versione 4.4.2 / KitKat / API Level 19.
Ho capito che il problema principale era la seguente riga nel metodo invocato durante l'acquisizione della foto ( dispatchTakePictureIntent
nel tutorial):
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
Ne è risultata l'intenzione successivamente catturata onActivityResult
dall'essere nulla.
Per risolvere questo problema, ho tratto molta ispirazione dalle risposte precedenti qui e da alcuni post utili su github (principalmente questo di deepwinter - un grande ringraziamento a lui; potresti voler controllare la sua risposta anche su un post strettamente correlato ).
Seguendo questi piacevoli consigli, ho scelto la strategia di eliminare la putExtra
linea menzionata e fare la cosa corrispondente per recuperare la foto scattata dalla fotocamera con il metodo onActivityResult (). Le linee decisive di codice per recuperare la bitmap associata all'immagine sono:
Uri uri = intent.getData();
Bitmap bitmap = null;
try {
bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), uri);
} catch (IOException e) {
e.printStackTrace();
}
Ho creato un'app esemplare che ha solo la possibilità di scattare una foto, salvarla sulla scheda SD e visualizzarla. Penso che questo potrebbe essere utile per le persone nella mia stessa situazione quando mi sono imbattuto in questo problema, poiché gli attuali suggerimenti di aiuto si riferiscono principalmente a post di github piuttosto estesi che fanno la cosa in questione ma non sono troppo facili da supervisionare per i neofiti come me. Per quanto riguarda il file system creato da Android Studio per impostazione predefinita durante la creazione di un nuovo progetto, ho dovuto modificare tre file per il mio scopo:
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>
Nota che la soluzione che ho trovato per il problema ha anche portato a una semplificazione del file manifest di Android: le modifiche suggerite dal tutorial Android in termini di aggiunta di un provider non sono più necessarie poiché non ne sto usando alcuna nel mio codice Java. Quindi, solo poche righe standard, principalmente relative alle autorizzazioni, dovevano essere aggiunte al file manifest.
Potrebbe inoltre essere utile sottolineare che l'importazione automatica di Android Studio potrebbe non essere in grado di gestirla java.text.SimpleDateFormat
e java.util.Date
. Ho dovuto importarli entrambi manualmente.