Uri изображения не отображает изображения в ImageView на некоторых устройствах android

у меня есть ImageView. Когда вы нажимаете на ImageView, он открывает галерею, и вы выбираете изображение и показываете его на ImageView. У меня есть условие, что когда я закрываю свое приложение, а затем открываю его, изображение сохранится там . Поэтому для этого я сохраняю Uri изображения на sharedprefrence. И при открытии приложения я получаю тот же Uri и пытается отобразить изображение на imageView.

однако в некоторых телефонах-изображение выглядит идеально, как Mi (Lollipop), Samsung (KitKat) но это не отображается в телефонах типа-Motorola (Marshmallow) ,One Plus One (Marshmallow). Есть идеи, почему это происходит ? Вот мой код

для сбора изображения, которое я использую

Intent intent=new Intent();intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent, "Select Picture"), Constants.SELECT_PICTURE);

и на onActivityResult () код

 @Override
 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
     super.onActivityResult(requestCode, resultCode, data);

     if(requestCode==Constants.SELECT_PICTURE && resultCode==RESULT_OK && null!=data) {
         Uri uri=data.getData();
         Picasso.with(UsersProfileActivity.this)
             .load(uri)
             .centerCrop()
             .resize(200,200)
             .into(img_photo);

         // This profile image i am storing into a sharedpreference        
         profile_image=uri.toString();
     }

и при извлечении из sharedprefrence я конвертирую строку в uri с помощью Uri.parse(profile_image)

однако, если я замечу, uri возвращается для разных телефонов android следующим образом

Mi(Lollipop)- Uri=content://media/external/images/media/12415
samsung(KitKat)-Uri=content://media/external/images/media/786
Motorola(Marshmallow)- Uri=content://com.android.providers.media.documents/document/image%3A30731
One Plus One (Marshmallow)- Uri=content://com.android.providers.media.documents/document/image%3A475

следовательно, когда содержимое uri is-content: / / media / external/images/ media / is отображение изображения на ImageView идеально, а в других случаях это не

11 ответов


попробуй такое

я разработал и tested и работает на

  1. Lenovo K3 Примечание (Зефир)
  2. Motorola (Леденец)
  3. Samsung (KitKat)

в своем MainActivity.java добавить

    profilePicture = (ImageView)findViewById(R.id.imgProfilePicture);
    sharedPreferences = getSharedPreferences(getString(R.string.app_name)+"_ProfileDetails",MODE_PRIVATE);

    // get uri from shared preference
    String profilePicUri = sharedPreferences.getString("profilePicUrl","");

    // if uri not found
    if (profilePicUri.equalsIgnoreCase("")){
        // code for open gallery to select image
        Intent intent;
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
            intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
            intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
            intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
        }else{
            intent = new Intent(Intent.ACTION_GET_CONTENT);
        }
        intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        intent.setType("image/*");

        startActivityForResult(Intent.createChooser(intent, "Select Picture"), SELECT_PICTURE_CONSTANT);
    }else{
        try{
            final int takeFlags =  (Intent.FLAG_GRANT_READ_URI_PERMISSION
                    | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
            // Check for the freshest data.
            getContentResolver().takePersistableUriPermission(Uri.parse(profilePicUri), takeFlags);
            // convert uri to bitmap
            Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), Uri.parse(profilePicUri));
            // set bitmap to imageview
            profilePicture.setImageBitmap(bitmap);
        }
        catch (Exception e){
            //handle exception
            e.printStackTrace();
        }
    }

Теперь Ручка onActivityResult.

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == SELECT_PICTURE_CONSTANT && resultCode == RESULT_OK && null != data) {
        Uri uri = data.getData();
        // This profile image i am storing into a sharedpreference
        SharedPreferences.Editor editor = sharedPreferences.edit();
        // save uri to shared preference
        editor.putString("profilePicUrl",uri.toString());
        editor.commit();
        profilePicture.setImageURI(uri);
    }
}

в своем Добавить permission

<uses-permission android:name="android.permission.MANAGE_DOCUMENTS"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

Примечание:

Assuming вы добавили код take permission на Marshmallow

результат Uri

