Android屏幕适配讲解与实战

简介: Android屏幕适配讲解与实战文章大纲一、屏幕适配是什么二、 重要概念讲解三、屏幕适配实战四、项目源码下载 一、屏幕适配是什么  Android中屏幕适配就是通过对尺寸单位、图片、文字、布局这四种类型的资源进行合理的设计和规划,在布局时合理利用各种类型的资源,让布局拥有适应能力,能在各种设备下保持良好的展现效果。

Android屏幕适配讲解与实战

文章大纲

一、屏幕适配是什么
二、 重要概念讲解
三、屏幕适配实战
四、项目源码下载

 

一、屏幕适配是什么

  Android中屏幕适配就是通过对尺寸单位、图片、文字、布局这四种类型的资源进行合理的设计和规划,在布局时合理利用各种类型的资源,让布局拥有适应能力,能在各种设备下保持良好的展现效果。

二、常见屏幕适配方法介绍

1 屏幕尺寸

  屏幕尺寸是指屏幕对角线的长度,单位是英寸,1英寸=2.54厘米

2 屏幕分辨率

  屏幕分辨率是指横纵向上的像素点数,单位是px,1px=1个像素点,一般以纵向像素横向像素,如19201080,分辨率越高,显示效果越好。

3 屏幕像素密度

  屏幕像素密度是指每英寸上的像素点数,单位是dpi,屏幕像素密度与屏幕尺寸和屏幕分辨率有关。

4. px、dp、sp

(1)安卓里面获取屏幕宽和高,也是以px作为单位的。
(2)在160dpi(即分辨率是480*320)基准下,1dip=1px(也就是px不能适配所有机型),如下图所示,要充满屏幕,箭头的px值是不一样的。1dp=那个机型dpi/160px。所以用dp会比px好。

 

(3)在使用sp(设置文字的)时候,使用偶数,不要使用奇数或者小数,最最推荐的是12.14.18,22sp的文字大小(尽量不要使用12sp以下的大小,用户可能看不清楚)。

5. mdpi,hdpi,xdpi,xxdpi

安卓软件运行时,会自动根据屏幕像素去不同文件夹加载对应图片。

 

三、屏幕适配实战

1. 使用dp设置控件大小,sp设置字体大小(不可行)

activity_main2.xml布局代码如下:

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity"> <Button android:layout_width="200dp" android:layout_height="50dp" android:textSize="20sp" android:text="按钮1"/> <Button android:layout_width="200dp" android:layout_height="50dp" android:layout_marginTop="20dp" android:textSize="20sp" android:text="按钮2"/> </LinearLayout> 

运行结果如下:

 

得出结论:即使使用dp设置控件大小,sp设置字体大小,也是无法适配所有手机的。因为这是谷歌自己的一个标准。dp是根据屏幕像素和分辨率一起来解决的。但是有些手机像素和分辨率不是对应关系,所以不行。

2. weight属性使用

activity_main3.xml布局代码如下:

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" tools:context=".MainActivity"> <Button android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:textSize="20sp" android:text="按钮1"/> <Button android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="2" android:textSize="20sp" android:text="按钮2"/> </LinearLayout> 

运行结果如下:

 

得出结论:采用weight,可以使得组件按屏幕大小进行放大缩小,weight的计算方式如下:

 


如果将xml中的android:layout_weight属性值1和2互换,则结果是相反的,有兴趣伙伴可以下载源码看看效果。

3. 使用自动拉伸位图.9图

什么是.9图
因为Android有太多的分辨率了,当圆角矩形控件在被拉伸放大的时候,圆角的部分就会出现模糊的情况。而点九切图就不同,它可以保证不管你上下还是左右拉伸,放大都可以保持原有的清晰度。
.9图存放位置

 

4. 屏幕方向限定符large

  比如我们想在屏幕竖屏时候加载什么布局,在屏幕横线时候加载什么布局。在手机加载什么布局,在平板电脑加载什么布局。
该文章暂不展开讨论,将在Fragment使用中进行讲解。

5.多文件适配(重要)

  大家经过上面的学习之后,已经知道有些手机像素和分辨率不是对应关系,无法使用dp等单位来解决,那么我们可以以某个常见的屏幕分辨率作为基准,自定义需要适配到的屏幕分辨率的xml文件,如下图所示:

 
 

使用生成器生成对应的文件
生成器代码(使用320*480作为基准)如下:

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintWriter;

