Android ACTION_IMAGE_CAPTURE Намір


163

Ми намагаємось використовувати нативну програму камери, щоб дати змогу користувачеві сфотографуватись. Це добре працює, якщо ми не залишимо EXTRA_OUTPUT extraі повернемо маленьке зображення Bitmap. Однак, якщо ми putExtra(EXTRA_OUTPUT,...)задумали перед його запуском, все працює, поки ви не спробуєте натиснути кнопку «Ок» у додатку для камери. Кнопка «Ок» просто нічого не робить. Додаток для камери залишається відкритим, і нічого не блокується. Ми можемо скасувати його, але файл ніколи не записується. Що саме ми повинні зробити, щоб ACTION_IMAGE_CAPTUREзаписати знімок, зроблений у файл?

Редагувати: це робиться через MediaStore.ACTION_IMAGE_CAPTUREнаміри, просто щоб було зрозуміло

Відповіді:


144

це добре задокументована помилка в деяких версіях Android. тобто на Google-накопичувачах для андроїд збирання зображень не працює як задокументовано. що я зазвичай використовував щось подібне у класі комунальних послуг.

public boolean hasImageCaptureBug() {

    // list of known devices that have the bug
    ArrayList<String> devices = new ArrayList<String>();
    devices.add("android-devphone1/dream_devphone/dream");
    devices.add("generic/sdk/generic");
    devices.add("vodafone/vfpioneer/sapphire");
    devices.add("tmobile/kila/dream");
    devices.add("verizon/voles/sholes");
    devices.add("google_ion/google_ion/sapphire");

    return devices.contains(android.os.Build.BRAND + "/" + android.os.Build.PRODUCT + "/"
            + android.os.Build.DEVICE);

}

тоді, коли я запускаю захоплення зображень, я створюю намір, який перевіряє на помилку.

Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
if (hasImageCaptureBug()) {
    i.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File("/sdcard/tmp")));
} else {
    i.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
}
startActivityForResult(i, mRequestCode);

то в діяльності, до якої я повертаюся, я роблю різні речі на основі пристрою.

protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
     switch (requestCode) {
         case GlobalConstants.IMAGE_CAPTURE:
             Uri u;
             if (hasImageCaptureBug()) {
                 File fi = new File("/sdcard/tmp");
                 try {
                     u = Uri.parse(android.provider.MediaStore.Images.Media.insertImage(getContentResolver(), fi.getAbsolutePath(), null, null));
                     if (!fi.delete()) {
                         Log.i("logMarker", "Failed to delete " + fi);
                     }
                 } catch (FileNotFoundException e) {
                     e.printStackTrace();
                 }
             } else {
                u = intent.getData();
            }
    }

це заощаджує вам, що потрібно писати нову програму для камери, але і цей код не великий. великі проблеми є

  1. ви ніколи не отримуєте повнорозмірні зображення з пристроїв із помилкою. Ви отримуєте зображення розміром 512 пікселів, які вставляються в постачальник вмісту зображень. на пристроях без помилок все працює як документ, ви отримуєте велику нормальну картину.

  2. ви повинні підтримувати список. як написано, для пристроїв можна мигнути версією андроїда (скажімо, цианогенмоди ), у якому виправлена ​​помилка. якщо це станеться, ваш код вийде з ладу. виправлення полягає у використанні всього відбитка пристрою.


21
на Galaxy S: intent.getData () повертає нуль, і більше того, додаток камери на Galaxy s вставляє нову фотографію в галерею самостійно, але урі не повертається Отже, якщо ви вставите фотографію з файлу в галерею, там буде дублюватися фотографія.
Арсеній

1
агов моє пристрій не перераховане тут і я реалізував Ю.Р. way.but мого додаток по- , як і раніше падає я надягаю KNW в reason.please допомогу мені
Geetanjali

використовуючи цей код на пристроях droid-x та sony xpheria. Обидва пристрої повертають значення наміру як нульове. droidx також повертає код результату як 0, тоді як xpheria повертає код результату як -1. Хтось знає, чому саме так має бути.
rOrlig