lenovo K3 Примечание:content://com.android.externalstorage.documents/document/primary%3ADCIM%2FCamera%2FIMG_20160606_212815.jpg

samsung: content://com.android.providers.media.documents/document/image%3A2086

motorola:content://com.android.providers.media.documents/document/image%3A15828


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

package fandooo.com.fandooo.util;

import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;

public class ImageFilePath {

    /**
     * Method for return file path of Gallery image
     * 
     * @param context
     * @param uri
     * @return path of the selected image file from gallery
     */
    public static String getPath(final Context context, final Uri uri) {

        // check here to KITKAT or new version
        final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;

        // DocumentProvider
        if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {

            // ExternalStorageProvider
            if (isExternalStorageDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];

                if ("primary".equalsIgnoreCase(type)) {
                    return Environment.getExternalStorageDirectory() + "/"
                            + split[1];
                }
            }
            // DownloadsProvider
            else if (isDownloadsDocument(uri)) {

                final String id = DocumentsContract.getDocumentId(uri);
                final Uri contentUri = ContentUris.withAppendedId(
                        Uri.parse("content://downloads/public_downloads"),
                        Long.valueOf(id));

                return getDataColumn(context, contentUri, null, null);
            }
            // MediaProvider
            else if (isMediaDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];

                Uri contentUri = null;
                if ("image".equals(type)) {
                    contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
                } else if ("video".equals(type)) {
                    contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
                } else if ("audio".equals(type)) {
                    contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
                }

                final String selection = "_id=?";
                final String[] selectionArgs = new String[] { split[1] };

                return getDataColumn(context, contentUri, selection,
                        selectionArgs);
            }
        }
        // MediaStore (and general)
        else if ("content".equalsIgnoreCase(uri.getScheme())) {

            // Return the remote address
            if (isGooglePhotosUri(uri))
                return uri.getLastPathSegment();

            return getDataColumn(context, uri, null, null);
        }
        // File
        else if ("file".equalsIgnoreCase(uri.getScheme())) {
            return uri.getPath();
        }

        return null;
    }

    /**
     * Get the value of the data column for this Uri. This is useful for
     * MediaStore Uris, and other file-based ContentProviders.
     * 
     * @param context
     *            The context.
     * @param uri
     *            The Uri to query.
     * @param selection
     *            (Optional) Filter used in the query.
     * @param selectionArgs
     *            (Optional) Selection arguments used in the query.
     * @return The value of the _data column, which is typically a file path.
     */
    public static String getDataColumn(Context context, Uri uri,
                                       String selection, String[] selectionArgs) {

        Cursor cursor = null;
        final String column = "_data";
        final String[] projection = { column };

        try {
            cursor = context.getContentResolver().query(uri, projection,
                    selection, selectionArgs, null);
            if (cursor != null && cursor.moveToFirst()) {
                final int index = cursor.getColumnIndexOrThrow(column);
                return cursor.getString(index);
            }
        } finally {
            if (cursor != null)
                cursor.close();
        }
        return null;
    }

    /**
     * @param uri
     *            The Uri to check.
     * @return Whether the Uri authority is ExternalStorageProvider.
     */
    public static boolean isExternalStorageDocument(Uri uri) {
        return "com.android.externalstorage.documents".equals(uri
                .getAuthority());
    }

    /**
     * @param uri
     *            The Uri to check.
     * @return Whether the Uri authority is DownloadsProvider.
     */
    public static boolean isDownloadsDocument(Uri uri) {
        return "com.android.providers.downloads.documents".equals(uri
                .getAuthority());
    }

    /**
     * @param uri
     *            The Uri to check.
     * @return Whether the Uri authority is MediaProvider.
     */
    public static boolean isMediaDocument(Uri uri) {
        return "com.android.providers.media.documents".equals(uri
                .getAuthority());
    }

    /**
     * @param uri
     *            The Uri to check.
     * @return Whether the Uri authority is Google Photos.
     */
    public static boolean isGooglePhotosUri(Uri uri) {
        return "com.google.android.apps.photos.content".equals(uri
                .getAuthority());
    }
}