/** * Created by 吴晓畅 */ public class GenerateValueFiles { private int baseW; private int baseH; private String dirStr = "./res"; private final static String WTemplate = "<dimen name=\"x{0}\">{1}px</dimen>\n"; private final static String HTemplate = "<dimen name=\"y{0}\">{1}px</dimen>\n"; private final static String VALUE_TEMPLATE = "values-{0}x{1}"; //新增文件的分辨率,以x,x;隔开 private static final String SUPPORT_DIMESION = "320,480;480,800;480,854;540,960;600,1024;720,1184;720,1196;720,1280;768,1024;768,1280;800,1280;1080,1812;1080,1920;1440,2560;"; private String supportStr = SUPPORT_DIMESION; public GenerateValueFiles(int baseX, int baseY, String supportStr) { this.baseW = baseX; this.baseH = baseY; if (!this.supportStr.contains(baseX + "," + baseY)) { this.supportStr += baseX + "," + baseY + ";"; } this.supportStr += validateInput(supportStr); System.out.println(supportStr); File dir = new File(dirStr); if (!dir.exists()) { dir.mkdir(); } System.out.println(dir.getAbsoluteFile()); } private String validateInput(String supportStr) { StringBuffer sb = new StringBuffer(); String[] vals = supportStr.split("_"); int w = -1; int h = -1; String[] wh; for (String val : vals) { try { if (val == null || val.trim().length() == 0) continue; wh = val.split(","); w = Integer.parseInt(wh[0]); h = Integer.parseInt(wh[1]); } catch (Exception e) { System.out.println("skip invalidate params : w,h = " + val); continue; } sb.append(w + "," + h + ";"); } return sb.toString(); } public void generate() { String[] vals = supportStr.split(";"); for (String val : vals) { String[] wh = val.split(","); generateXmlFile(Integer.parseInt(wh[0]), Integer.parseInt(wh[1])); } } private void generateXmlFile(int w, int h) { StringBuffer sbForWidth = new StringBuffer(); sbForWidth.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"); sbForWidth.append("<resources>"); float cellw = w * 1.0f / baseW; System.out.println("width : " + w + "," + baseW + "," + cellw); for (int i = 1; i < baseW; i++) { sbForWidth.append(WTemplate.replace("{0}", i + "").replace("{1}", change(cellw * i) + "")); } sbForWidth.append(WTemplate.replace("{0}", baseW + "").replace("{1}", w + "")); sbForWidth.append("</resources>"); StringBuffer sbForHeight = new StringBuffer(); sbForHeight.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"); sbForHeight.append("<resources>"); float cellh = h *1.0f/ baseH; System.out.println("height : "+ h + "," + baseH + "," + cellh); for (int i = 1; i < baseH; i++) { sbForHeight.append(HTemplate.replace("{0}", i + "").replace("{1}", change(cellh * i) + "")); } sbForHeight.append(HTemplate.replace("{0}", baseH + "").replace("{1}", h + "")); sbForHeight.append("</resources>"); File fileDir = new File(dirStr + File.separator + VALUE_TEMPLATE.replace("{0}", h + "")// .replace("{1}", w + "")); fileDir.mkdir(); File layxFile = new File(fileDir.getAbsolutePath(), "lay_x.xml"); File layyFile = new File(fileDir.getAbsolutePath(), "lay_y.xml"); try { PrintWriter pw = new PrintWriter(new FileOutputStream(layxFile)); pw.print(sbForWidth.toString()); pw.close(); pw = new PrintWriter(new FileOutputStream(layyFile)); pw.print(sbForHeight.toString()); pw.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } } public static float change(float a) { int temp = (int) (a * 100); return temp / 100f; } public static void main(String[] args) { //基准大小,比如320.480,其他则以这个基准进行放大缩小 int baseW = 320; int baseH = 480; String addition = ""; try { if (args.length >= 3) { baseW = Integer.parseInt(args[0]); baseH = Integer.parseInt(args[1]); addition = args[2]; } else if (args.length >= 2) { baseW = Integer.parseInt(args[0]); baseH = Integer.parseInt(args[1]); } else if (args.length >= 1) { addition = args[0]; } } catch (NumberFormatException e) { System.err .println("right input params : java -jar xxx.jar width height w,h_w,h_..._w,h;"); e.printStackTrace(); System.exit(-1); } new GenerateValueFiles(baseW, baseH, addition).generate(); } } 

运行代码,结果会在项目的res文件夹中生成对应的内容,如下图所示:

 
 

温馨提示:上图每个文件夹是以320*480作为基准进行放大缩小后的px值

将上面生成的文件夹复制到实际项目中
复制所有文件夹,右击studio中的res文件夹进行粘贴

 

xml布局中进行引用

activity_main4.xml代码如下:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity"> <Button android:layout_width="@dimen/x200" android:layout_height="@dimen/y30" android:text="按钮1"/> <Button android:layout_width="@dimen/x200" android:layout_height="@dimen/y30" android:layout_marginTop="@dimen/y30" android:text="按钮2"/> </LinearLayout> 

运行结果如下图所示:

 

温馨提示:

  1. 如果运行后发现某个尺寸的屏幕没有是配到,那么可以在生成器中添加对应屏幕尺寸,重新生成文件夹,之后拷贝到项目中即可
  2. 图片适配则可以采用图片放在不同文件夹里面,系统会自动选图,但最好每次切图都包含多种分辨率的图片,例如某一测试的机器是xxhdpi密度的,所以当把图片放在xxhdpi时候(其他文件夹没放),加载时候占用内存是较小的。默认是没有加载任何东西(运行一个空白app)占用的内存。如果加载其他像素下的图片,则会占用很多内容。所以总结来说就是要各个文件夹都放有图片,可以减少占用内存的加载。
 

四、项目源码下载

链接:https://pan.baidu.com/s/1xDRVaSS9Kk9OGLzloBeneA
提取码:0zyt

原文地址https://www.cnblogs.com/WUXIAOCHANG/p/10544545.html

相关文章
|
2月前
|
安全 Android开发 Kotlin
Android经典实战之SurfaceView原理和实践
本文介绍了 `SurfaceView` 这一强大的 UI 组件,尤其适合高性能绘制任务,如视频播放和游戏。文章详细讲解了 `SurfaceView` 的原理、与 `Surface` 类的关系及其实现示例,并强调了使用时需注意的线程安全、生命周期管理和性能优化等问题。
155 8
|
25天前
|
缓存 前端开发 Android开发
Android实战之如何截取Activity或者Fragment的内容?
本文首发于公众号“AntDream”,介绍了如何在Android中截取Activity或Fragment的屏幕内容并保存为图片。包括截取整个Activity、特定控件或区域的方法,以及处理包含RecyclerView的复杂情况。
18 3
|
2月前
|
Android开发 开发者 索引
Android实战经验之如何使用DiffUtil提升RecyclerView的刷新性能
本文介绍如何使用 `DiffUtil` 实现 `RecyclerView` 数据集的高效更新,避免不必要的全局刷新,尤其适用于处理大量数据场景。通过定义 `DiffUtil.Callback`、计算差异并应用到适配器,可以显著提升性能。同时,文章还列举了常见错误及原因,帮助开发者避免陷阱。
176 9
|
1月前
|
Android开发
Android实战之如何快速实现自动轮播图
本文介绍了在 Android 中使用 `ViewPager2` 和自定义适配器实现轮播图的方法,包括添加依赖、布局配置、创建适配器及实现自动轮播等步骤。
22 0
|
2月前
|
开发工具 Android开发 git
Android实战之组件化中如何进行版本控制和依赖管理
本文介绍了 Git Submodules 的功能及其在组件化开发中的应用。Submodules 允许将一个 Git 仓库作为另一个仓库的子目录,有助于保持模块独立、代码重用和版本控制。虽然存在一些缺点,如增加复杂性和初始化时间,但通过最佳实践可以有效利用其优势。
38 3
|
1月前
|
Android开发
Android开发显示头部Bar的需求解决方案--Android应用实战
Android开发显示头部Bar的需求解决方案--Android应用实战
21 0
|
2月前
|
Java Android开发 UED
🧠Android多线程与异步编程实战!告别卡顿,让应用响应如丝般顺滑!🧵
在Android开发中,为应对复杂应用场景和繁重计算任务,多线程与异步编程成为保证UI流畅性的关键。本文将介绍Android中的多线程基础,包括Thread、Handler、Looper、AsyncTask及ExecutorService等,并通过示例代码展示其实用性。AsyncTask适用于简单后台操作,而ExecutorService则能更好地管理复杂并发任务。合理运用这些技术,可显著提升应用性能和用户体验,避免内存泄漏和线程安全问题,确保UI更新顺畅。
92 5
|
2月前
|
Java Android开发 C++
🚀Android NDK开发实战!Java与C++混合编程,打造极致性能体验!📊
在Android应用开发中,追求卓越性能是不变的主题。本文介绍如何利用Android NDK(Native Development Kit)结合Java与C++进行混合编程,提升应用性能。从环境搭建到JNI接口设计,再到实战示例,全面展示NDK的优势与应用技巧,助你打造高性能应用。通过具体案例,如计算斐波那契数列,详细讲解Java与C++的协作流程,帮助开发者掌握NDK开发精髓,实现高效计算与硬件交互。
129 1
|
2月前
|
编解码 前端开发 Android开发
Android经典实战之TextureView原理和高级用法
本文介绍了 `TextureView` 的原理和特点,包括其硬件加速渲染的优势及与其他视图叠加使用的灵活性,并提供了视频播放和自定义绘制的示例代码。通过合理管理生命周期和资源,`TextureView` 可实现高效流畅的图形和视频渲染。
224 12
|
2月前
|
Android开发 容器
Android经典实战之如何获取View和ViewGroup的中心点
本文介绍了在Android中如何获取`View`和`ViewGroup`的中心点坐标,包括计算相对坐标和屏幕上的绝对坐标,并提供了示例代码。特别注意在视图未完成测量时可能出现的宽高为0的问题及解决方案。
47 7