5
Посилання на "добре задокументовану помилку", яка знаходиться на початку відповіді Янокви, невірна. Ця помилка стосується виклику програми на камеру без putExtra (EXTRA_OUTPUT, ...)
Френк Харпер

code.google.com/p/android/isissue/detail?id=1480 Ну, як ви це поясните?
Pencilcheck

50

Я знаю, що на це відповіли і раніше, але я знаю, що багато людей натрапляють на це, тому я збираюся додати коментар.

У мене така сама проблема трапилася на моєму Nexus One. Це було з файлу, який не існує на диску до запуску програми для камери. Тому я переконався, що файл, який існував до запуску програми для камери. Ось приклад коду, який я використав:

String storageState = Environment.getExternalStorageState();
        if(storageState.equals(Environment.MEDIA_MOUNTED)) {

            String path = Environment.getExternalStorageDirectory().getName() + File.separatorChar + "Android/data/" + MainActivity.this.getPackageName() + "/files/" + md5(upc) + ".jpg";
            _photoFile = new File(path);
            try {
                if(_photoFile.exists() == false) {
                    _photoFile.getParentFile().mkdirs();
                    _photoFile.createNewFile();
                }

            } catch (IOException e) {
                Log.e(TAG, "Could not create file.", e);
            }
            Log.i(TAG, path);

            _fileUri = Uri.fromFile(_photoFile);
            Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE );
            intent.putExtra( MediaStore.EXTRA_OUTPUT, _fileUri);
            startActivityForResult(intent, TAKE_PICTURE);
        }   else {
            new AlertDialog.Builder(MainActivity.this)
            .setMessage("External Storeage (SD Card) is required.\n\nCurrent state: " + storageState)
            .setCancelable(true).create().show();
        }

Спочатку я створюю унікальне (дещо) ім'я файлу за допомогою хеша MD5 і вкладаю його у відповідну папку. Потім я перевіряю, чи існує він (не повинен, але все-таки його слід перевірити). Якщо його не існує, я отримую батьківський dir (папку) і створюю до нього ієрархію папок (тому, якщо папок, що ведуть до місця розташування файлу, не існує, вони з'являться після цього рядка. Потім після цього Я створюю файл. Після створення файлу я отримую Uri і передаю його до наміру, а потім кнопка «ОК» працює як очікувалося, і все золоте.

Тепер, коли на додатку камери натиснуто кнопку Гаразд, файл буде присутній у вказаному місці. У цьому прикладі це /sdcard/Android/data/com.example.myapp/files/234asdioue23498ad.jpg

Немає необхідності копіювати файл у "onActivityResult", як розміщено вище.


13
Переконайтеся, що у вас є <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />ваш маніфест, якщо ви використовуєте новіший, ніж Android 1.5 - витратили кілька годин на налагодження матеріалів камери, і це не було включено, тому мої заощадження завжди будуть виходити з ладу.
лебедя

Це добре для мене, але кожні 10 зображень або близько того, порожній файл не перезаписується, і я закінчую файлом 0 байтів, де має бути зображення. Це дуже дратівливо і важко було відстежити.
joey_g216

2
на деяких пристроях, таких як Nexus 7 з jelly bean, потрібно змінити Environment.getExternalStorageDirectory (). getName (), щоб використовувати .getAbsolutePath () замість .getName ()
Кіт

35

Я пережив ряд стратегій зйомки фотографій, і завжди, здається, є випадок, платформа чи певні пристрої, де деякі або всі перераховані вище стратегії виходять з ладу несподіваними способами. Мені вдалося знайти стратегію, яка використовує код генерації URI нижче, який, здається, працює в більшості, якщо не у всіх випадках.

mPhotoUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 
            new ContentValues());
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, mPhotoUri);
startActivityForResult(intent,CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE_CONTENT_RESOLVER);

Для подальшого сприяння обговоренню та допомозі новачкам я створив зразок / тестовий додаток, який показує кілька різних стратегій реалізації фотозйомки. Внески інших реалізацій, безумовно, рекомендується додати до обговорення.

