android屏幕适配总结

内容简介.png

Android系统碎片化、Android机型屏幕尺寸碎片化、Android屏幕分辨率碎片化造成同一元素在不同手机上显示的效果不同,也就是我们要适配的根本原因。这里主要是对不同屏幕尺寸和不同屏幕密度的适配。

基础

屏幕尺寸:手机对角线的长度,单位是英寸,1英寸=2.54cm

屏幕分辨率:手机横向和纵向上的像素点数,单位是px,1px就是一个像素,一般表示形式:纵向像素*横向像素,如1920 * 1080

屏幕像素密度:每英寸的像素点数,单位是dpi,"dot per inch"的缩写

屏幕像素密度计算公式.png

我的手机是魅蓝max,屏幕分辨率:1920 * 1080,屏幕尺寸:6.0,计算屏幕像素密度:

屏幕像素密度= √(1920^2 + 1080^2)/ 6.0 = 367.15 dpi,手机官方:368dpi。

dip、dp、sp

dip与dp是一个意思,称为密度无关像素,android手机专属的单位,与px有什么关系呢?在android中,规定以160dpi为基准,也就是在手机屏幕像素密度为160的时候,1dp = 1px,然后其他的屏幕像素密度之间需要进行比例换算。比如屏幕像素密度为320dpi时,就这么计算,1dp / 160 dpi * 320 dpi = 2 px.

sp是独立比例像素,可以根据文字大小首选项自动进行缩放,最好用复数,

mdpi hdpi xhdpi xxdpi xxxdpi用来区分不同的像素密度

名称 像素密度范围
mdpi 120dpi~160dpi
hdpi 160dpi~240dpi
xhdpi 240dpi~320dpi
xxhdpi 320dpi~480dpi
xxxhdpi 480dpi~640dpi

图片像素换算比例:
mdpi:hdpi:xhdpi:xxhdpi:xxxhdpi = 2:3:4:6:8

根据要适配的机型的屏幕像素密度切图,将图标放入相应像素密度的文件夹中。

屏幕像素匹配

做前端的的朋友都不会遇到像android开发猿们遇到的这种适配问题,因为有一种百分比的东西。那我们也可以使用百分比来做适配,假如我们以480 * 320作为基准,那么在480 * 320像素的手机上显示一张图片,这时候1px就是一像素,用x1表示,x表示为宽,1表示1像素,也就是x1=1px;如果应用安装在960 * 640的手机上的话,我们想让应用呈现出与480 * 320一样的效果,那么这张图片的宽高都必须乘以2才行。可以阅读洋哥原文


/**
 * email:naildingmouren@gmail.com
 * 针对各种屏幕密度的匹配
 * 在480*320的设备上,x2就代表2.0px,y2就代表3.0px。这里以320*480为基准
 * 在800×480的设备上,x2就代表3.0px,y2就代表3.33px。
 */

public class MakeXml {
    //项目中res文件夹的路径
    private final static String rootPath = "D:\\AndroidSampleAll\\app\\src\\main\\res\\values-{0}x{1}\\";
    //手机屏幕像素的基准
    private final static float dw = 320f;
    private final static float dh = 480f;

    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";

    public static void main(String[] args) {
        makeString(320, 480);
        makeString(480,800);
        makeString(480, 854);
        makeString(540, 960);
        makeString(600, 1024);
        makeString(720, 1184);
        makeString(720, 1196);
        makeString(720, 1280);
        makeString(768, 1024);
        makeString(800, 1280);
        makeString(1080, 1812);
        makeString(1080, 1920);
        makeString(1440, 2560);
    }

