android - 使用位图工厂选项支持多个屏幕?

标签 android android-emulator module screen bitmapfactory

编辑一个:

根据 Joseph 的回答所做的更改:

以 bytesToDrawable(byte[] imageBytes) 为单位:

更改了以下内容:使用 BitmapDrawable(Resources res, Bitmap bitmap) 而不是 BitmapDrawable(Bitmap bitmap):

return new BitmapDrawable(ApplicationConstants.ref_currentActivity.getResources(),BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length, options));

这是该更改的结果:略有不同的问题:

enter image description here

问题:

如果我为可绘制位图使用新的构造函数并且它根据所需的目标密度缩放图像,我是否仍需要使用我的 calculateSampleSize 方法?


原始问题:

friend 们好

我的应用程序是基于模块的,因此特定于该模块的图像仅从包含它们的 jar(模块)加载,而不是从主应用程序加载。

每个模块都有自己的 ModularImageLoader - 这基本上允许我根据在 jar 中找到的图像名称获取 Drawables。

构造函数接受 zipFile(模块 A)和文件名列表(zip 中以“.png”结尾的任何文件)。

进行的研究:

我使用了以下内容:Link to Developer Page on Loading bitmaps efficiently

最初我是为每种密度创建尺寸的图像,但现在我只有一组尺寸为 96x96 的图像图标。

如果屏幕密度小于 xhdpi,我会加载 96x96 图像的较小采样尺寸 - 如 36x36(对于 ldpi)、48x48(对于 mdpi)、72x72(对于 hdpi)。否则我只返回 96x96 图像。 (查看方法 calculateSampleSize() 和 bytesToDrawable())

我认为用代码更容易理解这个概念:所以这里是 ModularImageLoader

代码:

public class ModularImageLoader
{
    public static HashMap<String, Drawable> moduleImages = new HashMap<String, Drawable>();
    public static int reqHeight = 0;
    public static int reqWidth = 0;
    public ModularImageLoader(ZipFile zip, ArrayList<String> fileNames)
    {
         float sdpi = ApplicationConstants.ref_currentActivity.getResources().getDisplayMetrics().density;
         if(sdpi == 0.75)
         {
            reqHeight = 36;
            reqWidth = 36;
         }
         else if(sdpi == 1.0)
         {
            reqHeight = 48;
            reqWidth = 48;
         }
         else if (sdpi == 1.5)
         {
            reqHeight = 72;
            reqWidth = 72;
         }
         else if (sdpi == 2.0)
         {
            reqHeight = 96;
            reqWidth = 96;
          }
          String names = "";
          for(String fileName : fileNames)
          {
            names += fileName + " ";
          }
          createByteArrayImages(zip, fileNames);
     }

public static Drawable findImageByName(String imageName)
{
    Drawable drawableToReturn = null;
    for (Entry<String, Drawable> ent : moduleImages.entrySet())
    {
        if(ent.getKey().equals(imageName))
        {
            drawableToReturn = ent.getValue();
        }
    }
    return drawableToReturn;
}
private static void createByteArrayImages(ZipFile zip, ArrayList<String> fileNames)
{
    InputStream in = null;
    byte [] temp = null;
    int nativeEndBufSize = 0;
    for(String fileName : fileNames)
    {
        try
        {
            in = zip.getInputStream(zip.getEntry(fileName));
            nativeEndBufSize = in.available();
            temp = toByteArray(in,nativeEndBufSize);

            // get rid of .png
            fileName = fileName.replace(".png", "");
            fileName = fileName.replace("Module Images/", "");
            moduleImages.put(fileName, bytesToDrawable(temp));
        }
        catch(Exception e)
        {
            System.out.println("getImageBytes() threw an exception: " + e.toString());
            e.printStackTrace();
        }
    }
    try
    {
        in.close();
    }
    catch (IOException e)
    {
        System.out.println("Unable to close inputStream!");
        e.toString();
        e.printStackTrace();
    }
}
public static byte[] toByteArray(InputStream is, int length) throws IOException 
{
    ByteArrayOutputStream buffer = new ByteArrayOutputStream();
    int l;
    byte[] data = new byte[length];
    while ((l = is.read(data, 0, data.length)) != -1) 
    {
      buffer.write(data, 0, l);
    }
    buffer.flush();
    return buffer.toByteArray();
}
public static Drawable bytesToDrawable(byte[] imageBytes)
{
    try
    {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        int imageHeight = options.outHeight;
        int imageWidth = options.outWidth;

        BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length, options);
        String imageType = options.outMimeType;
        Log.d("ImageInfo : ", "Height:" + imageHeight +",Width:" +imageWidth + ",Type:" + imageType);

        options.inJustDecodeBounds = false;
        //Calculate sample size
        options.inSampleSize = calculateSampleSize(options);
        return new BitmapDrawable(BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length, options));
    }
    catch(Exception e)
    {
        Message.errorMessage("Module Loading Error", "The images in this module are too large to load onto cell memory. Please contact your administrator",
                "Source of error: ModularImageLoader - bytesToDrawable method", e.toString());
        return null;
    }
}
public static int calculateSampleSize(BitmapFactory.Options options)
{
    // raw height and width of the image itself
    int sampleSize = 1;
    int height = options.outHeight;
    int width = options.outWidth;
    if(height > reqHeight || width > reqWidth)
    {
        if(width > height)
        {
            sampleSize = Math.round((float)height / (float)reqHeight);
        }
        else
        {
            sampleSize = Math.round((float)width / (float)reqWidth);
        }
    }
    return sampleSize;
}
}