https://github.com/deepwinter/AndroidCameraTester


Це було чудово. Я ще перевіряв це на всіх своїх пристроях, але я припускаю, що ви це зробили вже зовсім небагато. На цю тему так багато шуму, і ця конкретна відповідь така проста і чиста. Наразі працює на Nexus 5, саме там я вперше отримав багато звітів про те, що ACTION_IMAGE_CAPTURE не працював без EXTRA_OUTPUT. Я сподіваюся, що це працює в усьому світі, але поки що добре. Thx
Багатий

1
@deepwinter - Дякую, сер. Я витягнув волосся, але ваше рішення щодо вмісту добре працює на всіх проблемних сценаріях.
Біллі Кувер

Добре спізнився на цю партію, але це було єдиним рішенням для мене. Дякую купу!
StackJP

чи можу я якось відновити MediaStore.EXTRA_OUTPUT onActivityResultабо мені справді потрібно пам'ятати про це? Мені доведеться наполегливо зберігати його, щоб моє додаток навіть запам’ятало його, якщо воно буде знищене під час фотографування…
prom85

Він не працює на samsung galaxy note 3 з lolipop: ava.lang.NullPointerException: спроба викликати віртуальний метод 'java.lang.String android.net.Uri.getScheme ()' на нульовій посиланні на об'єкт
Ігор Янкович

30

У мене була така ж проблема, коли кнопка ОК у додатку для камери нічого не робила, як на емуляторі, так і на Nexus.

Проблема усунулася після введення безпечного імені файлу без пробілів, без спеціальних символів. MediaStore.EXTRA_OUTPUT Також, якщо ви вказуєте файл, який знаходиться у ще не створеному каталозі, його потрібно створити спочатку. Додаток для камери не робить mkdir для вас.


1
І переконайтеся, що ви використовуєте mkdirs()замість того, mkdir()якщо ваш шлях має більше одного рівня каталогів, і ви хочете, щоб усі вони були створені ( mkdirстворюється лише останній, каталог "leaf"). У мене були деякі проблеми з цим, і ця відповідь мені допомогла.
Діана

Чи можемо ми використовувати прості часові позначки ?? чи може також зробити деякі помилки ??
благаєте

@ user2247689 Я не впевнений у назві файлу, який ви створили за допомогою "простих часових позначок", але поки вони не містять спеціальних символів, не дозволених форматом FAT на SD-картці, я думаю, це повинно бути нормально.
Єнчі

28

Описаний вами робочий процес повинен працювати так, як ви описали. Це може допомогти, якщо ви могли б показати нам код навколо створення наміру. Загалом, наступна схема повинна дозволяти вам робити те, що ви намагаєтесь.

private void saveFullImage() {
  Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
  File file = new File(Environment.getExternalStorageDirectory(), "test.jpg");
  outputFileUri = Uri.fromFile(file);
  intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
  startActivityForResult(intent, TAKE_PICTURE);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  if ((requestCode == TAKE_PICTURE) && (resultCode == Activity.RESULT_OK)) {
    // Check if the result includes a thumbnail Bitmap
    if (data == null) {    
      // TODO Do something with the full image stored
      // in outputFileUri. Perhaps copying it to the app folder
    }
  }
}

Зауважте, що саме Camera Activity буде створювати та зберігати файл, а він насправді не є частиною вашої програми, тому він не матиме дозволу на запис у папку додатків. Щоб зберегти файл у папці програми, створіть тимчасовий файл на SD-картці та перенесіть його у папку додатка на onActivityResultобробці.


Це / повинно / працювати, але кнопка "ОК" у програмі для камери просто нічого не робить. У нас це було написано на SD-карту, тому я підозрюю, що це проблема з дозволом на файли (хоча я вважав, що програма автоматично має дозволи на запис до її особистого каталогу). Дякую за допомогу!
Дрю

