欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android屏幕适配讲解与实战

程序员文章站 2022-06-04 11:20:05
文章大纲 一、屏幕适配是什么二、 重要概念讲解三、屏幕适配实战四、项目源码下载 一、屏幕适配是什么 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好。

 
Android屏幕适配讲解与实战

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

5. mdpi,hdpi,xdpi,xxdpi

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

 
Android屏幕适配讲解与实战

三、屏幕适配实战

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>

运行结果如下:

 
Android屏幕适配讲解与实战

得出结论:即使使用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>

运行结果如下:

 
Android屏幕适配讲解与实战

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

 
Android屏幕适配讲解与实战

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

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

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

 
Android屏幕适配讲解与实战

4. 屏幕方向限定符large

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

5.多文件适配(重要)

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

 
Android屏幕适配讲解与实战
 
Android屏幕适配讲解与实战

使用生成器生成对应的文件
生成器代码(使用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文件夹中生成对应的内容,如下图所示:

 
Android屏幕适配讲解与实战
 
Android屏幕适配讲解与实战

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

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

 
Android屏幕适配讲解与实战

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>

运行结果如下图所示:

 
Android屏幕适配讲解与实战

温馨提示:

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

四、项目源码下载

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