1

I have a problem that is driving me nuts.

I had 4 functions that would resize images correcly throuhout UI using density (1.5) and width/height values reported back by views, bitmaps, screen etc.

This worked excellent on SG II. But on a HTC Wildfire which reports density 0.75 ... some images got about 25% too small visually on screen... But when I decided to override the density to 1.0 those images suddenly fit while other became crisp, but no longer sized as correctly... And it is driving me nuts...

I can only conlcude hat I am some places comparing apples to pears (pixels and DIPs), but I can not get my tests to fit with what I read, so in an effort to be 100% sure of some things:

...

Will I get pixels underneath here? or DIPs? :

int bitmapWidth = bitmap.getWidth();

Suppose underneat is an imageview containing a bitmap. Pixels or DIPs?

int widthParent = view.getWidth();      

Underneath is actually DIPs, right?

getContext().getResources().getDisplayMetrics().widthPixels;

...

Here is my code that is not working (this code is giving a bitmap that visually takes maybe 2/3s width where it should take 100%) If you check comments the pixels values are apparently correct, but end result is not correct:

vto.addOnGlobalLayoutListener(
  new OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
      mainLogo.getViewTreeObserver().removeGlobalOnLayoutListener(this);                                                                 
      mainLogo.setScaleType(ImageView.ScaleType.CENTER_INSIDE); // mainLogo = ImageView
      SharedCode.sharedUtilScaleImage_Width(mainLogo, false);              
    }
  }                        
);                                   

// ...

public static void sharedUtilScaleImage_Width(ImageView view, boolean tryBackground)
  {
    Drawable drawing = null;
    boolean useBackground = false;         
    if (drawing == null) {    
      drawing = view.getDrawable();
    }          
    if (drawing == null) {    
      if (tryBackground) {
        drawing = view.getBackground();
        useBackground = true;
      }
    }      
    if (drawing == null) {
      return; 
    }        
    if (!(drawing instanceof BitmapDrawable)) {
      return; 
    }                      
    Bitmap bitmap = ((BitmapDrawable)drawing).getBitmap();
    if (bitmap == null) {
      return;
    }
    //--                               
    int bitmapWidth = bitmap.getWidth(); // returns 770 (which is correct checking on disk)
    int bitmapHeight = bitmap.getHeight();                
    // float density = 1;
    // density = MicApp.getContext().getResources().getDisplayMetrics().density; // 1.5          
    float widthScreen = MicApp.getContext().getResources().getDisplayMetrics().widthPixels; // returns 480              
    int widthParent = view.getWidth(); // returns 480, should be same as screen      
    //--
    float xScale = ( (float) widthParent / (float) bitmapWidth); 
    Matrix matrix = new Matrix();
    matrix.postScale(xScale, xScale);
    //--
    Bitmap scaledBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmapWidth, bitmapHeight, matrix, true);
    int bitmapWidth2 = scaledBitmap.getWidth(); // 480
    int bitmapHeight2 = scaledBitmap.getHeight();      
    //--          
    BitmapDrawable result = new BitmapDrawable(scaledBitmap);          
    //--          
    if (useBackground) {
      LayoutParams layoutparams = new LayoutParams(bitmapWidth, bitmapHeight);
      view.setLayoutParams(layoutparams);
      view.setBackgroundDrawable(result);
    }
    else {
      view.setImageDrawable(result);
    }            
  }
4

1 回答 1

1

在解码图像和显示图像之间有几个步骤。这些步骤通常用于以正确的比例显示来自资源的图像。如果您仅将图像放入/res/drawable-mdpi/但不在其他文件夹中,则它仍然可以在具有其他密度的设备上以正确的尺寸显示。

如果您想mdpi在 (320dpi) 设备上加载适用于 (160dpi)的图像x-hdpi并希望它以相同的大小显示,则需要将其像素大小加倍。例如,需要以 200x200 像素显示 100x100 像素的图像。

涉及缩放的地方是

  • 解码图像(参见例如BitmapFactory.Options#inDensity)。BitmapFactory已经可以将 100x100 png 图像解码为 200x200 图像。
  • Bitmap自身密度。这是每个位图的属性,它用于存储图像的密度。如果BitmapFactory将 100x100 mdpi 图像解码为 200x200,那么该密度基本上会说“我是 x-hdpi”。您还可以在解码时禁用缩放,并获得具有 mdpi 密度的 100x100 图像。显示该图像需要绘制 2 倍缩放的图像。
  • BitmapDrawable目标密度,通常是设备的密度。如果BitmapDrawable必须Bitmap在密度不匹配的地方绘制一个,它将绘制一个缩放版本。

来自 的代码BitmapDrawablemTargetDensity应该是设备密度。

private void computeBitmapSize() {
    mBitmapWidth = mBitmap.getScaledWidth(mTargetDensity);
    mBitmapHeight = mBitmap.getScaledHeight(mTargetDensity);
}

和相关的部分BitmapmDensity是位图的密度

public int getScaledWidth(int targetDensity) {
    return scaleFromDensity(getWidth(), mDensity, targetDensity);
}

static public int scaleFromDensity(int size, int sdensity, int tdensity) {
    if (sdensity == DENSITY_NONE || sdensity == tdensity) {
        return size;
    }
    
    // Scale by tdensity / sdensity, rounding up.
    return ((size * tdensity) + (sdensity >> 1)) / sdensity;
}
  • 例如的缩放选项ImageView。如果要在屏幕上显示一个ImageDrawable(由于缩放或实际上)200x200 像素的图像,而ImageView实际上是 400x300 像素,则需要再次缩放图像。

使用ImageView.ScaleType.CENTER_INSIDE会将图像绘制为 200x200 像素,因为CENTER_INSIDE仅在图像大于视图时才缩放图像。FIT_CENTER将其放大到 300x300。根本CENTER没有规模。它只是中心。

CENTER_INSIDEBitmapDrawable+ 由于存储在/中的所有缩放因子,上面的 100x100 mdpi 图像仍会将其绘制为 200x200px Bitmap

BitmapFactory> Bitmap> BitmapDrawable> ImageView链中还有其他几个地方,比如BitmapDrawable(Bitmap bitmap)构造函数说

此构造函数在 API 级别 4 中已弃用。使用 BitmapDrawable(Resources, Bitmap) 确保可绘制对象已正确设置其目标密度。

这可能会以不希望的方式影响图像缩放。

-> 防止所有缩放

  • 解码而不缩放
  • 确保您的位图密度是当前设备密度或只是Bitmap#setDensity(Bitmap.DENSITY_NONE)
  • 通过 绘制时不要应用缩放ImageView.ScaleType.CENTER
于 2013-09-18T14:27:13.567 回答