1
Ваш додаток має дозвіл на запис до особистого каталогу - додаток для камери (який робить знімок) не робить. Ви можете перемістити файл у програмі onActivityResult, як це є у вашій програмі. Крім того, ви могли самостійно реалізувати камеру, але це здається непосильним.
Reto Meier

Повторне додаток для камери, здається, є загальним, але нудним підходом ... Коли ми використовуємо getDir ("образи", MODE_WORLD_WRITEABLE), чи не дозвіл на цей файл поширюється на будь-який файл, який ми говоримо йому для створення у цьому каталозі?
Дрю

Не обов'язково. Дозвіл призначений для папки, не обов'язково для файлів, що знаходяться в ній.
Reto Meier

Я протестував код на пристроях Android з Android 2.2 і новішими, і всі вони зберегли зображення до наданого шляху. Тому я здогадуюсь, що це має бути стандартна поведінка для отримання зображень.
Януш

7

Щоб продовжити коментар Yenchi вище, кнопка OK також нічого не зробить, якщо програма для камери не зможе записатись у відповідний каталог.

Це означає, що ви не можете створити файл у тому місці, яке може записуватися лише вашою програмою (наприклад, щось під " getCacheDir())Щось під" getExternalFilesDir()повинно працювати, однак.

Було б добре, якби програма камери надрукувала повідомлення про помилку до журналів, якщо воно не могло записати у вказаний EXTRA_OUTPUTшлях, але я не знайшов його.


4

щоб камера записувала на sdcard, але зберігаю в новому альбомі в додатку галереї, я використовую це:

 File imageDirectory = new File("/sdcard/signifio");
          String path = imageDirectory.toString().toLowerCase();
           String name = imageDirectory.getName().toLowerCase();


            ContentValues values = new ContentValues(); 
            values.put(Media.TITLE, "Image"); 
            values.put(Images.Media.BUCKET_ID, path.hashCode());
            values.put(Images.Media.BUCKET_DISPLAY_NAME,name);

            values.put(Images.Media.MIME_TYPE, "image/jpeg");
            values.put(Media.DESCRIPTION, "Image capture by camera");
           values.put("_data", "/sdcard/signifio/1111.jpg");
         uri = getContentResolver().insert( Media.EXTERNAL_CONTENT_URI , values);
            Intent i = new Intent("android.media.action.IMAGE_CAPTURE"); 

            i.putExtra(MediaStore.EXTRA_OUTPUT, uri);

            startActivityForResult(i, 0); 

Зверніть увагу, що вам потрібно буде кожен раз генерувати унікальне ім’я файлу та замінювати те, що я написав. Це було перевірено Nexus 1. uri оголошується в приватному класі, тому за результатами діяльності я можу завантажити зображення з uri в imageView для попереднього перегляду, якщо це потрібно.


звідки походить стовпець "_дані"? чи немає для нього постійної?
Маттіас

3
ах, це developer.android.com/reference/android/provider/… Ви дійсно не повинні використовувати магічні рядки в коді.
Маттіас

1

У мене була та сама проблема, і я її виправив із наступним:

Проблема полягає в тому, що коли ви вказуєте файл, до якого має доступ лише ваш додаток (наприклад, зателефонувавши за телефоном getFileStreamPath("file"); )

Ось чому я просто переконався, що даний файл дійсно існує і що ВСЕ кожен має доступ до нього.

Intent intent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
File outFile = getFileStreamPath(Config.IMAGE_FILENAME);
outFile.createNewFile();
outFile.setWritable(true, false);
intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT,Uri.fromFile(outFile));
startActivityForResult(intent, 2);

Таким чином, додаток для камери має доступ для запису до даної Урі, і кнопка ОК працює чудово :)


AndroidRuntime (2.1 емулятор) кидає NoSuchMethodError: java.io.File.setWritable
jwadsack

1

Я рекомендую вам дотримуватися тренувального поста на андроїд для зйомки фотографії. Вони показують на прикладі, як робити маленькі та великі фотографії. Ви також можете завантажити вихідний код звідси


0