    public static void makeString(int w, int h) {

        StringBuffer sb = new StringBuffer();
        sb.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
        sb.append("<resources>");
        float cellw = w / dw;
        for (int i = 1; i < 320; i++) {
            sb.append(WTemplate.replace("{0}", i + "").replace("{1}",
                    change(cellw * i) + ""));
        }
        sb.append(WTemplate.replace("{0}", "320").replace("{1}", w + ""));
        sb.append("</resources>");

        StringBuffer sb2 = new StringBuffer();
        sb2.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
        sb2.append("<resources>");
        float cellh = h / dh;
        for (int i = 1; i < 480; i++) {
            sb2.append(HTemplate.replace("{0}", i + "").replace("{1}",
                    change(cellh * i) + ""));
        }
        sb2.append(HTemplate.replace("{0}", "480").replace("{1}", h + ""));
        sb2.append("</resources>");

        String path = rootPath.replace("{0}", h + "").replace("{1}", w + "");
        File rootFile = new File(path);
        if (!rootFile.exists()) {
            rootFile.mkdirs();
        }
        File layxFile = new File(path + "lay_x.xml");
        File layyFile = new File(path + "lay_y.xml");
        try {
            PrintWriter pw = new PrintWriter(new FileOutputStream(layxFile));
            pw.print(sb.toString());
            pw.close();
            pw = new PrintWriter(new FileOutputStream(layyFile));
            pw.print(sb2.toString());
            pw.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

    }

    public static float change(float a) {
        int temp = (int) (a * 100);
        return temp / 100f;
    }
}

屏幕像素密度适配.png

屏幕尺寸匹配

布局匹配

  • 为了 使布局元素自适应屏幕尺寸,禁用绝对布局(AbsoluteLayout),建议使用相对布局(RelativeLayout),其他布局根据需求选择。相对布局通过相对位置排列,屏幕大小变化也是没有影响的
  • 为了可以根据屏幕的配置或设备来加载相应的布局(适配平板电脑、电视tv时),使用限定符来实现。
* 在平板电脑和电视的屏幕(>7英寸)上:实施“双面板”模式以同时显示更多内容
* 在手机较小的屏幕上:使用单面板分别显示内容

限定符.png

尺寸限定符只适合android 3.2之前,large不能具体指定多大,所以android 3.2以后引入了最小宽度限定符,单位是dp。为了后期的维护,引入了“布局别名”的机制,同时维护sw600dp和large的两套xml平板布局,解决重复的问题。

取以下例子:

小屏幕, 竖屏: 单面板
小屏幕, 横屏: 单面板
7 英寸平板电脑,纵向:单面板,带操作栏
7 英寸平板电脑,横向:双面板,宽,带操作栏
10 英寸平板电脑,纵向:双面板,窄,带操作栏
10 英寸平板电脑,横向:双面板,宽,带操作栏
电视,横向:双面板,宽,带操作栏

res/layout/目录下的xml资源:

res/layout/onepane.xml:(单面板)

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    android:orientation="vertical"  
    android:layout_width="match_parent"  
    android:layout_height="match_parent">  
  
    <fragment android:id="@+id/headlines"  
              android:layout_height="fill_parent"  
              android:name="com.example.android.newsreader.HeadlinesFragment"  
              android:layout_width="match_parent" />  
</LinearLayout>

res/layout/onepane_with_bar.xml:(单面板带操作栏)

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">
    <!--操作栏-->
    <LinearLayout android:layout_width="match_parent"
                  android:id="@+id/linearLayout1"
                  android:gravity="center"
                  android:layout_height="50dp">
        <ImageView android:id="@+id/imageView1"
                   android:layout_height="wrap_content"
                   android:layout_width="wrap_content"
                   android:src="@mipmap/ic_launcher_round"
                   android:paddingRight="30dp"
                   android:layout_gravity="left"
                   android:layout_weight="0" />
        <View android:layout_height="wrap_content"
              android:id="@+id/view1"
              android:layout_width="wrap_content"
              android:layout_weight="1" />
        <Button android:id="@+id/categorybutton"
                android:background="@android:color/holo_red_light"
                android:layout_height="match_parent"
                android:layout_weight="0"
                android:layout_width="120dp"
                android:text="BUTTON"
                />
    </LinearLayout>

    <fragment android:id="@+id/headlines"
              android:layout_height="fill_parent"
              android:layout_width="match_parent" />
</LinearLayout>


res/layout/twopanes.xml:(双面板,宽布局)

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal">
    <fragment android:id="@+id/headlines"
              android:layout_height="fill_parent"
              android:name="com.example.android.newsreader.HeadlinesFragment"
              android:layout_width="400dp"
              android:layout_marginRight="10dp"/>
    <fragment android:id="@+id/article"
              android:layout_height="fill_parent"
              android:name="com.example.android.newsreader.ArticleFragment"
              android:layout_width="fill_parent" />
</LinearLayout>
res/layout/twopanes_narrow.xml:(双面板,窄布局)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal">
    <fragment android:id="@+id/headlines"
              android:layout_height="fill_parent"
              android:name="com.example.android.newsreader.HeadlinesFragment"
              android:layout_width="200dp"
              android:layout_marginRight="10dp"/>
    <fragment android:id="@+id/article"
              android:layout_height="fill_parent"
              android:name="com.example.android.newsreader.ArticleFragment"
              android:layout_width="fill_parent" />
</LinearLayout>

系统根据设备的情况自动加载布局

res/values/layouts.xml:(默认布局)

<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--默认布局
    bool 可以获取boolean值动态判断当前适配的布局
-->
    <item name="main_layout" type="layout">@layout/onepane</item>
    <bool name="has_two_panes">false</bool>

</resources>
res/values-sw600dp-land/layouts.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--大屏  横向、双面板、宽  android3.2后-->
    <item name="main_layout" type="layout">@layout/twopanes</item>
    <bool name="has_two_panes">true</bool>
</resources>
res/values-sw600dp-port/layouts.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--大屏、纵向、单面板带操作栏  android3.2后-->
    <item name="main_layout" type="layout">@layout/onepane_with_bar</item>
    <bool name="has_two_panes">false</bool>
</resources>
res/values-large-land/layouts.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--大屏、横向、双面板、宽  android3.2前-->
    <item name="main_layout" type="layout">@layout/twopanes</item>
    <bool name="has_two_panes">true</bool>
</resources>
res/values-large-port/layouts.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--大屏 纵向 单面板带操作栏  android3.2前-->
    <item name="main_layout" type="layout">@layout/onepane_with_bar</item>
    <bool name="has_two_panes">false</bool>
</resources>



实现自适应用户界面流程(紧接限定符实现的布局适配)

以上的布局匹配设置好以后,我们要实现自适应的用户界面流程,因为设备特点不同的话,就就会加载不同的布局,相应的用户界面流程可能会有所不同。例如,如果应用处于双面板模式下,点击左侧面板上的项即可直接在右侧面板上显示相关内容;而如果该应用处于单面板模式下,点击相关的内容应该跳转到另外一个Activity进行后续的处理。所以我们应该按照下面的流程,一步步的完成自适应界面的实现。

1.确定当前布局

由于每种布局的实施都会稍有不同,因此我们需要先确定当前向用户显示的布局。例如,我们可以先了解用户所处的是“单面板”模式还是“双面板”模式。要做到这一点,可以通过查询指定视图是否存在以及是否已显示出来。

public class NewsReaderActivity extends FragmentActivity {
    boolean mIsDualPane;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_layout);

        View articleView = findViewById(R.id.article);
        mIsDualPane = articleView != null && articleView.getVisibility() == View.VISIBLE;
    }
}

2.根据当前布局作出响应

有些操作可能会由于当前布局而产生不同的操作结果。例如,在新闻阅读器示例中,如果用户界面处于双面板模式下,那么点击标题列表中的标题就会在右侧面板中打开相应报道;但如果用户界面处于单面板模式下,那么上述操作就会启动一个独立活动。

//自定义的点击回调
@Override
public void onHeadlineSelected(int index) {
    mArtIndex = index;
    if (mIsDualPane) {
        /*在右边面板直接显示相应的内容,双面板 */
        mArticleFragment.displayArticle(mCurrentCat.getArticle(index));
    } else {
        /* 开启一个新的activity来显示内容,单面板 */
        Intent intent = new Intent(this, ArticleActivity.class);
        intent.putExtra("catIndex", mCatIndex);
        intent.putExtra("artIndex", index);
        startActivity(intent);
    }
}

3.处理屏幕旋转变化

如果我们使用独立Activity实施界面的独立部分,那么请注意,我们可能需要对特定配置变化(例如屏幕方向的变化)做出响应,以便保持界面的一致性。

布局组件匹配

为了让布局组件自适应屏幕尺寸。
LinearLayout中权重属性的使用可以通过百分比的形式对组件进行空间匹配。

图片资源匹配

为了让图片资源在不同屏幕密度上显示相同的像素效果,可以使用.9图,可以自动拉伸图。


参考:Android 屏幕适配:最全面的解决方案

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,817评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,329评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,354评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,498评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,600评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,829评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,979评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,722评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,189评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,519评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,654评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,329评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,940评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,762评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,993评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,382评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,543评论 2 349

推荐阅读更多精彩内容