используйте getPath, чтобы найти точный путь к файлу. Это простой способ справиться с проблемой, как вы определили. Пожалуйста, попробуйте и дайте мне знать, если вам нужна дополнительная помощь :)


цитирую 4.4 обзор API:

'в предыдущих версиях Android, Если вы хотите, чтобы ваше приложение, чтобы получить определенный тип файла из другого приложения, он должен вызвать намерение с ACTION_GET_CONTENT действие. Это действие по-прежнему является подходящим способом запроса файла, который вы хотите импорт в вашем приложении. Тем не менее, Android 4.4 вводит ACTION_OPEN_DOCUMENT действие, которое позволяет пользователю выберите файл определенного типа и предоставьте вашему приложению долгосрочный доступ для чтения к этому файлу (возможно, с доступом на запись) без импорта файла в приложение.(подчеркнуто)

чтобы ваше приложение могло извлекать ранее выбранные изображения, просто измените действие с ACTION_GET_CONTENT to ACTION_OPEN_DOCUMENT (решение подтвердило работу на Nexus 7 2013).

    Intent intent = new Intent();
    intent.setType("image/*");
    String action = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT ? Intent.ACTION_OPEN_DOCUMENT : Intent.ACTION_GET_CONTENT;
    intent.setAction(action);
    startActivityForResult(Intent.createChooser(intent, "Select Picture"), Constants.SELECT_PICTURE);

передайте свой URI в этом методе, он вернет строку и преобразует ее в Bitmap

  String path = getPath(getApplicationContext(), uri);
  Bitmap bitmap = BitmapFactory.decodeFile(path);
  imageView.setImageBitmap(bitmap);





 public static String getPath(final Context context, final Uri uri) {

    // DocumentProvider
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && DocumentsContract.isDocumentUri(context, uri)) {

        if (isExternalStorageDocument(uri)) {// ExternalStorageProvider
            final String docId = DocumentsContract.getDocumentId(uri);
            final String[] split = docId.split(":");
            final String type = split[0];
            String storageDefinition;


            if ("primary".equalsIgnoreCase(type)) {

                return Environment.getExternalStorageDirectory() + "/" + split[1];

            } else {

                if (Environment.isExternalStorageRemovable()) {
                    storageDefinition = "EXTERNAL_STORAGE";

                } else {
                    storageDefinition = "SECONDARY_STORAGE";
                }

                return System.getenv(storageDefinition) + "/" + split[1];
            }

        } else if (isDownloadsDocument(uri)) {// DownloadsProvider

            final String id = DocumentsContract.getDocumentId(uri);
            final Uri contentUri = ContentUris.withAppendedId(
                    Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));

            return getDataColumn(context, contentUri, null, null);

        } else if (isMediaDocument(uri)) {// MediaProvider
            final String docId = DocumentsContract.getDocumentId(uri);
            final String[] split = docId.split(":");
            final String type = split[0];

            Uri contentUri = null;
            if ("image".equals(type)) {
                contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
            } else if ("video".equals(type)) {
                contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
            } else if ("audio".equals(type)) {
                contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
            }

            final String selection = "_id=?";
            final String[] selectionArgs = new String[]{
                    split[1]
            };

            return getDataColumn(context, contentUri, selection, selectionArgs);
        }

    } else if ("content".equalsIgnoreCase(uri.getScheme())) {// MediaStore (and general)

        // Return the remote address
        if (isGooglePhotosUri(uri))
            return uri.getLastPathSegment();

        return getDataColumn(context, uri, null, null);

    } else if ("file".equalsIgnoreCase(uri.getScheme())) {// File
        return uri.getPath();
    }

    return null;
}

вам нужно запросить разрешение на чтение внешнего хранилища пользователю во время выполнения для Android 6+

https://developer.android.com/training/permissions/requesting.html#perm-check

надеюсь, что это помогает.


возможно, вы можете справиться с этим, сначала преобразовав URI в реальный путь.

