# -*- coding: utf-8 -*- """液态玻璃 第二步:动态效果 * 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. Tab 栏:玻璃 + 滑动指示器 ---------------- TABBAR_OLD = ''' // ---- 分页标签 ---- HorizontalScrollView tabScroll = new HorizontalScrollView(this); tabScroll.setHorizontalScrollBarEnabled(false); tabBar = new LinearLayout(this); tabBar.setOrientation(LinearLayout.HORIZONTAL); tabBar.setPadding(0, 0, 0, 0); tabScroll.addView(tabBar); controlPanel.addView(tabScroll, new LinearLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, dp(42)));''' TABBAR_NEW = ''' // ---- 分页标签(玻璃容器 + 滑动指示器)---- HorizontalScrollView tabScroll = new HorizontalScrollView(this); tabScroll.setHorizontalScrollBarEnabled(false); FrameLayout tabWrap = new FrameLayout(this); tabWrap.setBackground(glass(18)); tabWrap.setPadding(dp(4), dp(4), dp(4), dp(4)); tabIndicator = new View(this); tabIndicator.setBackground(glassButton(C_ACCENT, 14)); tabIndicator.setLayoutParams(new FrameLayout.LayoutParams(dp(60), dp(34))); tabWrap.addView(tabIndicator); tabBar = new LinearLayout(this); tabBar.setOrientation(LinearLayout.HORIZONTAL); tabWrap.addView(tabBar, new FrameLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); tabScroll.addView(tabWrap); controlPanel.addView(tabScroll, new LinearLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, dp(48)));''' # ---------------- 2. 标签按钮:透明底 ---------------- TABITEM_OLD = ''' tab.setPadding(dp(14), dp(8), dp(14), dp(8)); tab.setOnClickListener(new View.OnClickListener() {''' TABITEM_NEW = ''' tab.setPadding(dp(14), dp(8), dp(14), dp(8)); tab.setBackgroundColor(0x00000000); tab.setOnClickListener(new View.OnClickListener() {''' TABITEM_MARGIN_OLD = ''' LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT); params.setMargins(0, 0, dp(6), 0);''' TABITEM_MARGIN_NEW = ''' LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT); params.setMargins(0, 0, 0, 0);''' # ---------------- 3. selectTab ---------------- SELECT_OLD = ''' currentTab = index; for (int i = 0; i < tabBar.getChildCount(); i++) { View child = tabBar.getChildAt(i); boolean on = (i == index); child.setBackground(rounded(on ? C_ACCENT : C_CARD, 8)); if (child instanceof TextView) { ((TextView) child).setTextColor(on ? 0xFFFFFFFF : C_FG); } }''' SELECT_NEW = ''' currentTab = index; for (int i = 0; i < tabBar.getChildCount(); i++) { View child = tabBar.getChildAt(i); boolean on = (i == index); child.setBackgroundColor(0x00000000); if (child instanceof TextView) { ((TextView) child).setTextColor(on ? 0xFFFFFFFF : C_FG); child.animate().alpha(on ? 1f : 0.72f).setDuration(200).start(); } } moveTabIndicator(tabBar.getChildAt(index));''' SELECT_ANIM_OLD = ''' for (Act item : tabDefs[index].acts) { addRow(tabContent, action(item.label, item.task), null); } flushGrid(tabContent); }''' SELECT_ANIM_NEW = ''' for (Act item : tabDefs[index].acts) { addRow(tabContent, action(item.label, item.task), null); } flushGrid(tabContent); // 切页时内容淡入 + 轻微上移,做出"流动"的感觉 animateIn(tabContent, 10f, 220); } /** 指示器滑到目标标签;宽度和位置一起动。 */ private void moveTabIndicator(final View target) { if (tabIndicator == null || target == null) { return; } if (target.getWidth() == 0) { // 布局还没完成,等一帧再试 tabIndicator.post(new Runnable() { @Override public void run() { moveTabIndicator(target); } }); return; } final int startX = (int) tabIndicator.getTranslationX(); final int startW = tabIndicator.getLayoutParams().width; final int endX = tabBar.getLeft() + target.getLeft(); final int endW = target.getWidth(); ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f); animator.setDuration(300); animator.setInterpolator(new android.view.animation.DecelerateInterpolator()); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float fraction = (Float) animation.getAnimatedValue(); ViewGroup.LayoutParams params = tabIndicator.getLayoutParams(); params.width = (int) (startW + (endW - startW) * fraction); tabIndicator.setLayoutParams(params); tabIndicator.setTranslationX(startX + (endX - startX) * fraction); } }); animator.start(); }''' # ---------------- 4. 字段 ---------------- FIELD_OLD = " private List featureGroups = null;" FIELD_NEW = (" private List featureGroups = null;\n" " private View tabIndicator;") # ---------------- 5. 顶栏 / 日志 / 连接面板 ---------------- TOPBAR_OLD = ''' top.addView(disconnect); controlPanel.addView(top);''' TOPBAR_NEW = ''' top.addView(disconnect); top.setBackground(glass(16)); top.setPadding(dp(10), dp(8), dp(8), dp(8)); controlPanel.addView(top);''' LOG_OLD = " logView.setBackground(rounded(C_CARD, 6));" LOG_NEW = " logView.setBackground(glass(GLASS_FILL_SOLID, 14, GLASS_EDGE_DIM));" CONNECT_OLD = ''' ScrollView scroll = new ScrollView(this); scroll.setBackgroundColor(C_BG); LinearLayout box = new LinearLayout(this); box.setOrientation(LinearLayout.VERTICAL); box.setBackgroundColor(C_BG);''' CONNECT_NEW = ''' ScrollView scroll = new ScrollView(this); scroll.setBackgroundColor(0x00000000); LinearLayout box = new LinearLayout(this); box.setOrientation(LinearLayout.VERTICAL); box.setBackgroundColor(0x00000000);''' # ---------------- 6. 面板滑入滑出(用唯一锚点)---------------- MIRROR_OPEN_OLD = ''' mirrorPanel.setVisibility(View.VISIBLE); mirrorPanel.bringToFront();''' MIRROR_OPEN_NEW = ''' mirrorPanel.setVisibility(View.VISIBLE); mirrorPanel.bringToFront(); animateIn(mirrorPanel, 24f, 260);''' # 关闭镜像面板:只在 closeMirrorPanel() 里那一处(后面紧跟 mirrorImage 判空) MIRROR_CLOSE_OLD = ''' mirrorPanel.setVisibility(View.GONE); if (mirrorImage != null) {''' MIRROR_CLOSE_NEW = ''' animateOut(mirrorPanel, 24f, 200, new Runnable() { @Override public void run() { mirrorPanel.setVisibility(View.GONE); } }); if (mirrorImage != null) {''' DIAG_OPEN_OLD = ''' diagPanel.setVisibility(View.VISIBLE); diagPanel.bringToFront();''' DIAG_OPEN_NEW = ''' diagPanel.setVisibility(View.VISIBLE); diagPanel.bringToFront(); animateIn(diagPanel, 26f, 280);''' DIAG_CLOSE_OLD = ''' if (diagPanel != null) { diagPanel.setVisibility(View.GONE); }''' DIAG_CLOSE_NEW = ''' if (diagPanel != null) { animateOut(diagPanel, 26f, 200, new Runnable() { @Override public void run() { diagPanel.setVisibility(View.GONE); } }); }''' PAIRS = [ (FIELD_OLD, FIELD_NEW, "tabIndicator 字段"), (TABBAR_OLD, TABBAR_NEW, "Tab 栏玻璃容器"), (TABITEM_OLD, TABITEM_NEW, "标签透明底"), (TABITEM_MARGIN_OLD, TABITEM_MARGIN_NEW, "标签间距"), (SELECT_OLD, SELECT_NEW, "selectTab 指示器"), (SELECT_ANIM_OLD, SELECT_ANIM_NEW, "内容转场 + moveTabIndicator"), (TOPBAR_OLD, TOPBAR_NEW, "顶栏玻璃"), (LOG_OLD, LOG_NEW, "日志玻璃"), (CONNECT_OLD, CONNECT_NEW, "连接面板透明"), (MIRROR_OPEN_OLD, MIRROR_OPEN_NEW, "镜像面板滑入"), (MIRROR_CLOSE_OLD, MIRROR_CLOSE_NEW, "镜像面板滑出"), (DIAG_OPEN_OLD, DIAG_OPEN_NEW, "诊断面板滑入"), (DIAG_CLOSE_OLD, DIAG_CLOSE_NEW, "诊断面板滑出"), ] def main() -> int: text = MAIN.read_text(encoding="utf-8") failed = [] applied = 0 for old, new, tag in PAIRS: if old not in text: failed.append(tag) print(f" [跳过] {tag} —— 锚点没找到") continue occurrences = text.count(old) if occurrences > 1: print(f" [注意] {tag} 匹配 {occurrences} 处,只改第一处") text = text.replace(old, new, 1) applied += 1 print(f" {tag}: OK") # 无条件写盘:失败的只跳过那一处,不能整体丢 MAIN.write_text(text, encoding="utf-8") print(f"\n 已写盘(成功 {applied}/{len(PAIRS)})") if failed: print(f" 未应用: {', '.join(failed)}") return 1 return 0 if __name__ == "__main__": sys.exit(main())