Pixel-perfect layout of Android layouts
UPD: Post controversial, but a lot of valuable advice in the comments

UPD: I am sincerely sorry, I, as a marketer, did not fully understand the topic and did not convey the main idea of the post. This method was used on a project where the customer provided the design and his requirement was that he look the same on all devices. Based on this, such a layout method appeared, it is controversial and does not always fit.
Many thanks to the commentators for correcting me and expressing my point of view on the correct layout. If you do not need an absolute coincidence of layouts, a summary of the tips from the comments:
- Android Design in Action: Responsive Design video in English how Android application design should stretch
- Supporting Multiple Screens basic information about the different screen sizes of Android devices, DPI and how to adapt applications to multiple screens.
- Designing for Multiple Screens are three great lessons from Google about supporting different screen resolutions and DPI, I recommend starting with it.
- silentnuke font scaling tip
Use different font sizes for different screen resolutions.
in values \ dens.xml text_size = 16sp
in values-sw600dp \ dens.xml text_size = 20sp
in values-sw720dp \ dens.xml text_size = 24sp
If necessary, take a look at our idea in the post.
On the iPhone layout, absolutely and only two screens of iPhone 4 and iPhone 5 are set. We draw two layouts, write an application and apply translucent screenshots to the layouts. There are no problems, the will of the designer is clear, it can be verified by the developer, the tester, or even the build server.
Under Android, we have two problems: you cannot draw an infinite number of layouts and you cannot compare an infinite number of devices with a finite number of layouts. Designers check manually. Developers often have no idea how to properly stretch elements and scale fonts. The number of iterations tends to infinity.
To streamline the chaos, we came to the following layout algorithm. Layouts are drawn and layout for any flagship full-hd phone. On the rest they adapt beautifully. The finished application is checked by the designer on popular smartphone models. The method works for all phones; tablets (> 6.5 inches) require separate layouts and layout.
At hand, I only have the Nexus 4 take its screen characteristics as an example.
Layouts of a fake portfolio application that we will typeset (full-size by click).



Layout
We do the main layout through the nested LinearLayout. The sizes of elements and blocks in pixels are transferred from the layout to weight and weightSum, respectively. Indentation we impose FrameLayout or in the necessary places we add Gravity .
For example, we make up the cell of the list of applications:

Further we need a lot of DisplayMetrics magic, we will write a static helper for it.
public class S {
private static final int ORIGINAL_VIEW_WIDTH = 768;
private static final int ORIGINAL_VIEW_HEIGHT = 1184;
private static final int ORIGINAL_VIEW_DIAGONAL = calcDiagonal(ORIGINAL_VIEW_WIDTH, ORIGINAL_VIEW_HEIGHT);
private static int mWidth;
private static int mHeight;
private static int mDiagonal;
private static float mDensity;
static {
DisplayMetrics metrics = TouchinApp.getContext().getResources().getDisplayMetrics();
mWidth = metrics.widthPixels;
mHeight = metrics.heightPixels;
mDiagonal = calcDiagonal(mWidth, mHeight);
mDensity = metrics.density;
}
public static int hScale(int value){
return (int)Math.round(value * mWidth / (float) ORIGINAL_VIEW_WIDTH);
}
public static int vScale(int value){
return (int)Math.round(value * mHeight / (float) ORIGINAL_VIEW_HEIGHT);
}
public static int dScale(int value){
return (int)Math.round(value * mDiagonal / (float) ORIGINAL_VIEW_DIAGONAL);
}
public static int pxFromDp(int dp){
return (int)Math.round(dp * mDensity);
}
private static int calcDiagonal(int width, int height){
return (int)Math.round(Math.sqrt(width * width + height * height));
}
}1184 is the height of the Nexus 4 without buttons, 768 is the width. These values are used to find out how many times the height and width of the device on which the application is running differ from the reference.
ScrollView and List
The approach with weightSum is not acceptable for scrollable elements; their internal size along the scroll is unlimited. For layout ScrollView and List we need to set their sizes in the code (130 - the height of the list item).
if (view == null) {
view = mInflater.inflate(R.layout.item_app_list, viewGroup, false);
view.setLayoutParams(new AbsListView.LayoutParams (ViewGroup.LayoutParams.MATCH_PARENT, S.dScale(130)));
}And then you can apply the trick with weightSum.
Images
The size of the application icons is set in the code:
view.findViewById(R.id.appImg).setLayoutParams(new LinearLayout.LayoutParams(S.dScale(240) - S.pxFromDp(20), S.dScale(240) - S.pxFromDp(20)));Where 240 is the height of the list item, 20 is the indentation height above and below.
Fonts
Android does not provide a unit proportional to the size of the screen. Font sizes are calculated based on the diagonal of the device:
textSizePx = originalTextSizePx * (deviceDiagonalPx / originalDeviceDiagonalPx )Yes, the font size will have to be set in the code (36 font size in pixels on the original layout).
titleTxt.setTextSize(TypedValue.COMPLEX_UNIT_PX, S.dScale(36));Graphics Tips
1. Use Nine-patch wherever possible, where impossible — redraw the design.
2. Draw simple elements using Shape
3. Avoid scaling images in runtime
Nine-patch is a graphic resource containing meta-information about how it should be stretched. Read more in the Android documentation or on Habr .
Nine-patch needs to be cut for all dpi: ldpi mdpi tvdpi hdpi, xhdpi, xxhdpi. Scaling resources while the application is running is bad, and scaling Nine-Patch leads to unexpected artifacts. In no case do not indent in the Nine-patch, they are formed by separate layout elements to stretch in proportion to the content.

Shape
If the resource is easily decomposed into simple geometric shapes and gradients, it is better to use xml-shape instead of slicing . For example, let's draw a background frame around the project in the list, which we cut above as Nine-patch.

Images
Graphics scaling by Android is a time-consuming and memory-intensive operation. Pictures inside Android are processed as a bitmap. For example, our logo in the size of 500x500 from a splash screen is unpacked in a 1mb bitmap ( 4 bytes per pixel ), when scaling, another bitmap is created, say 500kb. Or 1.5mb of the available 24mb per process . We have repeatedly faced with a lack of memory in graphics-rich projects.
Therefore, I propose to supply pictures that can not be described with either Nine-patch or Shape in the application as a huge resource in the nodpi folder and at the first start up the image to the desired size and cache the result. This will allow us to speed up the application (apart from the first launch) and reduce memory consumption.
For complex resources loaded from the server (application icons on our layouts), the ideal option is if the server will give pictures of any size. As, for example, done on the Stream project . The application calculates the desired image size for the smartphone screen where it is running, and requests them from the server.
http:///media/img/movies/vposter/plain/22741680/<любая ширина px>_<любая высота px>.jpg PS tips are invented and the basis of the post was written by our Android guru Lesha, thank him so much!
And how do you recommend layout layouts for Android? How many layouts does the designer draw? How do you handle graphic resources?
Subscribe to our habr-blog (the button in the upper right). Every Thursday, interesting articles about mobile development, marketing and the mobile studio business. Next article (September 5) “C # async on iOS and Android”