美文网首页
适配完结篇三 - 给定多套分辨率解决适配问题(原创)

适配完结篇三 - 给定多套分辨率解决适配问题(原创)

作者: acc8226 | 来源:发表于2018-06-24 20:37 被阅读21次

要点

  • 适配还是使用百分比布局靠谱(想想match_parent其实值100%, 权重也是按比例吧)
  • 如何合理建立多套dimen值
按px进行划分真的好吗

上图给的是最原始的鸿洋_的方案:
假设现在的UI设计图是按照480*320设计的,且上面的宽和高的标识都是px的值,你可以直接将px转化为x[1-320],y[1-480],这样写出的布局基本就可以全分辨率适配了。

这大概会有什么问题

  • 首先得有很多套布局, 这无形会增加apk体积
  • 很多情况下如果设备有虚拟按键, 会导致宽或者高匹配不到对于的dimens文件夹, 解决办法是根据需要再额外加几套布局

探索新的方法

突然有一天, 我看到在官网看到了这么一个片段: 支持多种屏幕 | Android Developers - 声明适用于 Android 3.2 的平板电脑布局
然后想能不能使用新的修饰符来实现适配. 这就需要采集手头所有设备的分辨率并dp化.

  • 640*360 (手机)
  • 698*392 (手机)
  • 768*480 (Pad)
  • 853*533 (Pad)
  • 960*600 (Pad)
  • 1024*640 (Pad)
  • 1024*768 (Pad)
  • 1280*800 (Pad)

若明确规定方向为横屏方向, 将得到480,640,698,768,853,960,1024,1280

再考虑到240X320,320×480,480X800的手机

还需要考虑哪些因素呢, 比如得考虑平板的虚拟按键栏, 所以还得采集具体参数)

例如平板M2 PLE-703L 在横屏状态下为1920px = 768dp, 但是如果有了虚拟按键, 则只剩下1830px = 732dp.
最终得到320,480,532,640,698,732,768,800,852,912,960,1024,1280

选择sw<N>dp 还是 w<N>dp修饰符

由于sw取得是最小宽度, 而我们在开发时候肯定会指定横屏或者竖屏, 所以会选择 w<N>dp修饰符.

  • 针对竖屏手机, 则N=320,360,392... ...
  • 针对横屏手机, 则N=640,698,768... ...

假如以1280dp*800dp横屏为基准, 会将宽度分成了640偶数份(剔除了奇数, 觉得太多余)

# 参考公式
# 假定基准宽度为1280
x1 = 目标宽度(dp) / 1280
x2 = 2 * x1
x4 = 4 * x1
...

生成文件夹形如

$ ls
values-w1024dp/  values-w320dp/  values-w640dp/  values-w768dp/  values-w852dp/
values-w1280dp/  values-w480dp/  values-w698dp/  ... ... ...

且每个文件夹下都有precent_width.xml文件, 以下是values-w640dp下的xml文件

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <dimen name="x2">1dp</dimen>
    <dimen name="x4">2dp</dimen>
    <dimen name="x6">3dp</dimen>
    <dimen name="x8">4dp</dimen>
    ```
    ```
    <dimen name="x1276">638dp</dimen>
    <dimen name="x1278">639dp</dimen>
    <dimen name="x1280">640dp</dimen>
</resources>
生成工具(参考了鸿洋_的示例)
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;

/**
 * 辅助生成资源文件
 * 
 * @author leiTKai
 */
public class GenerateValueFiles {
    private static final String dirStr = "./res";
    private static final String FILE_NAME = "precent_width.xml";
    private static final String TEMPLATE = "    <dimen name=\"x%d\">%sdp</dimen>\n";
    private static final String VALUE_TEMPLATE = "values-w%ddp";

    private final int mBaseWidth;
    private final String mSupportStr;

    /**
     * constructor
     * 
     * @param baseX
     *            基准宽
     * @param supportStr
     */
    public GenerateValueFiles(int baseX, String supportStr) {
        this.mBaseWidth = baseX;
        this.mSupportStr = supportStr;

        System.out.println("baseW: " + this.mBaseWidth);
        System.out.println("supportStr: " + this.mSupportStr);

        File dir = new File(dirStr);
        if (!dir.exists())
            dir.mkdir();
        System.out.print("FileDir: " + dir.getAbsoluteFile());
    }

    public void generate() {
        for (String val : mSupportStr.split(",")) {
            try {
                generateXmlFile(Integer.parseInt(val));
            } catch (IOException e) {
                e.printStackTrace();
                break;
            }
        }
    }

    private void generateXmlFile(final int smallestWidth) throws IOException {
        final File fileDir = new File(dirStr + File.separator
                + String.format(VALUE_TEMPLATE, smallestWidth));
        fileDir.mkdir();

        final File file = new File(fileDir, FILE_NAME);
        Writer writer = new BufferedWriter(new OutputStreamWriter(
                new FileOutputStream(file), "UTF-8"));
        writeContent(writer, mBaseWidth, smallestWidth);
        writer.close();
    }

    private static void writeContent(Writer writer, final int baseLength,
            final int totalLength) throws IOException {
        writer.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
        writer.write("<resources>\n");
        float cell = (float) totalLength / baseLength;
        for (int i = 2; i < baseLength; i+=2) {
            writer.write(String.format(TEMPLATE, i, float2String(cell * i)));
        }
        writer.write(String.format(TEMPLATE, baseLength,
                String.valueOf(totalLength)));
        writer.write("</resources>");
    }

    /**
     * 如果float类型没有小数部分则不输出小数
     * 
     * @param f
     * @return
     */
    private static String float2String(float f) {
        if (Math.round(f) == f) {
            return String.valueOf((int) f);
        }
        return String.format("%.1f", f);
    }

    public static void main(String[] args) {
        int baseW = 1280;
        String addition = "320,480,532,640,698,732,768,800,852,912,960,1024,1280";
        if (args.length == 1) {
            addition = args[0];
        } else if (args.length == 2) {
            baseW = Integer.parseInt(args[0]);
            addition = args[1];
        }
        new GenerateValueFiles(baseW, addition).generate();
    }
}

总结

  • 规避了高度, 但同时由于忽略了高度, 设备的宽高比差异会导致某些情况y轴方向上的少量留白或者显示不全
  • 可以大胆使用x系列的dimen值, 然后字体也建议大家使用dp, 如果你真的做了sp的适配, 否则sp和View的dp真的不搭.
  • 需要选取以一个设备作为基板, 如1280dp*800dp切图和布局, 如果是手机请考虑 640dp * 360dp
  • 缺点是还得穷举所有已知屏幕的宽度, 各家安卓厂商的虚拟按键宽度不一致导致可能没有对应的w<N>dp只会就近取值, 这让我不得不考虑能否在布局的时候就能指定dimen值的百分比, 期待下篇文章吧

参考

Android 屏幕适配方案 - CSDN博客

相关文章

网友评论

      本文标题:适配完结篇三 - 给定多套分辨率解决适配问题(原创)

      本文链接:https://www.haomeiwen.com/subject/frhsyftx.html