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()
相关推荐
阿pin1 小时前
Android随笔-Activity启动流程深度分析
android·activity启动流程
zzq77972 小时前
加固包闪退四象限定位:targetSdk 与保护策略实战解析
android·安全·安卓·安全架构
我命由我123454 小时前
Android 开发问题:Cannot resolve method ‘repeat‘ in ‘String‘
android·java·java-ee·android studio·android jetpack·android-studio·android runtime
光头闪亮亮5 小时前
Fyne ( go跨平台GUI )项目实战-scanner摄像头扫码组件开发技术详解
android·go
Android打工仔5 小时前
Continuation 到底是谁创建的?
android·kotlin
DeepAgent5 小时前
AI Agent 工程实践(17):Agent 为什么需要可观测性(Observability)?
android·llm·agent
提笔了无痕5 小时前
MySQL SQL 从 EXPLAIN 到索引优化,搞懂 SQL 为什么慢
android·sql·mysql
zhangphil6 小时前
Android OAID是什么?有什么功用?
android
Android-Flutter8 小时前
android fragment 使用
android·kotlin