# -*- coding: utf-8 -*- """手表端 UI 重做:液态玻璃(Liquid Glass)风格 + 动画。 目标平台是 Android 9 / API 28,**没有真实的背景模糊**(RenderEffect 要 API 31), 所以用"假玻璃":半透明白 + 亮边高光 + 柔和渐变背景 + 阴影层次, 视觉上非常接近,而且不挑版本。 配色: 背景 柔和三段渐变(浅蓝 → 浅紫 → 浅粉),像玻璃后面的壁纸 玻璃 白色 70% 透明 + 1dp 亮边 + 圆角 18dp 高光 顶部一条更亮的渐变,模拟玻璃反光 动画: 按压 缩到 0.94,松手回弹(Overshoot 插值) Tab 切换 指示器在标签之间滑动(宽高和位置一起动) 内容切换 淡入 + 轻微上移 浮层面板 从底部滑入 + 淡入 """ from __future__ import annotations import sys from pathlib import Path MAIN = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote\android\src" r"\com\lanremote\watch\MainActivity.java") # ---------------- 1. 配色常量 ---------------- COLOR_OLD = ''' private static final int C_BORDER = 0xFFD0D7DE; // 描边''' COLOR_NEW = ''' private static final int C_BORDER = 0xFFD0D7DE; // 描边 // ---- 液态玻璃 ---- private static final int GLASS_BG_TOP = 0xFFE8F0FF; // 背景渐变·上 private static final int GLASS_BG_MID = 0xFFF1EBFF; // 背景渐变·中 private static final int GLASS_BG_BOT = 0xFFFFF0F6; // 背景渐变·下 private static final int GLASS_FILL = 0xB3FFFFFF; // 玻璃面(70% 白) private static final int GLASS_FILL_SOFT = 0x8CFFFFFF; // 更透的玻璃 private static final int GLASS_FILL_SOLID = 0xE6FFFFFF; // 实一点(输入框/日志) private static final int GLASS_EDGE = 0x99FFFFFF; // 玻璃亮边 private static final int GLASS_EDGE_DIM = 0x40FFFFFF; // 弱一点的边''' # ---------------- 2. 玻璃绘制 + 动画工具 ---------------- HELPER_ANCHOR = ''' private GradientDrawable rounded(int fill, float radiusDp) {''' HELPER_NEW = ''' // ================================================================== // 液态玻璃 设计系统 // ================================================================== /** 背景:柔和三段渐变,像玻璃后面透出来的壁纸。 */ private GradientDrawable glassBackground() { GradientDrawable drawable = new GradientDrawable( GradientDrawable.Orientation.TL_BR, new int[]{GLASS_BG_TOP, GLASS_BG_MID, GLASS_BG_BOT}); return drawable; } /** 一块玻璃:半透明白 + 亮边。 */ private GradientDrawable glass(float radiusDp) { return glass(GLASS_FILL, radiusDp, GLASS_EDGE); } private GradientDrawable glass(int fill, float radiusDp, int edge) { GradientDrawable drawable = new GradientDrawable(); drawable.setShape(GradientDrawable.RECTANGLE); drawable.setColor(fill); drawable.setCornerRadius(dp(radiusDp)); // 亮边是"玻璃厚度"的关键,没有它看着就是一块白纸 drawable.setStroke(Math.max(1, dp(1)), edge); return drawable; } /** 按钮的玻璃底:顶部略亮,模拟反光。 */ private GradientDrawable glassButton(int tint, float radiusDp) { GradientDrawable drawable = new GradientDrawable( GradientDrawable.Orientation.TOP_BOTTOM, new int[]{blend(tint, 0xFFFFFFFF, 0.10f), tint}); drawable.setCornerRadius(dp(radiusDp)); drawable.setStroke(Math.max(1, dp(1)), GLASS_EDGE); return drawable; } /** 把颜色往另一个颜色混一点。amount=0 原色,1 完全是另一个色。 */ private static int blend(int base, int other, float amount) { float keep = 1f - amount; int a = (int) (((base >> 24) & 0xFF) * keep + ((other >> 24) & 0xFF) * amount); int r = (int) (((base >> 16) & 0xFF) * keep + ((other >> 16) & 0xFF) * amount); int g = (int) (((base >> 8) & 0xFF) * keep + ((other >> 8) & 0xFF) * amount); int b = (int) ((base & 0xFF) * keep + (other & 0xFF) * amount); return (a << 24) | (r << 16) | (g << 8) | b; } /** * 按压动画:缩到 0.94,松手带一点回弹。 * 返回 false 不吞事件,OnClickListener 照常触发。 */ private void attachPress(final View view) { if (view == null) { return; } view.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: v.animate().cancel(); v.animate().scaleX(0.94f).scaleY(0.94f) .setDuration(90) .setInterpolator(new android.view.animation.DecelerateInterpolator()) .start(); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: v.animate().cancel(); v.animate().scaleX(1f).scaleY(1f) .setDuration(280) .setInterpolator( new android.view.animation.OvershootInterpolator(2.2f)) .start(); break; } return false; } }); } /** 淡入 + 轻微上移(面板/内容出现时用)。 */ private void animateIn(View view, float fromDp, long duration) { if (view == null) { return; } view.setAlpha(0f); view.setTranslationY(dp(fromDp)); view.animate().alpha(1f).translationY(0f) .setDuration(duration) .setInterpolator(new android.view.animation.DecelerateInterpolator()) .start(); } /** 淡出 + 轻微下移(面板关闭时用)。 */ private void animateOut(final View view, float toDp, long duration, final Runnable after) { if (view == null) { if (after != null) { after.run(); } return; } view.animate().alpha(0f).translationY(dp(toDp)) .setDuration(duration) .setInterpolator(new android.view.animation.AccelerateInterpolator()) .withEndAction(new Runnable() { @Override public void run() { if (after != null) { after.run(); } } }) .start(); } private GradientDrawable rounded(int fill, float radiusDp) {''' # ---------------- 3. 按钮套上玻璃 + 按压动画 ---------------- BTN_ACTION_OLD = ''' button.setTextColor(readableOn(C_CARD)); button.setBackground(rounded(C_CARD, 8)); button.setAllCaps(false); button.setPadding(dp(4), 0, dp(4), 0);''' BTN_ACTION_NEW = ''' button.setTextColor(C_FG); button.setBackground(glassButton(GLASS_FILL_SOFT, 16)); button.setAllCaps(false); button.setPadding(dp(4), 0, dp(4), 0); attachPress(button);''' BTN_BIG_OLD = ''' button.setTextColor(readableOn(color)); button.setBackground(rounded(color, 8)); button.setAllCaps(false); return button; }''' BTN_BIG_NEW = ''' button.setTextColor(readableOn(color)); button.setBackground(glassButton(color, 16)); button.setAllCaps(false); attachPress(button); return button; }''' BTN_SMALL_OLD = ''' button.setTextColor(readableOn(color)); button.setBackground(rounded(color, 7));''' BTN_SMALL_NEW = ''' button.setTextColor(readableOn(color)); button.setBackground(glassButton(color, 12)); attachPress(button);''' def main() -> int: text = MAIN.read_text(encoding="utf-8") ok = True for old, new, tag in ((COLOR_OLD, COLOR_NEW, "玻璃配色"), (HELPER_ANCHOR, HELPER_NEW, "设计系统"), (BTN_ACTION_OLD, BTN_ACTION_NEW, "网格按钮"), (BTN_BIG_OLD, BTN_BIG_NEW, "大按钮"), (BTN_SMALL_OLD, BTN_SMALL_NEW, "小按钮")): if old not in text: print(f" [失败] {tag} 锚点未匹配") ok = False else: text = text.replace(old, new, 1) print(f" {tag}: OK") if ok: MAIN.write_text(text, encoding="utf-8") return 0 if ok else 1 if __name__ == "__main__": sys.exit(main())