After two years of Android development I'm still not 100% sure about what resources I need to provide to make my GridView work on different size and resolution devices.
I'm still learning, for example I recently discovered that you don't have to supply every drawable in all screen sizes - if you put something in xhdpi then Android is clever enough to resize that on the fly most of the time - but there are a few few quirks. For example if I try using drawables which are only defined in the drawable-xhdpi bin on a mdpi device in a GridView, the drawable will visually resize correctly but the whitespace around it won't - that will still be the original size. Originally I got around this by forcably defining dimensions for all aspects of the GridView and the associated adapter view, but this had a load of code smell around it, so I resized the drawables manually, put them in their respective bins (drawable-mdpi, drawable-hdpi and drawable-xhdpi) and removed all the forcing of image sizes etc.
So I currently have:
<GridView
android:id="#id/home_image_grid"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:columnWidth="100dp"
android:gravity="center"
android:listSelector="#00000000"
android:numColumns="auto_fit"
android:stretchMode="columnWidth"
>
</GridView>
and the layout for each item is:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="0dip"
android:gravity="center"
android:orientation="vertical"
android:padding="0dip">
<ImageView
android:id="#id/home_button_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<TextView
android:id="#id/home_button_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:textColor="#color/default_text_colour"
android:textSize="#dimen/home_button_text"
/>
</LinearLayout>
So far so good...
The app I'm currently working on I've almost finished and have been doing some testing on different devices - it looks good on normal screen size xhdpi, hdpi & mdpi devices. Then I extending my testing to other devices - I'm testing on an HTC Flyer which is large screen size, mdpi - and the gridview now looks rubbish - it's correctly picking up the mdpi images, which are tiny.
Doing some reading around I found the GridView tutorial GridView tutorial, which says to put all your drawables in the 'drawable' bin, so I followed this advice and again everything looked rubbish - on closer inspection (and having actually read through the example code) it seems that they are manually setting the size of each image in the GridView - which I'd been doing to start off with, and which means that I'm going to have to manually set the image sizes for every permutation of devices.
So..I'm left wondering if I've missed a trick here - should I take my first approach of sticking the images in my drawable folder and manually forcing the sizes for every purmutation? Should I draw a new set of images for all permutations of screen size and resolution?
How can I guarantee that whatever I do, it's going to look good on a device I've not tested it on?
Either way it feels like I'm doing something wrong - but I can't work out how to do this properly without loads of hassle. Can anyone help?
I'm not sure what you mean by "forcing the sizes for every permutation" but you could put the proper sized drawables in the following folders:
drawable-mdpi
drawable-large-mdpi
Each device would pick the right drawables from the corresponding folder and you could separate normal and large images for mdpi devices.
Wouldn't that fix your problem?
P.S: The drawable folder is meant for XML drawables only or photos which do not depend on the device's density, not images for the UI. Those will scale properly for each resolution and density.
Related
I am currently trying to adjust my Android App so it will look and feel similar on multiple screens/devices.
I know by now that a major part in this is to provide multiple image sizes for every image file according to the x1, x1.5, x2, x3, x4 ratios for mdpi, hdpi, xhpi, xxhdpi and xxxhdpi respectively, and I have finished doing so today.
After doing this, I have defined Density independent Pixel dimensions in the #dimen.xml values resource that correspond with the actual image sizes in pixels of the MDPI resources. Subsequently, i have set the imageviews in question's layout_width and layout_height to this dimension.
I am currently at a loss, however, as to why my application still looks significantly different on an MDPI emulator than it does on an HDPI emulator. To highlight what I mean, I'll provide the following screenshot showing the HDPI and MPDI emulator next to one another (left is HDPI (4" WVGA Nexus S) and right is MDPI (5.4" FWVGA)). I want both of them to look like the MPDI version, but what I've done so far apparently isn't working.
I have three theories of my own as to why this is not working the way I intend it to:
1. I am not supposed to set ImageView layout_width and layout_height to a dp value, but rather match_parent or wrap_content (?) (and change the structure of my .xml layouts in the process).
2. I am not only supposed to define multiple drawable resources, but also multiple layout resources for different screen sizes (?).
3. I have misunderstood the entire idea behind how this is supposed to work (?).
I will also give you an example of one of the list items that can be seen in the first screenshot (#drawable/phone_speed_icon is a 64 x64 pixel resource in MPDI and a 96x96 resource in HDPI, and #dimen/icon_attribute_size is 64dp):
<LinearLayout
android:id="#+id/llSpeed_PreSession"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingBottom="10dp"
android:paddingEnd="20dp"
android:paddingStart="20dp"
android:weightSum="100">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="#dimen/icon_attribute_size"
android:layout_weight="20"
android:weightSum="100">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="70"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:layout_width="#dimen/icon_attribute_size"
android:layout_height="#dimen/icon_attribute_size"
android:src="#drawable/phone_speed_icon" />
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="30"
android:gravity="center_vertical"
android:paddingStart="10dp"
android:text="Speed"
android:textAppearance="#android:style/TextAppearance.Large"
android:textColor="#878787"
android:textStyle="bold" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="80"
android:gravity="center">
<android.support.v7.widget.SwitchCompat
android:id="#+id/swSpeed_PreSession"
android:layout_width="wrap_content"
android:layout_height="match_parent" />
</LinearLayout>
</LinearLayout>
In the end, I have four questions that I'd like answered:
1. How do I make the list items in screenshot 1 look the same on MDPI devices as HDPI devices? Does this have anything to do with one of the three theories I mentioned earlier?
2. Why is the header text ("What do you want to measure?") wrapped on one device, but not on the other? They use the sp unit (via android:style/TextApperance.TextApperance.Large)?
3. Shouldn't everything be more spaced out on an HDPI device (if anything) rather than less spaced out? The HDPI emulator looks as if it "has got way less pixels available", if you can understand what I'm saying even a little.
4. How do I make the Fragments on the second screenshot look the same? Or should i not even want this, because the HDPI is (for some reason) physically smaller, which is why the layout is less spread out?
Anyway, I have been at this all day and the more I read the more thouroughly confused I get, so any help is greatly appreciated!
You have the option to create multiple dimens.xml files depending on a variety of factors. Here you'll see what my current project looks like with the various dimens.xml files in Android Studio based on screen width.
However, you can have different requirements for each dimens file you want. For example, you can create dimens files for each density:
I'm developing a kind of a instructions app. It uses fragments with ScrollvView, vertical LinearLayout and some TextViews / ImageViews in it to explain how a product works.
The images are just simple vector graphics which I save as png due to the lack of vector image support on Android. Here's an example:
The images are always filling the the screen using android:scaleType="fitXY" (portait and landscape) and the scrollviewer takes care of the rest (using android:adjustViewBounds="true"in the image view to prefent from scrolling over the original size of the image).
I read the guidelines of Google to use different versions for the different resolutions and densities but this seems to be overengineered for my purpose.
My idea was to just supply one high-res (2000px width) image of the images in the drawable folder and let Android auto scaling do the rest.
The only issue I can think of is memory. But I'm not quite sure how the internals work here. If an OutOfMemory only occurs when displaying an image it shouldn't be a problem as the autoscaling hits in first.
If the scaler can run into memory issues I'm thinking using 3 versions of the image with different widths (i.e. 800pixel, 1500pixel and 2200pixel) and put them in different resource directories.
This seems not possible though...
The Android Studio wizard for Resource directories has an option for "smallest screen width" but its only for dp (density-independent pixels)! I want a resource file for real device pixels. I hope Android takes the closest fitting and don't fall back to the default one if it can't find an exact match.
My images don't care about density as they are always filling the screen.
So in a nutshell:
Is supplying just one large pixel image with a small file size an issue?
If yes how can I setup device pixel resource directories?
The nature of the app already has an huge amount of images. That's why I don't want to duplicate each a couple of times which x-folds the number of images to deal with. The app is for API 11 onward.
I finally found a solution to proportionally scale one image to device width with no memory issues.
First you have to draw up your image (my_image.png) in a rather high resolution
Put this image ONLY into the drawable-xxhdpi folder.
Extend the Android ImageView class using this awesome answer.
Use it in your layout as shown below:
Here is layout that works for me:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:fillViewport="true">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/main_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/hello_world" />
<my.package.name.DynamicImageView
android:id="#+id/main_image"
android:adjustViewBounds="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#id/main_text_view"
android:src="#drawable/my_image"
android:scaleType="fitXY"/>
<my.package.name.DynamicImageView
android:id="#+id/main_image2"
android:adjustViewBounds="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#id/main_image"
android:src="#drawable/my_image"
android:scaleType="fitXY"/>
<TextView
android:id="#+id/main_text_view2"
android:layout_below="#id/main_image2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/hello_world" />
</RelativeLayout>
</ScrollView>
Some more final information:
I did a lot of testing with point 2 (which folder to put the image in) before I found the solution of point 3.
If you put the image in the standard drawable folder the scaling works without point 3 but you might run into OutOfMemory exceptions on older devices as android tries to scale up the already large image (Android thinks in drawable are only small images without checking their size).
When the image is in the drawable-xxhdpi folder Android knows its high-res and does only a down scaling if necessary. But it doesn't scale the image to width on density fitting devices! For example in a Nexus 10 the image gets drawn in its original size and scale - doesn't matter what you set for scaleType (I reckon it has something to do with inTargetDensity as explained in the inScaled description).
One important thing to keep in mind here is that I have to use a ScollView around the whole content. You may find some other solutions when no ScrollView is involved.
Finally, you could also use the drawable-xxxhdpi folder but it was introduced with API 19. I tried it and it worked on lower APIs as well. Apparently Android build tool 19.1.0 and higher takes care of lower API versions.
I hope this helps someone...
I am using an imageview in my xml and setting an image from res/drawable-xhdpi to it. My image is of 354x404 in size and when i am running the code on a 10" samsung tablet, it showing the image as 177x202.
Here is the code which i am using:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:baselineAligned="false" >
<ImageView
android:id="#+id/img_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="#string/app_name"
android:src="#drawable/android1" />
</LinearLayout>
Here is the image which i am using:
And this is the screenshot of the tablet:
Please tell why it is happening and what is the solution to resolve this problem.
Update: when i putting this image in "res/drawable" folder instead of xhdpi, it showing the image correctly i.e. 354x404.
Edit after comments:
The ImageView will render the image inside of it at the correct resolution for the device. As per the documentation on providing resources, you must make sure that you provide resources at the correct DPI for each of the resolution types. Android will pick the best resource resolution for you, but if only one resource exists then it will pick that one and try to render at the device resolution.
Samsung tablets, while they have a large screen, are MDPI (the Galaxy Tab 2, at least) which is the standard density even though it has a large screen. Resources for it should be placed in res/drawable.
I have an app that displays a bitmap in a TextView as a "drawableTop":
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="fill_parent"
android:layout_weight="1"
android:drawableTop="#drawable/bitmap"
android:gravity="center"
android:text="#string/text" />
The bitmap comes from a PNG file in the res/drawable folder, let's call it bitmap.png.
I need to support multiple screen sizes and densities, and want to find the cheapest way (in term of how many PNG files I need to have in res/* folders) to do it.
Various densities: Phones only
I've come to the conclusion that it's enough to have the bitmap.png in res/drawable-xhdpi folder. When being displayed on phones with high density, it will be displayed as is, while on phones will lower density, it will be scaled down automatically.
In other words, no need for having lower resolution versions of the bitmap.png in res/drawable-hdpi, res/drawable-mdpi etc.
Basically only one version of the PNG file is enough for all phones.
Various densities: Tablets as well
Now, the problem arises if I want to support as well tablets: large and xlarge screens.
On tablets, I want the aspect ratio of the icon to be the same as it was on phones: If the bitmap took 1/4 of the screen on phones, I want it to take 1/4 of the screen on tablets. I cannot reuse the same PNG file as before or it would look too small since the tablet just has more pixels... So for tablets I need a higher res PNG. And this applies both to large screen and xlarge screens.
Which results in the need for 3 folders, and 3 versions of the PNG:
1: res/drawable-xhdpi/bitmap.png
2: res/drawable-large-xhdpi/bitmap.png
3: res/drawable-xlarge-xhdpi/bitmap.png
But 1 and 2 are basically lower res versions of 3.
So is there an easy way to have a single folder not 3?
If you want to keep only one image for all resolutions, why not keep in res/drawable and res/drawable-land folders?
If you are talking about cheapest way to do this (not adding more than one image in drawables), then use 9-Patch Images. If you dont know about it, then have a look n learn usage here:
http://www.androiddom.com/2011/05/android-9-patch-image-tutorial.html
http://www.androiddom.com/2011/05/creating-custom-drawn-button.html
Judging from your question, you don't need different orientated images for tablet or phone.
If you want the image to take 1/4 size on both devices, then define your image in your xml as using d(i)p. This will scale the image to the same size on all devices.
d(i)p is scaled relative to 640x480 (or 480x640 resolution for portrait orientation).
So you can calculate different combinations of resolutions to cover 1/4 screen space, such as 320x240 or 240x320
If you want control over the scaling of your image, I would suggest putting the image in an ImageView. This will allow you to use the android:scaleType property to scale your image how you wish.
You can set the background of your TextView to be transparent, and have the ImageView below it contained within a relativeLayout of your desired size to achieve the same effect as you current have, like so:
<RelativeLayout
android:layout_width="320dp"
android:layout_height="240dp"
>
<!-- Images at the top of the xml are drawn on the bottom! -->
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitCenter"
android:src="#drawable/bitmap"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="#string/text"
/>
</RelativeLayout>
Also consider using alias resources.
This will allow you to define an xml file which will act as if it's the real PNG image (and is referenced as such in R.drawable. , but is actually a reference to another image.
From the linked Google documentation:
To create an alias to an existing drawable, use the element.
For example:
<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="#drawable/icon_ca" />
If you save this file as icon.xml (in an alternative resource
directory, such as res/drawable-en-rCA/), it is compiled into a
resource that you can reference as R.drawable.icon, but is actually an
alias for the R.drawable.icon_ca resource (which is saved in
res/drawable/).
I am reading through http://developer.android.com/guide/practices/screens_support.html and trying to understand how pre-scaling works.
I have the following layout file which is optimized for the galaxy tab :
<?xml version="1.0" encoding="UTF-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/relativelayoutmain"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#drawable/mainmenu"
>
<ImageButton
android:id="#+id/start_button"
android:layout_marginLeft="150dip"
android:layout_marginTop ="70dip"
android:layout_width="500dip"
android:layout_height="214dip"
android:background="#drawable/startsession"
android:contentDescription="#string/descbuttonstart"
/>
I have placed the images for the screen in the drawable-ldpi folder. The problem is that when I test the app on a smaller screen (using the emulator) the screen does not scale properly - the button is massive and not positioned correctly.
Is it possible to just have one layout file for all screens and if so what is the secret to getting this working ?
Thank you.
NO , AFAIK its not possible you need to have three differents folders(hdpi,mdpi,ldpi) with different dimensions for buttons,images etc
and also for layout again you need to have three layout folders (layout-small,layout-medium,layout-large)
Screen size and screen resolution are two independent things. the resources in ldpi folder are for low resolution and not necessory small screen sizes. The way I prefer is to define resources for high resolution and let them scale down on others.