ScreenMatch适配安卓屏幕脚本,用于老项目

dimen适配在原生安卓开发中,仍然是兼容性最高的适配方案

核心就是 以你设计图最初的,比例换算成其他屏幕比例

简述原理

我们安装好ScreenMatch插件后,base_dp就是你设计图上的屏幕比如,比如48dp,在1080上面就是dimen,这里的533 是800屏幕hdpi的值 这里不发散基础使用了

复制代码
screenMatch.properties
base_dp=1080
match_dp=533,800,1080

那么我们会得到
复制代码
values-sw800dp/dimens.xml <dimen name="dp_48">48.0000dp</dimen>
values-sw1080dp/dimens.xml <dimen name="dp_48">48.0000dp</dimen>
values/dimens.xml <dimen name="dp_48">48.0000dp</dimen>

新项目就还好直接使用,不同屏幕就会去匹配不同的dimen,但如果是老项目呢?是不是还要全局改xdp —> @dimen/dp_x之类的东西
XML 复制代码
    <TextView
        android:id="@+id/tvDate"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="@dimen/dp_48"
        android:layout_marginTop="@dimen/dp_45"
        android:lineSpacingExtra="@dimen/dp_4"
        android:textColor="@color/white"
        android:textSize="@dimen/dp_48"
        tools:text="2025 年 1 月 2 日 14:30" />
XML 复制代码
    <TextView
        android:id="@+id/tvDate"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="48dp"
        android:layout_marginTop="45dp"
        android:lineSpacingExtra="4dp"
        android:textColor="@color/white"
        android:textSize="48sp"
        tools:text="2025 年 1 月 2 日 14:30" />

解决痛点

你就还差一步全局替换

xdp ---> @dimen/dp_x

xsp ---> @dimen/dp_x (统一用dp更好维护)

缺失未定义的 dimen 得去加上values /dimen 里面加上 重新adapter

这就是属于纯苦力活了,既然是重复劳动力 则我这里分享一个脚本,放到项目根目录(screenMatch.properties同级)

执行即可

脚本分享

python 复制代码
#!/usr/bin/env python3
"""
批量将 layout XML 中的硬编码 dp/sp 值替换为 @dimen/ 引用
并自动补充缺失的 dimen 条目到 values/dimens.xml
运行后需重新执行 screenMatch 插件生成各分辨率的 dimens 文件
"""
import re
import os
from collections import OrderedDict

PROJECT_DIR = os.path.dirname(os.path.abspath(__file__))
LAYOUT_DIR = os.path.join(PROJECT_DIR, "app", "src", "main", "res", "layout")
DIMENS_FILE = os.path.join(PROJECT_DIR, "app", "src", "main", "res", "values", "dimens.xml")

HARDCODED_PATTERN = re.compile(r'="(\d+\.?\d*)(dp|sp)"')


def num_to_dimen_name(num_str, unit):
    clean = num_str.replace('.', '_')
    return f"{unit}_{clean}"


def scan_file(filepath):
    found = OrderedDict()
    with open(filepath, 'r', encoding='utf-8') as f:
        content = f.read()
    for match in HARDCODED_PATTERN.finditer(content):
        num_str, unit = match.group(1), match.group(2)
        dimen_name = num_to_dimen_name(num_str, unit)
        val = float(num_str)
        original = f"{int(val)}{unit}" if val == int(val) else f"{num_str}{unit}"
        found[dimen_name] = original
    return found


def replace_file(filepath):
    with open(filepath, 'r', encoding='utf-8') as f:
        content = f.read()

    def replacer(match):
        num_str, unit = match.group(1), match.group(2)
        dimen_name = num_to_dimen_name(num_str, unit)
        return f'="@dimen/{dimen_name}"'

    new_content = HARDCODED_PATTERN.sub(replacer, content)
    if new_content != content:
        with open(filepath, 'w', encoding='utf-8') as f:
            f.write(new_content)
        return True
    return False


def parse_existing_dimens(filepath):
    existing = set()
    with open(filepath, 'r', encoding='utf-8') as f:
        for line in f:
            m = re.match(r'\s*<dimen name="([^"]+)">', line)
            if m:
                existing.add(m.group(1))
    return existing


def add_missing_dimens(filepath, missing):
    if not missing:
        return
    with open(filepath, 'r', encoding='utf-8') as f:
        content = f.read()

    dp_items = sorted(
        [(k, v) for k, v in missing.items() if k.startswith('dp_')],
        key=lambda x: float(x[0].replace('dp_', '').replace('_', '.'))
    )
    sp_items = sorted(
        [(k, v) for k, v in missing.items() if k.startswith('sp_')],
        key=lambda x: float(x[0].replace('sp_', '').replace('_', '.'))
    )

    lines = []
    for name, val in dp_items:
        lines.append(f'    <dimen name="{name}">{val}</dimen>')
    if dp_items and sp_items:
        lines.append('')
    for name, val in sp_items:
        lines.append(f'    <dimen name="{name}">{val}</dimen>')

    insert_str = '\n'.join(lines) + '\n'
    content = content.replace('</resources>', insert_str + '\n</resources>')

    with open(filepath, 'w', encoding='utf-8') as f:
        f.write(content)


def main():
    all_dimens = OrderedDict()
    xml_files = [f for f in os.listdir(LAYOUT_DIR) if f.endswith('.xml')]
    modified_count = 0

    print("=" * 50)
    print("  扫描 layout XML 中的硬编码 dp/sp 值")
    print("=" * 50)

    for xml_file in sorted(xml_files):
        filepath = os.path.join(LAYOUT_DIR, xml_file)
        found = scan_file(filepath)
        if found:
            print(f"  {xml_file}: 发现 {len(found)} 个硬编码值")
            for name, val in found.items():
                all_dimens[name] = val

    print(f"\n共发现 {len(all_dimens)} 个不同的 dimen 值")

    print("\n" + "=" * 50)
    print("  替换为 @dimen/ 引用")
    print("=" * 50)

    for xml_file in sorted(xml_files):
        filepath = os.path.join(LAYOUT_DIR, xml_file)
        if replace_file(filepath):
            modified_count += 1
            print(f"  ✓ {xml_file}")

    print(f"\n共修改 {modified_count} 个文件")

    print("\n" + "=" * 50)
    print("  检查 dimens.xml 缺失条目")
    print("=" * 50)

    existing = parse_existing_dimens(DIMENS_FILE)
    missing = OrderedDict()
    for name, val in all_dimens.items():
        if name not in existing:
            missing[name] = val

    if missing:
        print(f"  发现 {len(missing)} 个缺失的 dimen:")
        for name, val in missing.items():
            print(f"    {name} -> {val}")
        add_missing_dimens(DIMENS_FILE, missing)
        print("  ✓ 已补充到 dimens.xml")
    else:
        print("  所有 dimen 值已存在,无需补充")

    print("\n" + "=" * 50)
    print("  完成!")
    print("=" * 50)
    print("  ⚠ 请重新运行 screenMatch 插件生成各分辨率 dimens 文件")


if __name__ == '__main__':
    main()
相关推荐
千里马学框架4 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台4 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone4 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc4 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo4 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077004 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼4 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone4 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen4 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone4 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui