简体   繁体   中英

Best quality image allocation from resources based on resolution/density

I'm struggling with a pretty trivial task in the Android "multiple-screen sizes" domain.

What I'm trying to achieve

A layout matching the screen width, with a nine-patch background which resizes (only horizontally, since there is always enough vertical space). Here is a dummy image: 水平可伸缩的九个补丁图像,左右两端有两个区域

My goal is, depending on the screen resolution, to display the image at the highest resolution possible, by using a set of different sizes, eg. 320x45, 480x67, 600x87, 720x101, without any down-scaling. I'm hoping for a non-programmatic solution.

An example with the above mentioned image sizes would be:

  • 3.7" Nexus One (480 x 800) - the 480x67 image would look best.
  • 4.7" Galaxy Nexus (720 x 1280) - the 720x101 image.
  • 4.7" Nexus 4 (768 x 1280) - again the 720x101 image, stretching to the full 768 px width and becoming 768x101.

Issue

The whole Android resource allocation revolves around dp s (density-independent pixels), when in fact I want to display an image based on the actual available pixels.

If I allocate the 480x67 image to res/drawable-mdpi and a 600x87 to res/drawable-hdpi, then image would display correctly on a 5.4" display of 480x800 , ie mdpi display. However, a 4" 480x800 displays qualifies as hdpi and the system would appoint the 600x87 image, which won't fit the screen .

I tried the smallestWidth parameter as described in the online guide , but that yields strange results. For instance, a 3.7" 480 x 800 display (hdpi) uses my drawable-sw320dp image, although there is a drawable-sw480dp resource available too.

What is the best way to assign a stretchable, width-matching image with the best possible quality? Isn't there any non-programmatic solution?

Thanks in advance!

I believe that by combining density and screen size resource qualifiers you can achieve a close to optimal behavior.

Lets assume this kind of resource folders structure:

  • drawable-normal-hdpi - A normal size dictates minimum width of 320dp. hdpi dictates 1.5X dp to pixel multiplier. So the minimum px width of the normal hdpi bucket is 480px. We put here a 480px wide image.
  • drawable-normal-xhdpi - Again size dictates 320dp but this time with 2X multiplier. So we use a 640px wide image.
  • drawable-xlarge-mdpi - Size means at least 720dp. mdpi multiplier is 1X, so we use a 720px wide image.

Now lets look at some devices to see how they fall in with those buckets:

  • Nexus one - normal hdpi . Actual px width: 480px. The image fits perfectly.
  • Galaxy nexus - normal xhdpi . We could fit a 720px image, so the 640px image we use isn't optimal - but it's very close.
  • Nexus 4 is just like the Gnex.
  • Nexus 10.1 (1280X800) - xlarge mdpi . We could fit 800px, our image is 720px. Again not ideal but close enough.

Worst case scenario: image used could have 5-10% better quality. Best case: perfect fit.

The main down side of this method is that you need to provide a lot of resources and folders to account for all the permutations of sizes and densities (even worse if you need to combine that with more qualifiers for locale, orientation and so on). However, as far as my Android understanding goes I don't think you can achieve something better than this without coding.


A remark regarding smallestWidth : Your example for the weird behavior is actually the expected behavior. hdpi multiplier is 1.5 - So a 480px wide hdpi display is exactly 320dp wide. This makes the drawable-sw320dp the right choice, as documented. I'm not sure if you can combine the smallestWidth qualifier with the dpi qualifier. If it's possible you might get more accurate results than just size modifiers. But this would mean a lot more permutations for a 5% increase in image quality. Probably not worth it.

Actually, your method is not how it is supposed to be. I will suggest 2 ways for you, one is easy but doing programmatically, other one is using a custom view.

Method 1 - Programmatically

    DisplayMetrics displaymetrics = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
    int screenWidth = displaymetrics.widthPixels; 
    // this will determine "scale ratio" so using which image height and width won't matter 
    int imageOriginalHeight = 101; // your original image height        
    int imageOriginalWidth = 720; // your original image width
    int imageScaleHeight = (screenWidth*imageOriginalHeight) / imageOriginalWidth;

    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(screenWidth, imageScaleHeight);
    imageView.setLayoutParams(params);
    imageView.setImageResource(R.drawable.file);

Method 2 - Custom View

You can use a custom view called ScaleImageView which is written by Maurycy Wojtowicz.

Class is defined like below:

This view will auto determine the width or height by determining if the height or width is set(exact size or match_parent) and scale the other dimension depending on the images dimension This view also contains an ImageChangeListener which calls changed(boolean isEmpty) once a change has been made to the ImageView

Here is how you are going to implement it.

Create a class named ScaleImageView.java and copy contents of the link above.

In your xml file, create a ScaleImageView , just same like ImageView (the example I am writing below is for filling screenwidth, and scaling height according to that so there will be no empty spaces on right/left)

    <com.project.customview.ScaleImageView
        android:id="@+id/scaleImageView"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:adjustViewBounds="true"
        android:scaleType="fitCenter"
        android:src="@drawable/file" />

If you need to declare and set programmatically in your Activity, it is also the same as using ImageView:

imageView = (ScaleImageView)findViewById(R.id.scaleImageView);
imageView.setImageResource(R.drawable.file);

In android you have the option hdpi, mdpi, xdpi,etc..

folders for that , you have to create different images according your device resolution and put your images at there after confirming your device resolution and density category.

for the more reference why it'll happen you can see here

here i explain some chart may be helpful to you.

Low density Small screens QVGA 240x320 (120dpi):

drawable-small-ldpi (240x320)
drawable-small-land-ldpi (320x240)

Low density Normal screens WVGA400 240x400 (x432) (120dpi):

drawable-ldpi (240 x 400 ) drawable-land-ldpi (400 x 240 )

Medium density Normal screens HVGA 320x480 (160dpi):

drawable-mdpi (320 x 480 ) drawable-land-mdpi (480 x 320 )

Medium density Large screens HVGA 320x480 (160dpi):

drawable-large-mdpi (320 x 480 ) drawable-large-land-mdpi (480 x 320)

Galaxy Tab ( 240 dpi ):

drawable-large (600 x 1024) drawable-large-land (1024 x 600)

High density Normal screens WVGA800 480x800 (x854) (240 dpi):

drawable-hdpi (480 x 800) drawable-land-hdpi (800 x 480)

Xoom (medium density large but 1280x800 res) (160 dpi):

drawable-xlarge (800 x 1280) drawable-xlarge-land (1280 x 800)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM