android UI适配简单记录三

作为一个开发人员,屁股决定脑袋,偶尔需要换位思考,站在UI设计人员的角度考虑下,想想UI设计是怎么来的。

这里可以先简单看下 Material Design,了解下ui设计。
https://material.io/design/layout/understanding-layout.html#usage

Material是一个适应性指南,组件和工具系统,支持用户界面设计的最佳实践。在开源代码的支持下,Material简化了设计人员和开发人员之间的协作,并帮助团队快速构建精美的产品。

Material上的示例标识的单位是dp,但是现实中可能你拿到的图片标识单位还是px。


例如下图这种图。

图片出处:
Android 应用设计开发有没有更好的 UI 适配方法? - captain的回答 - 知乎
https://www.zhihu.com/question/22181844/answer/58124185

想象一下,如何画一张这样简单的界面的图并标识尺寸。
打开电脑自带画图软件,新建一个400x400像素大小的文件(随便画画),单击查看,勾选标尺和状态栏,网格线。

image.png

这里只是简单画一画,并不是很精准。但是在画图的过程中加深了对px以及ui设计的了解。专业的画图工具功能会更全,但是一般都收费。


首先,这张图上的单位都是px,因为图片设计基本单位是px,要如何把px换成dp呢?


image.png
以下部分出处:
安卓UI图px标注适配攻略:批量转化px为dp
https://blog.csdn.net/zengd0/article/details/52464627

px转dp的公式:dp = px/density
density是设备密度,有了设备密度,我们才可以将px转为dp。
这时候你可能会想,Android系统不是提供了api供我们转换吗,或者Android系统获取设备密度:context.getResources().getDisplayMetrics().density,然后将UI图的px去除以desity?
非也。density值是获取了,但是请问UI图上的px值是按照你的手机来标注的吗?如果UI图是以iPhone6手机(4.7寸屏幕,分辨率1334*750)为基准进行设计的,那么你用这个px值去除以你自己安卓手机的设备密度,怎么可能对。
也就是说,我们要获取UI图的设备密度(density)。

这里说的设备密度是指density 。
计算公式:density = PPI/160。
ppi计算公式如下图。

image.png

由于示例图片在设计的时候是适配640 * 1136机型的图,屏幕尺寸应该是4.0英寸(根据分辨率搜索所得)。因此ppi ≈ 326。
density = 326/160 = 2.0。
所以为640 * 1136机型设计的ui图中, 一个 宽度为 50 px的图形转换 dp : dp =50 px/2.0 = 25dp。

640 * 1136并不是主流机型,这里以市面上较为主流720 * 1280机型分辨率为例,简单看下如何适配分辨率720 * 1280的屏幕。
这里计算下720 * 1280 与640 * 1136的宽高比率。720/640 = 1.125 1280/1136 ≈ 1.127,屏幕分辨率比率几乎相等,就按1.125 去计算,进行缩放。50 px应该 乘以 1.125, 50 px * 1.125 = 56.25px。

640 x 1136中宽度为 50 px的图形在720 x1280 中对应的图形大小是56.25px。
而720 * 1280的屏幕,缩放因子density一般是2.0,因此
dp = 56.25px / 2.0 ≈ 28.13dp 。因此在控件里写28.13dp。

如果不能理解可以看下下面的例子。
打开window自带画图工具,新建一个200 x 200 像素大小的图,以中心为原点画一个宽度100px的正方形,中心坐标(100,100),左上角坐标(50,50),右下角坐标(150,150)。


image.png

然后重新调整大小至800 x 800 px,扩大4倍。



此时,正方形宽度变成了400px ,中心坐标(400,400),左上角坐标(200,200),右下角坐标(600,600)。随着图片大小放大,正方形大小也放大了。不同分辨率中,正方形的宽度不一致(可以把正方形想象成如TextView、Button之类的控件)。
image.png

在有su权限的设备上,执行命令,将屏幕分辨率调整至

adb shell wm size 200x200

由于这台设备带有底部虚拟键并且android默认带状态栏和工具栏,所以需要这里先隐藏下。

//因此状态栏和底部导航栏
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        View decorView = getWindow().getDecorView();
        int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_FULLSCREEN;
        decorView.setSystemUiVisibility(uiOptions);
    }
}

因此工具栏则需要修改manifest文件中,application节点下的 android:theme,修改结果如下所示。

 android:theme="@style/Theme.AppCompat.Light.NoActionBar"

xml文件如所示。由于设备density为1.0,所以layout_width写100dp。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center">

  <TextView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:text="test"
        android:textSize="12sp"
        android:gravity="center"
        android:background="@color/colorPrimary"/>

</LinearLayout>

运行结果如下所示。白色部分内为界面内,黑色部分为界外,黑色部分可忽略。


此时再将屏幕分辨率调到800x800

adb shell wm size 800x800

屏幕上会变成如下所示。



可以发现控件宽度为100px,要想使控件宽度为400px,此时就需要修改xml文件了。将layout_width和textSize都放大4倍。

  <TextView
        android:layout_width="400dp"
        android:layout_height="400dp"
        android:text="test"
        android:textSize="48sp"
        android:gravity="center"
        android:background="@color/colorPrimary"/>

运行结果如下所示


将截屏图片与设计图片简单对比,大小基本一致,控件的宽度达到了设计图的宽度为400px的效果。


这是比较极端的情况,仅用于举例。

但是其实刚刚将屏幕分辨率调到800x800后,要想使控件宽度为400px,除了修改xml文件,还有另一种修改方法:修改屏密度。
首先将layout_width和layout_height改回100dp,textSize改回12sp。

    <TextView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:text="test"
        android:textSize="12sp"
        android:gravity="center"
        android:background="@color/colorPrimary"/>

然后运行以下命令修改dpi。

//160*4 = 640 这是设置android中的Dpi 值
adb shell wm density 640

运行该命令可以修改屏幕密度。
此时缩放因子density = dpi/160 = 4。
又 px = dp * density
所以当layout_width为100dp时, 控件的实际物理大小为100dp*4 即 400px。
运行结果如下所示,与方法一结果一致。


这个例子很直观的显示出,UI受density和分辨率的影响。

我们先控制单一变量去思考适配ui。
先以分辨率为变量为例进行说明。
由于UI受屏幕分辨率影响,屏幕分辨率限定符法就出现了。这个方法我真的没有找到出处。
屏幕分辨率限定符法,即每种屏幕分辨率的设备需要定义一套 dimens.xml 文件。这种方法也有人称之为,百分比适配方法。
以某一分辨率为基准,生成所有分辨率对应像素数列表。
现在我们以200x200的分辨率为基准:
将屏幕的宽度分为200份,取值为x1~x200
将屏幕的高度分为200份,取值为y1~y200
在values目录下新建lay_x.xml和lay_y.xml文件。

简单来说,就是将 android:layout_width,android:layout_height, android:textSize等尺寸大小,按不同的分辨率,定义在不同的values下。

在android studio中新建MakeXml .java文件,根据MakeXml 生成xml文件。
代码如下。由于我手上屏幕为横屏,所以稍微修改了一点,以480x320为基准分辨率。

代码出处:
Android-屏幕适配全攻略(绝对详细)(二)
https://www.jianshu.com/p/ad563d169871

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

public class MakeXml {
    private final static String rootPath = "D:\\layoutroot\\values-{0}x{1}\\";

  /*  private final static float dw = 320f;
    private final static float dh = 480f;*/
  private final static float dw = 480f;
    private final static float dh = 320f;


    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);*/
        makeString(480, 320);
        makeString(800, 480);
        makeString(854, 480);
        makeString(960, 540);
        makeString(1024 ,600 );
        makeString(1184 ,720 );
        makeString(1196 ,720 );
        makeString(1280 , 720);
        makeString( 1024 ,768 );
        makeString(1280 ,800 );
        makeString( 1812 ,1080 );
        makeString(1920 ,1080 );
        makeString(2560 ,1440 );
    }

    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 < 480; i++) {
            sb.append(WTemplate.replace("{0}", i + "").replace("{1}",
                    change(cellw * i) + ""));
        }
        sb.append(WTemplate.replace("{0}", "480").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 < 320; i++) {
            sb2.append(HTemplate.replace("{0}", i + "").replace("{1}",
                    change(cellh * i) + ""));
        }
        sb2.append(HTemplate.replace("{0}", "320").replace("{1}", h + ""));
        sb2.append("</resources>");

        String path = rootPath.replace("{0}", w + "").replace("{1}", h + "");
        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;
    }

}

选中java文件后右键,然后选择run 'MakeXml.main()'。


image.png

将生成的文件copy至src\main\res目录下。


image.png

image.png

又由于必须在默认values里面也创建对应默认lay_x.xml和lay_y.xml文件。
由于我这里以480x320为基准分辨率,因此将values-480x320下的lay_x.xml和lay_y.xml中的px先全部通过notepad++替换成dp,再放到values目录下。


image.png

在activity中增加测试textview控件大小的代码。

 mTextView.post(new Runnable() {
            @Override
            public void run() {
                int width = mTextView.getWidth();
                int height = mTextView.getHeight();
                Log.d(TAG,"width = " + width + " , height =" + height);
            }
        });

然后将TextView属性改为如下所示。

   <TextView
        android:id="@+id/test_text_view"
        android:layout_width="@dimen/x240"
        android:layout_height="@dimen/y160"
        android:background="@color/colorPrimary"
        android:gravity="center"
        android:text="test" />

运行程序后,不断通过wm size命令修改屏幕分辨率,并且运行程序。运行结果如下。

07-05 17:19:06.547 3859-3859/com.demo.myapplication D/MainActivity: screenWidth = 480 , screenHeight =320
07-05 17:19:06.641 3859-3859/com.demo.myapplication D/MainActivity: width = 240 , height =160
07-05 17:19:31.083 3859-3859/com.demo.myapplication D/MainActivity: screenWidth = 480 , screenHeight =800
07-05 17:19:31.132 3859-3859/com.demo.myapplication D/MainActivity: width = 240 , height =160
07-05 17:19:31.468 3859-3859/com.demo.myapplication D/MainActivity: screenWidth = 800 , screenHeight =480
07-05 17:19:31.517 3859-3859/com.demo.myapplication D/MainActivity: width = 240 , height =160
07-05 17:20:11.736 3859-3859/com.demo.myapplication D/MainActivity: screenWidth = 854 , screenHeight =480
07-05 17:20:11.782 3859-3859/com.demo.myapplication D/MainActivity: width = 240 , height =160
07-05 17:20:26.221 3859-3859/com.demo.myapplication D/MainActivity: screenWidth = 960 , screenHeight =540
07-05 17:20:26.259 3859-3859/com.demo.myapplication D/MainActivity: width = 427 , height =240
07-05 17:20:51.721 3859-3859/com.demo.myapplication D/MainActivity: screenWidth = 1024 , screenHeight =600
07-05 17:20:51.774 3859-3859/com.demo.myapplication D/MainActivity: width = 480 , height =270
07-05 17:21:23.098 3859-3859/com.demo.myapplication D/MainActivity: screenWidth = 1024 , screenHeight =768
07-05 17:21:23.138 3859-3859/com.demo.myapplication D/MainActivity: width = 512 , height =300
07-05 17:21:57.911 3859-3859/com.demo.myapplication D/MainActivity: screenWidth = 1184 , screenHeight =720
07-05 17:21:57.959 3859-3859/com.demo.myapplication D/MainActivity: width = 512 , height =300
07-05 17:22:36.078 3859-3859/com.demo.myapplication D/MainActivity: screenWidth = 1196 , screenHeight =720
07-05 17:22:36.128 3859-3859/com.demo.myapplication D/MainActivity: width = 512 , height =300
07-05 17:22:51.951 3859-3859/com.demo.myapplication D/MainActivity: screenWidth = 1280 , screenHeight =720
07-05 17:22:52.002 3859-3859/com.demo.myapplication D/MainActivity: width = 512 , height =300
07-05 17:22:59.092 3859-3859/com.demo.myapplication D/MainActivity: screenWidth = 1280 , screenHeight =800
07-05 17:22:59.142 3859-3859/com.demo.myapplication D/MainActivity: width = 640 , height =360
07-05 17:23:18.623 3859-3859/com.demo.myapplication D/MainActivity: screenWidth = 1812 , screenHeight =1080
07-05 17:23:18.678 3859-3859/com.demo.myapplication D/MainActivity: width = 640 , height =400
07-05 17:23:48.159 3859-3859/com.demo.myapplication D/MainActivity: screenWidth = 1920 , screenHeight =1080
07-05 17:23:48.200 3859-3859/com.demo.myapplication D/MainActivity: width = 640 , height =400
07-05 17:24:16.942 3859-3859/com.demo.myapplication D/MainActivity: screenWidth = 2560 , screenHeight =1440
07-05 17:24:16.997 3859-3859/com.demo.myapplication D/MainActivity: width = 960 , height =540

但是从运行结果上来看,效果并不理想。
screenWidth = 800 , screenHeight =480时, width = 240 , height =160。
但是我定义的values-800x480下。

values-800x480
lay_x.xml
<dimen name="x240">400.0px</dimen>
lay_y.xml
<dimen name="y160">240.0px</dimen>

按定义去工作,结果应该是 width = 400 , height =240。这里values-800x480并没有生效。
而且wm size 2560x1440时, width = 960 , height =540,匹配的是values-1920x1080下的dimen。

values-1920x1080
lay_x.xml
<dimen name="x240">960.0px</dimen>
lay_y.xml
<dimen name="y160">540.0px</dimen>
values-2560x1440
lay_x.xml
<dimen name="x240">1280.0px</dimen>
lay_y.xml
<dimen name="y160">720.0px</dimen>

分辨率限定符根本没有按我的想象去工作!!!
后来我找到了解释,一直以来其实都误用了分辨率限定符。
详细解释可以看这里。
被误用的屏幕分辨率限定符
https://www.jianshu.com/p/b0253e457031

这个方案后面基本上也没有人推荐了,基本上退出历史舞台了。

现在来看看以density为变量为例进行ui适配。这种方案最近比较火,由 字节跳动技术团队 提出。
https://mp.weixin.qq.com/s/d9QCoBP6kV9VSWvVldVVwA

可以参考这个链接,简单看看下效果。
Android今日头条UI适配完善版
https://www.jianshu.com/p/41930fde7aac
这个方案从竖屏测试结果上来看,效果还不错。不过横屏仍需要另外布局。

上面介绍了屏幕分辨率和density适配ui2种方法,其实还有一种写法,就是把TextView嵌套进LinearLayout然后通过android:layout_weight这个属性让其控件大小自适应。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="2">

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1" />

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="2">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@color/colorPrimary"
                android:gravity="center"
                android:text="test" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />
</LinearLayout>

200x200和800x800分辨率时运行结果分别如下图所示。


200x200
800x800

运行结果控件大小虽然效果一致,但是字体大小效果不一致,可以代码中动态调节,或者去定义 不同dimens.xml 文件。
先来看看通过代码去适配的做法。
大致思路是以200x200为基准,设置一个大小,例如12sp.
在values下新建一个dimens文件。

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <dimen name="text_size">12sp</dimen>
</resources>

然后再获取屏幕分辨率,例如800x800,去计算放大或缩小比例,例如这里是倍数是4,通过setTextSize设置大小为48sp。

        DisplayMetrics dm = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getRealMetrics(dm);
        int screenWidth = dm.widthPixels;
        int screenHeight = dm.heightPixels;
        Log.d(TAG,"screenWidth = " + screenWidth + " , screenHeight =" + screenHeight);

        int dimen = getResources().getDimensionPixelSize(R.dimen.text_size);
        if (screenWidth != 200){
            dimen = dimen * screenWidth/200;
            Log.d(TAG,"dimen = " + dimen);
        }
        mTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX,dimen);

运行结果如下,字体变大了。



看完代码中动态调节的方式,下面来看看通过定义不同dimens.xml 文件去适配的方式。
除了前面的 根据 屏幕分辨率限定符 定义 dimens,还可以用另一种方式定义dimens,那就是 android中的 sw(smallestWidth)限定符 (这个限定符android官网是有介绍的)。
下面来介绍下给 smallestWidth(最小宽度) 限定符方式定义dimens.xml 文件,这种方式定义dimens可以使apk体积更小。
先看看什么是smallestWidth。
android中定义了配置修饰符-sw。例如-sw600dp。

//以下部分出处:android编程权威指南
配置修饰符-sw600dp的作用是:如果设备尺寸大于某个指定值,就使用对应的资源文件。
sw是smallest width(最小宽度)的缩写。虽然字面上是宽度的含义,但它实际指的是屏幕最小尺寸(dimension),因而sw与设备的当前方向无关。
在确定可选资源时,-sw600dp配置修饰符表明:对任何最小尺寸为600dp或更高dp的设备,都使用该资源。

在res目录下新建values-sw800dp文件夹,再新建一个dimens文件。


image.png
    <dimen name="text_size">48sp</dimen>

在TextView中增加属性 android:textSize="@dimen/text_size",当屏幕分辨率为800x800时(这里是随便测试用的分辨率),android会自动适配values-sw800dp下的dimens文件。

 <TextView
                android:id="@+id/test_text_view"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@color/colorPrimary"
                android:gravity="center"
                android:text="test"
                android:textSize="@dimen/text_size"/>

这种最后运行结果与通过代码去适配一致。

这种sw限定符 相对于 屏幕分辨率限定符 ,所需的dimens.xml文件更少,匹配度也更好一些。
因此这种sw限定符 的方法广泛使用。
但是上面的这种定义xml文件的方法,布局嵌套层级太深了,会增加渲染层次,致使性能下降。针对这种情况google为我们提供了一个百分比布局兼容库:Android Percent Support Library。


但是后面这个方法又被废弃了,推荐我们去使用ConstraintLayout 。

所以我们到底是为了什么一直在学习这种要被废弃的东西??我们一直在不停地使用轮子,然后再这个过程中无数的轮子都被抛弃了。如果只会用轮子我们还剩什么?
em。。还是要学会思想,原理,理论结合实践,转换成自己的东西。多写文章,好记性不如烂笔头。
下面来粗略看下谷歌提供的百分比库的简单使用。
首先在gradle中添加依赖。

implementation 'com.android.support:percent:28.0.0'

定义xml文件。

<android.support.percent.PercentRelativeLayout 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"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:id="@+id/one"
        android:background="#f0f000"
        app:layout_heightPercent="30%"
        app:layout_widthPercent="70%"
        android:text="Hello World!"
         />
    <TextView
        android:layout_width="0dp"
        android:layout_toRightOf="@id/one"
        android:layout_height="0dp"
        app:layout_heightPercent="30%"
        app:layout_widthPercent="30%"
        android:background="#ff0000"
        android:text="Hello World!"
        />
    <TextView
          android:layout_width="0dp"
          android:layout_below="@id/one"
          android:layout_height="0dp"
          app:layout_heightPercent="70%"
          app:layout_widthPercent="100%"
          android:background="#ff00ff"
          android:text="Hello World!"
            />

</android.support.percent.PercentRelativeLayout>

预览图如下。



在3台设备上运行结果如下。


1
2
3

在横竖屏中运行效果一致。看起来是比较理想的方法。官方推了一段时间这个,但是最后还是将其废弃,原因未知。因此随着android系统的更新,后续的使用会有问题。这个是别人在使用中遇到的一些问题。

关于使用百分比布局导致布局设定失效的记录
https://blog.csdn.net/wzgl708937822/article/details/80880492

废弃百分比布局兼容库后又推出了ConstraintLayout 。到今天(2019.7.8)为止使用android studio新建project都会默认生成以ConstraintLayout 为根节点的xml文件。
思考:上面写了多种使用方法,那么到底什么时候该用什么方法呢?各自的使用场景最佳使用?

先不管了,看看ConstraintLayout (约束布局)再说。
ConstraintLayout 目前为止并没有完全代替LinearLayout 、 RelatvieLayout等,并且学习成本较高,我经历过入门到放弃的过程。
而且属性特别多,很容易迷失在一堆资料中,思绪乱掉。应对这种情况我的解决办法是找简单的demo去练习(越简单的demo越好,太复杂真的不容易入门,容易劝退的,这篇文章入门我觉得还可以。哲♂学三幻神带你学习ConstraintLayout(约束布局) https://www.jianshu.com/p/639fa7377cc0),然后再自己写一遍,再对照官方文档查属性。
//从写这篇文开始到写到这里其实已经过了一周多了虽然字不多但是真的很难憋出来

这里可以先通过使用ConstraintLayout 代替代替常见布局LinearLayout 、 RelatvieLayout 、 PercentLayout等,来简单对比他们之间的差异。
先使用LinearLayout 、 RelatvieLayout实现如下两种经典布局。


image.png

首先使用LinearLayout 。

<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:orientation="horizontal"
    tools:context=".MainActivity">
    <TextView
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:text="1"
        android:gravity="center"
        android:textSize="30sp"
        android:background="#f0f000"/>
    <TextView
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="2"
        android:text="2"
        android:gravity="center"
        android:textSize="30sp"
        android:background="#ff0000"/>
    <TextView
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:text="3"
        android:gravity="center"
        android:textSize="30sp"
        android:background="#ff00ff"/>
</LinearLayout>
image.png

这个在一个横屏和2个竖屏设备上显示效果基本一致。
然后再来看RelatvieLayout。
下面的xml是针对我手中分辨率为1080x1920,density为3的设备去写的。
但是在分辨率为 720x1280,density为2的时候显示效果也基本一致。
但是在横屏上这个xml显示效果就不太行了。

<RelativeLayout 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"
    tools:context=".MainActivity">
    <TextView
        android:id="@+id/text_view_1"
        android:layout_width="match_parent"
        android:layout_height="500dp"
        android:text="1"
        android:gravity="center"
        android:textSize="30sp"
        android:background="#f0f000"/>

    <TextView
        android:id="@+id/text_view_2"
        android:layout_width="240dp"
        android:layout_height="140dp"
        android:layout_below="@id/text_view_1"
        android:text="2"
        android:gravity="center"
        android:textSize="30sp"
        android:background="#ff0000"/>
    <TextView
        android:id="@+id/text_view_3"
        android:layout_width="120dp"
        android:layout_height="140dp"
        android:layout_below="@id/text_view_1"
        android:layout_toEndOf="@id/text_view_2"
        android:text="3"
        android:gravity="center"
        android:textSize="30sp"
        android:background="#ff00ff"/>
</RelativeLayout>

运行结果如下。


竖屏
横屏

PercentLayout这里就不用看了,前文有写。现在就来看ConstraintLayout 要怎么去写这2种布局。
先看LinearLayout 。
首先在根目录下写上ConstraintLayout,然后切到design界面。
选中TextView控件,拖到3个TextView下面的区域区中去。



image.png

添加2条Guideline。


image.png

选中Guideline,可以直接设置layout_constraintGuide_percent。



填0.25就是25%的位置。


image.png

或者可以选中Guideline,点击Cycle Guideline,切到百分比。


image.png
image.png

然后分别手动拖到25%和75%的地方。


然后把最左边的TextView控件的右边连到25%那条Guideline,最右边的TextView控件的左边连到75%那条Guideline,中间的TextView控件左右分别连到25%和75%的Guideline,其余部分连离各自最近的边框。


image.png

然后再设置每个TextView控件大小以及边距。修改箭头所示部分。


image.png

修改后如下。


image.png

然后每个都这样改,最后为了更好区分,给TextView加上背景色并且调大字体,并且把字改成123。

最终xml文件如下所示。

<android.support.constraint.ConstraintLayout 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"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/textView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#f0f000"
        android:text="1"
        android:textSize="30sp"
        android:gravity="center"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/guideline"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/textView2"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#ff0000"
        android:text="2"
        android:textSize="30sp"
        android:gravity="center"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/guideline2"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="@+id/guideline"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/textView3"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#ff00ff"
        android:text="3"
        android:textSize="30sp"
        android:gravity="center"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="@+id/guideline2"
        app:layout_constraintTop_toTopOf="parent" />

    <android.support.constraint.Guideline
        android:id="@+id/guideline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_percent="0.25" />

    <android.support.constraint.Guideline
        android:id="@+id/guideline2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_percent="0.75" />
</android.support.constraint.ConstraintLayout>

在3台设备上运行结果如下所示。

1
2

3

这个布局与LinearLayout 相比,写的过程中没感觉到有什么优势,可能胜在性能吧。
为了减少代码量,一般这种统一风格的属性,可以通过定义一个style,让TextView等控件去引用就好。
在values下的styles.xml文件中,定义如下style。

    <style name="style_main_text">
        <item name="android:gravity">center</item>
        <item name="android:textSize">30sp</item>
    </style>

然后再在TextView中加上一句

style="@style/style_main_text"

再来看RelativeLayout 。
新建一个horizontal方向的Guideline,设置百分比为0.78125(500/640),一个vertical方向的Guideline,设置百分比为667(240/360),再拖3个Textview,并且将其与Guideline连接起来。
最后xml文件如下。

<android.support.constraint.ConstraintLayout 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"
    tools:context=".MainActivity">


    <TextView
        android:id="@+id/textView1"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#f0f000"
        android:text="1"
        android:textSize="30sp"
        android:gravity="center"
        app:layout_constraintBottom_toTopOf="@+id/guideline4"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/textView2"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#ff0000"
        android:text="2"
        android:textSize="30sp"
        android:gravity="center"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/guideline11"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@+id/guideline4" />

    <TextView
        android:id="@+id/textView3"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#ff00ff"
        android:text="3"
        android:textSize="30sp"
        android:gravity="center"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="@+id/guideline11"
        app:layout_constraintTop_toTopOf="@+id/guideline4" />

    <android.support.constraint.Guideline
        android:id="@+id/guideline4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.78125" />

    <android.support.constraint.Guideline
        android:id="@+id/guideline11"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_percent="0.6667" />

</android.support.constraint.ConstraintLayout>

从运行结果上来看,横竖屏效果基本一致,效果比RelativeLayout 要好。
由此可见,ConstraintLayout使用场景上来说应该更适用于复杂布局,用来代替RelativeLayout 效果还不错。
总结:ConstraintLayout+sw限定符方式应该是不错的ui适配组合。
以上只是个人分析,具体情况具体操作。

参考链接:
屏幕兼容性概览
https://developer.android.com/guide/practices/screens_support?hl=zh-CN

支持不同屏幕尺寸
https://developer.android.com/training/multiscreen/screensizes.html?hl=zh-CN#flexible-layout

Android UI 设计规范
https://www.jianshu.com/p/b38e81be51ca

安卓UI图px标注适配攻略:批量转化px为dp
https://blog.csdn.net/zengd0/article/details/52464627

Material Design
https://material.io/design/layout/understanding-layout.html#usage

iPhone不同设备屏幕尺寸和分辨率
https://www.jianshu.com/p/0bb66e4cc732

小猪浅谈Android屏幕适配
https://blog.csdn.net/coder_pig/article/details/78193938

Android adb 修改手机分辨率与DPI
https://blog.csdn.net/Jumenglo/article/details/79140071

Android中的工具栏(ActionBar和ToolBar)
https://www.jianshu.com/p/4df8709a76fa

Android屏幕适配之百分比布局
https://www.jianshu.com/p/0c2a8db91bda

https://developer.android.com/training/constraint-layout/index.html

百分比布局支持库
https://mrfu.me/android/2015/08/31/percent_support_library/

屏幕适配(一)--屏幕分辨率限定
https://www.jianshu.com/p/8a67cd0f6ccb

一种非常好用的Android屏幕适配
https://www.jianshu.com/p/1302ad5a4b04

Android 屏幕适配:最全面的解决方案
https://www.jianshu.com/p/ec5a1a30694b

被误用的屏幕分辨率限定符
https://www.jianshu.com/p/b0253e457031

Android今日头条UI适配完善版
https://www.jianshu.com/p/41930fde7aac

哲♂学三幻神带你学习ConstraintLayout(约束布局)
https://www.jianshu.com/p/639fa7377cc0

[译] ConstraintLayout深入系列之代替常见布局
https://biaomingzhong.github.io/2017/constraintlayout-layouts-common/

https://developer.android.com/reference/android/support/constraint/ConstraintLayout

ConstraintLayout使用指南
http://tinycoder.cc/2018/06/01/ConstraintLayout%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97/

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

推荐阅读更多精彩内容