图像URI在某些安卓设备上无法在ImageView上显示图片

15

我有一个ImageView。当你点击ImageView时,它会打开相册,你可以选择一张图片并在ImageView上显示出来。我有一个条件,即当我关闭我的应用程序,然后再打开它时,图片将保留在那里。 因此,我正在使用sharedpreference存储图像Uri。 在打开应用程序时,我检索相同的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.parse(profile_image)将String转换为uri。

但是如果我注意到,不同安卓手机的uri返回结果如下:

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内容为“content://media/external/images/media/”时,在ImageView上显示的图像完美无缺,而其他情况则不是。

1
我猜这是Android 6.0权限问题,请检查您是否在Marshmallow版本的运行时处理<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> - SripadRaj
不是,我已经检查过那些东西了,甚至在某些设备上更低的安卓版本上也会出现这种情况。 - abh22ishek
你使用的 Picasso 版本是哪个? - JohnWowUs
12个回答

15

试试这个

我开发、测试并且正在处理以下设备:

  1. 联想K3 Note (棉花糖版本)
  2. 摩托罗拉 (棒棒糖版本)
  3. 三星 (奇巧巧克力版本)

在你的 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);
    }
}
在您的清单文件中添加权限
<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" />

注意:

假设您已经添加了代码以取得Marshmallow权限

结果 Uri

联想K3 Note: content://com.android.externalstorage.documents/document/primary%3ADCIM%2FCamera%2FIMG_20160606_212815.jpg

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

摩托罗拉: content://com.android.providers.media.documents/document/image%3A15828


你能给我展示一下你获取的所有三个设备的URI吗? - abh22ishek
你能检查一下吗?当你在sharedprefrence中存储数据后,关闭你的应用程序,并确保该活动被Android杀死,即按下主页按钮并杀死该应用程序。然后再打开此应用程序并检查图像是否存在。 - abh22ishek
是的,我按下主页按钮,然后从最近使用应用程序列表中关闭了该应用程序...... 我测试过了,它可以正常工作。 - Pramod Waghmare
我已经检查过了,现在它可以正常工作了。我认为这行代码 intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) 才是关键所在。 - abh22ishek
另一个问题是,如果您关闭并重新打开设备,则图像将不会出现。 - abh22ishek
显示剩余3条评论

3

我认为问题出在图片文件的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来查找文件的精确路径。这是处理像您定义的问题最简单的方法。请尝试并告诉我是否需要进一步协助 :)

2
4.4 API概述中引用:
“在之前的Android版本中,如果您希望您的应用程序从另一个应用程序检索特定类型的文件,则必须使用ACTION_GET_CONTENT操作调用意图。这个操作仍然是请求您想要导入到应用程序中的文件的适当方式。但是,Android 4.4引入了ACTION_OPEN_DOCUMENT操作,它允许用户选择特定类型的文件,并授予您的应用程序长期读取该文件的权限(可能具有写入权限),而无需将文件导入到您的应用程序中。”(强调添加)
为了使您的应用程序能够检索先前选择的图像,只需将操作从ACTION_GET_CONTENT更改为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);

1
在此方法中传递您的URI,它将返回一个字符串,然后将其转换为位图。
  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;
}

0
一个简单的解决方法是在文件路径前面加上 "file://"。 例如: 将 {{uri: "/path/to/file"}} 改为 {{uri: "file:///path/to/file"}} 适用于任何平台。

0

可能是 Picasso 版本问题。他们在版本 2.5.0(2.4.0…?)中修复了加载大图片的问题,但这个版本目前还没有通过软件源发布。

你需要手动包含 Picasso 的夜间快照版本,很可能这样就能解决你的问题。这种方法对我有效。


这不是Picasso的问题,我认为是因为当我关闭我的应用程序,然后重新打开它时,Picasso能够获取URI并在ImageView上显示,只有当包含我问题中提到的路径的ImageView所在的Activity被销毁时,它才无法显示。 - abh22ishek
好的,请检查我的下面的答案,保存实例状态。 - Marc DiMillo

0

运行时权限在Android Marshmallow中是必需的,但在这种情况下,我没有读写设备内部存储。并且我认为您不需要从sharedprefrences文件中获取写入和读取权限。 - abh22ishek
但是您没有保存文件,Uri 只包含文件路径,因此您需要读取路径内容的权限。 - Vlad
实际上,我正在将Uri转换为String,然后存储在sharedPreference中,那么它只会作为字符串或路径吗? - abh22ishek
是的,没错,实际上我不确定你是否可以将文件存储在SharedPreferences中,SharedPreferences只能存储原始数据类型。 - Vlad
我已经插入了运行时权限,但图片仍然无法显示。 - abh22ishek

0

你可以轻松地将URI转换为位图:

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


        parcelFileDescriptor.close();

然后将其设置到ImageView中:imgview.setImageBitmap(image);

希望这可以帮助您。


0

我之前也遇到过类似的问题,后来找到了一个有用的辅助类,可以给你提供文件的确切位置。这是链接

你可以使用像FileUtils.getPath(context, uri)或FileUtils.getFile(context, uri)这样的方法来获取文件。然后就可以像这样使用它:

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

希望这能有所帮助。

0

我不确定是否理解了问题。如果某些图像无法在imageView中显示(即使在关闭并重新启动应用程序之前),那么问题可能是Picasso无法处理来自某些提供程序的内容。我没有感觉到这种情况,但如果是这样,那么您可能需要考虑为该库提交错误/请求功能的bug/RFE。另一方面,如果Picasso正确显示给定Uri的图像,但在Uri_org -> String -> Uri_regen转换后无法显示,则意味着Uri_org与Uri_regen不同。您可以(暂时)添加几行代码将String转换回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

如果之前的实验成功了,那么在这里它应该再次工作,除非个人资料图片不同。无论如何,您都应该得到指针,以确定问题所在。
祝你好运!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接