问题:

下图显示了 4 个正在运行的模拟器,这些是它们的规范以及我如何在 eclipse AVD 中设置它们:

LDPI:密度 120,皮肤 QVGA MDPI:密度160,皮肤HVGA HDPI:密度240,皮肤WVGA800 XHDPI:密度320,皮肤800x1280

图像显示问题:

enter image description here

问题:

根据代码 - 在 XHDPI 窗口中,为什么联系人图像这么小?新闻图像也是 96x96(除了从主应用程序加载 - 所以它在 res>XHDPI 下)。 问题是,我看到它在 MDPI 屏幕和 HDPI 屏幕上加载正常,但在其他屏幕上却很奇怪。有什么想法吗?

最佳答案

如果您使用 BitmapFactory.Options 提供密度信息,BitmapFactory 能够为您缩放图像。如果这样做,您应该能够删除 ModularImageLoader 中的自定义缩放代码。

指定 inDensity 和 inTargetDensity - 如下所示:

BitmapFactory.Options options = new BitmapFactory.Options();
options.inDensity = DisplayMetrics.DENSITY_MEDIUM;
options.inTargetDensity = activityRef.getResources().getDisplayMetrics().densityDpi;
options.inScaled = true;
return BitmapFactory.decodeStream(openByteStream(), null, options);

BitmapFactory.decodeByteArray 中显然存在忽略某些缩放选项的错误,因此您可能需要将字节数组包装在 ByteArrayInputStream 中并使用 BitmapFactory.decodeStream,如上所示(参见 http://code.google.com/p/android/issues/detail?id=7538)。

关于android - 使用位图工厂选项支持多个屏幕?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13482946/

相关文章:

java - RxAndroid 链式操作

android - 在 Eclipse 上运行模拟器不工作

android - 何时在 Android 上使用本地存储,推荐使用哪个?

android - 在卡在白屏上的模拟器上 react native 应用程序

java - 纯控制台Android应用?

node.js - 带有 Passport 的 nodejs/hapi 应用程序中的模块化

python - 导入时应如何将选项传递给 Python 模块?

java - 如何从 ArrayList 在 ListView 上显示员工详细信息?

android - Visual Studio android 模拟器无法完全启动

python - 无法使用 easy_install 安装 Python 模块