public String getRealPathFromURI(Context context, Uri contentUri) {
    Cursor cursor = null;
    try {
        String[] proj = { MediaStore.Images.Media.DATA };
        cursor = context.getContentResolver().query(contentUri,  proj, null, null, null);
        int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
        cursor.moveToFirst();
        return cursor.getString(column_index);
    } finally {
        if (cursor != null) {
            cursor.close();
        }
    }
}

и ваш onActivityResult должен выглядеть так

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if(requestCode==Constants.SELECT_PICTURE && resultCode==RESULT_OK && null!=data)
    {
        Uri uri=data.getData();
        String imagePath = getRealPathFromURI(getApplicationContext(), uril); 
Picasso.with(UsersProfileActivity.this).load(imagePath).centerCrop().resize(200,200).into(img_photo);

     profile_image = imagePath;  

    }

мне удалось сделать это в прошлом. Вместо того, чтобы хранить его в SharePreferences, я сохраняю его в пакете, когда onSaveInstanceState(Bundle) вызывается: bundle.putParcelable (SOME_KEY, uri). Вы можете сделать это, потому что Uri реализует Parcelable. После этого проверьте этот uri в пакете, переданном в onCreate (). Код:

public void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    if (savedInstanceState != null){
        loadImage((Uri)saveInstanceState.getParcelable(SOME_KEY))
    }
}

public void onSaveInstanceState(Bundle bundle){
     bundle.putParcelable(SOME_KEY, uri)
}

P/S: Я думаю, проблема хранения uri в SharePreference заключается в кодировании / декодировании Uri.


вы можете легко конвертировать URI в Bitmap:

ParcelFileDescriptor parcelFileDescriptor =
                context.getContentResolver().openFileDescriptor(selectedFileUri, "r");
        FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
        Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor);


        parcelFileDescriptor.close();

а затем установите в Imageview: imgview.setImageBitmap(изображения);

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


может быть проблема с версиями Пикассо. Они исправили что-то с загрузкой больших изображений в версии 2.5.0 (2.4.0...?), который еще не доступен через репозитории.

вам нужно будет вручную включить ночной снимок Пикассо, и это, вероятно, исправит вашу проблему. Для меня-да.


Я также столкнулся с чем-то подобным ранее и нашел полезный вспомогательный класс, который дает вам точное местоположение файла. Вот это ссылке.

вы можете использовать такие методы, как FileUtils.getPath (контекст, uri) или FileUtils.getFile (context, uri), чтобы получить файл. Тогда просто используйте его так:

 Picasso.with(UsersProfileActivity.this)
         .load(Uri.fromFile(file))
         .centerCrop()
         .resize(200,200)
         .into(img_photo);

надеюсь, что это помогает.


просто не на 100% уверен, если я получу вопрос. Если определенные изображения не могут быть показаны в imageView (даже до закрытия и перезапуска приложения), то проблема в том, что Picasso может обрабатывать содержимое от определенных поставщиков. Я не понимаю, что это так, но если это так, то вы можете рассмотреть возможность подачи ошибки/RFE для этой библиотеки. С другой стороны, если Picasso правильно показывает изображение для данного Uri, но не после Uri_org - > String - > Uri_regen преобразование тогда это означает, что Uri_org не то же самое, что Uri_regen. Вы можете (временно) добавить пару строк кода, чтобы преобразовать строку обратно в Uri и поставить точку останова сразу после и сравнить два Uri. Это должно дать вам ключ к тому, как все идет не так.

profile_image=uri.toString();
//add the next few lines for debugging purposes only; remove them later
Uri uri2 = Uri.parse(profile_image)
if (!uri.equals(uri2)
   //add a breakpoint on the next line
   Log.d("Uri mismatch), "Ouch");
//remove the lines after you are done with debugging

Если вы не нажмете точку останова, затем переместите точку останова обратно в Оператор " if " и визуально проверьте два Uri (которые почти наверняка будут одинаковыми, иначе у вас было бы нажмите точку останова,но это поможет проверить сам код отладки). Если URI такие же, то строки предпочтение не правильно сохранился почему-то. В этом случае запишите исходный Uri и добавьте точку останова в строке, которая преобразует строку обратно в Uri:

Uri uri = Uri.parse(profile_image) //profile_image is retrieved from sharedpreferences

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

удачи!