Я створив просту бібліотеку, яка керуватиме вибором зображень з різних джерел (Галерея, Камера), можливо, збережіть її в якомусь місці (SD-карта чи внутрішня пам'ять) і поверніть зображення назад, тому, будь ласка, вільно використовувати його та вдосконалити - Android-ImageChooser .


ваше посилання більше не працює !! чому я бачив це 2 тижні тому, і я хотів його використовувати :(
Мохаммед Ерсан

0

Як зазначив Правен, файл повинен записувати камеру .

У своєму використанні я хотів зберегти файл у внутрішній пам’яті. Я зробив це з:

Intent i = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (i.resolveActivity(getPackageManager()!=null)){
    try{
        cacheFile = createTempFile("img",".jpg",getCacheDir());
        cacheFile.setWritavle(true,false);
    }catch(IOException e){}
    if(cacheFile != null){
        i.putExtra(MediaStore.EXTRA_OUTPUT,Uri.fromFile(cacheFile));
        startActivityForResult(i,REQUEST_IMAGE_CAPTURE);
    }
}

Ось cacheFileглобальний файл, який використовується для позначення записаного файлу. Тоді в методі результатів повернутий намір є нульовим. Тоді спосіб обробки наміру виглядає так:

protected void onActivityResult(int requestCode,int resultCode,Intent data){
    if(requestCode != RESULT_OK){
        return;
    }
    if(requestCode == REQUEST_IMAGE_CAPTURE){
        try{
            File output = getImageFile();
            if(output != null && cacheFile != null){
                copyFile(cacheFile,output);

                //Process image file stored at output

                cacheFile.delete();
                cacheFile=null;
            }
        }catch(IOException e){}
    }
}

Ось getImageFile()утилітний метод для назви та створення файлу, в якому має зберігатися зображення, та copyFile()метод копіювання файлу.


0

Ви можете використовувати цей код

private Intent getCameraIntent() {

    PackageManager packageManager = mContext.getPackageManager();
    List<ApplicationInfo> list = packageManager.getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES);
    Intent main = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
    List<ResolveInfo> launchables = packageManager.queryIntentActivities(main, 0);
    if (launchables.size() == 1)
        return packageManager.getLaunchIntentForPackage(launchables.get(0).activityInfo.packageName);
    else
        for (int n = 0; n < list.size(); n++) {
            if ((list.get(n).flags & ApplicationInfo.FLAG_SYSTEM) == 1) {
                Log.d("TAG", "Installed Applications  : " + list.get(n).loadLabel(packageManager).toString());
                Log.d("TAG", "package name  : " + list.get(n).packageName);
                String defaultCameraPackage = list.get(n).packageName;


                if (launchables.size() > 1)
                    for (int i = 0; i < launchables.size(); i++) {
                        if (defaultCameraPackage.equals(launchables.get(i).activityInfo.packageName)) {
                            return packageManager.getLaunchIntentForPackage(defaultCameraPackage);
                        }
                    }
            }
        }
    return null;
}

0

Вирішити цю проблему за допомогою коду результату діяльності дуже просто. Спробуйте скористатися цим методом

if (reqCode == RECORD_VIDEO) {
   if(resCode == RESULT_OK) {
       if (uri != null) {
           compress();
       }
    } else if(resCode == RESULT_CANCELED && data!=null){
       Toast.makeText(MainActivity.this,"No Video Recorded",Toast.LENGTH_SHORT).show();
   }
}

Вищенаведений неповний фрагмент не заслуговує на простоту, яку він вимагав. Звичайно, для незнайомого мандрівника є приховані складності. Такі як сканування на наявність або відсутність файлу. Наприклад, якщо програма доступна загальнодоступному чи приватному застосуванню. Наприклад, потреба в постачальнику послуг або отримання розміру мініатюри. Це інформаційні прогалини, які слід знати про необережного мандрівника.
truthadjustr

0
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode == RESULT_CANCELED)
    {
        //do not process data, I use return; to resume activity calling camera intent
        enter code here
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.