Mam aplikację na Androida, która zawiera wiele działań.
W jednym z nich używam przycisku, który wywoła aparat urządzenia:
public void onClick(View view) {
Intent photoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(photoIntent, IMAGE_CAPTURE);
}
W tym samym ćwiczeniu nazywam OnActivityResult
metodę na wynik obrazu:
@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();
}
}
}
Problem polega na tym, że intencja data
jest zerowa, a OnActivityResult
metoda zwraca się bezpośrednio do, (resultCode == RESULT_CANCELED)
a aplikacja wraca do poprzedniej aktywności.
Jak mogę naprawić ten problem i po wywołaniu kamery aplikacja wraca do bieżącej aktywności, która zawiera zdjęcie, ImageView
które będzie zawierało zrobione zdjęcie?
Dzięki
android
android-camera
android-imageview
soft_developer
źródło
źródło
Odpowiedzi:
Domyślna aplikacja aparatu dla systemu Android zwraca intencję inną niż zerowa tylko wtedy, gdy przekazuje miniaturę w zwróconej intencji. Jeśli przekażesz
EXTRA_OUTPUT
identyfikator URI do zapisu, zwróci onnull
intencję, a obraz znajduje się w przekazanym identyfikatorze URI.Możesz to sprawdzić, patrząc na kod źródłowy aplikacji aparatu na GitHub:
https://github.com/android/platform_packages_apps_camera/blob/gingerbread-release/src/com/android/camera/Camera.java#L1186
Bundle newExtras = new Bundle(); if (mCropValue.equals("circle")) { newExtras.putString("circleCrop", "true"); } if (mSaveUri != null) { newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri); } else { newExtras.putBoolean("return-data", true); }
Domyślam się, że albo mijasz się w
EXTRA_OUTPUT
jakiś sposób, albo aplikacja aparatu w telefonie działa inaczej.źródło
Znalazłem łatwą odpowiedź. to działa!!
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(); } } }
jeśli chcesz przyciąć i przeskalować ten obraz
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; }
źródło
FileUriExposedException
on nowoczesne wersje SDK.Doświadczyłem tego problemu,
intent
wartość nie jest zerowa, ale informacje wysłane przez tointent
nie są odbieraneonActionActivit()
To lepsze rozwiązanie przy użyciu metody 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()
dostać bitmapy przechowywanej przez 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(); } } }
Sprawdź mój przykład na github:
https://github.com/Jorgesys/TakePicture
źródło
Prosta działająca aplikacja aparatu, która pozwala uniknąć problemu z intencją zerową
- wszystkie zmienione kody zawarte w tej odpowiedzi; blisko samouczka Androida
Spędziłem dużo czasu nad tą kwestią, więc postanowiłem założyć konto i podzielić się z wami moimi wynikami.
Oficjalny samouczek Androida „Proste robienie zdjęć” okazał się niezupełnie zgodny z obietnicą. Podany tam kod nie działał na moim urządzeniu: Samsung Galaxy S4 Mini GT-I9195 z systemem Android w wersji 4.4.2 / KitKat / API Level 19.
Doszedłem do wniosku, że głównym problemem był następujący wiersz w metodzie wywołanej podczas przechwytywania zdjęcia (
dispatchTakePictureIntent
w samouczku):takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
Doprowadziło to do tego, że intencja została następnie przechwycona, ponieważ
onActivityResult
była nieważna.Aby rozwiązać ten problem, zaczerpnąłem wiele inspiracji z wcześniejszych odpowiedzi tutaj i kilku pomocnych postów na githubie (głównie ten od deepwinter - wielkie dzięki dla niego; możesz również sprawdzić jego odpowiedź w blisko powiązanym poście ).
Kierując się tymi przyjemnymi radami, wybrałem strategię usuwania wspomnianej
putExtra
linii i wykonując odpowiednią czynność polegającą na odzyskaniu zrobionego zdjęcia z kamery w ramach metody onActivityResult (). Decydujące wiersze kodu umożliwiające odzyskanie mapy bitowej skojarzonej z obrazem to:Uri uri = intent.getData(); Bitmap bitmap = null; try { bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), uri); } catch (IOException e) { e.printStackTrace(); }
Stworzyłem przykładową aplikację, która po prostu ma możliwość zrobienia zdjęcia, zapisania go na karcie SD i wyświetlenia. Myślę, że może to być pomocne dla osób w tej samej sytuacji co ja, kiedy natknąłem się na ten problem, ponieważ obecne sugestie pomocy odnoszą się głównie do dość obszernych postów na githubie, które robią to, o czym mowa, ale nie są zbyt łatwe do nadzorowania dla początkujących, takich jak mnie. Jeśli chodzi o system plików, który Android Studio tworzy domyślnie podczas tworzenia nowego projektu, musiałem tylko zmienić trzy pliki w swoim celu:
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>
Zwróć uwagę, że rozwiązanie problemu, które znalazłem, doprowadziło również do uproszczenia pliku manifestu Androida: zmiany sugerowane przez samouczek Androida w zakresie dodawania dostawcy nie są już potrzebne, ponieważ nie używam żadnego w moim kodzie Java. Dlatego do pliku manifestu trzeba było dodać tylko kilka standardowych wierszy - głównie dotyczących uprawnień.
Warto również zwrócić uwagę, że automatyczny import Android Studio może nie być w stanie obsłużyć
java.text.SimpleDateFormat
ijava.util.Date
. Musiałem zaimportować oba ręcznie.źródło
Prawdopodobnie dlatego, że miałeś coś takiego?
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); Uri fileUri = CommonUtilities.getTBCameraOutputMediaFileUri(); takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri); startActivityForResult(takePictureIntent, 2);
Nie wolno jednak umieszczać dodatkowych danych wyjściowych w intencji, ponieważ wtedy dane trafiają do identyfikatora URI zamiast do zmiennej danych. Z tego powodu musisz wyciągnąć dwie środkowe linie, aby mieć
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); startActivityForResult(takePictureIntent, 2);
To właśnie spowodowało ten problem, mam nadzieję, że pomogło.
źródło
U mnie działa następujący kod:
Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE); startActivityForResult(cameraIntent, 2);
A oto wynik:
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(); } } }
źródło
imageReturnedIntent.getData()
wartość null. Tworzę zamiar taki sam jak twój. Nie dodaje żadnych dodatkowych parametrów.Aby uzyskać dostęp do aparatu i robić zdjęcia oraz ustawić ImageView na Androidzie
Musisz użyć
Uri file = Uri.fromFile(getOutputMediaFile());
do marshmallow.Użyj poniższego linku, aby uzyskać ścieżkę
https://androidkennel.org/android-camera-access-tutorial/
źródło
Kod Kotlin, który działa dla mnie:
private fun takePhotoFromCamera() { val intent = Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE) startActivityForResult(intent, PERMISSIONS_REQUEST_TAKE_PICTURE_CAMERA) }
I uzyskaj wynik:
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 ") } } }
i nie zapomnij zadeklarować kodu żądania:
companion object { const val PERMISSIONS_REQUEST_TAKE_PICTURE_CAMERA = 300 }
źródło
Po wielu próbach i nauce udało mi się to rozgryźć. Po pierwsze, zmienne dane z Intent zawsze będą miały wartość NULL, więc sprawdzanie, czy
!null
aplikacja nie działa, spowoduje awarię aplikacji na czas przekazywania identyfikatora URI do startActivityForResult. Będę używać Kotlina.Otwórz zamiar aparatu
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); } } }
} `
Utwórz createImageFile (), ale MUSISZ ustawić zmienną imageFilePath jako globalną. Przykład, jak go utworzyć, znajduje się w oficjalnej dokumentacji Androida i jest całkiem prosty
Uzyskaj zamiar
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) }}
Metoda getBytesFromBitmap
fun getBytesFromBitmap(bitmap:Bitmap):ByteArray{ var stream=ByteArrayOutputStream() bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream); return stream.toByteArray(); }
Mam nadzieję, że to pomoże.
źródło
Kiedy przechwycimy obraz z aparatu w systemie Android, Uri lub stanie się
data.getdata()
zerowe. Mamy dwa rozwiązania tego problemu.Wdrażam tutaj wszystkie metody, proszę uważnie przyjrzeć się i przeczytać: -
Najpierw powiem, jak uzyskać Uri z obrazu bitmapowego: Kompletny kod to:
Najpierw przechwycimy obraz przez Intent, który będzie taki sam dla obu metod, więc ten kod napiszę tylko raz tutaj:
// 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); } } });
Teraz zaimplementujemy OnActivityResult :-( To będzie takie samo dla obu powyższych 2 metod): -
@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(); } }
Teraz stworzymy wszystkie powyższe metody do tworzenia Uri z metod Image i Cursor poprzez klasy:
Teraz ścieżka URI z obrazu bitmapowego
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 z rzeczywistej ścieżki zapisanego obrazu
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); }
źródło
Kiedy przechwytujemy obraz z aparatu w Androidzie, wtedy
Uri
lubdata.getdata()
staje się zerowy. Mamy dwa rozwiązania tego problemu.Oto jak pobrać Uri z obrazu bitmapowego. Najpierw przechwyć obraz za pomocą zamiaru, który będzie taki sam dla obu metod:
// 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); } } });
Teraz zaimplementuj
OnActivityResult
, co będzie takie samo dla obu metod:@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(); } }
Teraz utwórz wszystkie powyższe metody, aby utworzyć Uri z metod Image i Cursor:
Ścieżka Uri z obrazu bitmapowego:
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 z rzeczywistej ścieżki zapisanego obrazu:
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); }
źródło