@gkd-kit/docs 0.0.1744453086385 → 0.0.1744454064989

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/.vitepress/dist/404.html +15 -7
  2. package/.vitepress/dist/404_.md +15 -7
  3. package/.vitepress/dist/_config.json +1 -1
  4. package/.vitepress/dist/api/index.html +18 -10
  5. package/.vitepress/dist/api/index_.md +18 -10
  6. package/.vitepress/dist/api/interfaces/RawApp.html +18 -10
  7. package/.vitepress/dist/api/interfaces/RawAppGroup.html +18 -10
  8. package/.vitepress/dist/api/interfaces/RawAppGroup_.md +18 -10
  9. package/.vitepress/dist/api/interfaces/RawAppRule.html +18 -10
  10. package/.vitepress/dist/api/interfaces/RawAppRuleProps.html +18 -10
  11. package/.vitepress/dist/api/interfaces/RawAppRuleProps_.md +18 -10
  12. package/.vitepress/dist/api/interfaces/RawAppRule_.md +18 -10
  13. package/.vitepress/dist/api/interfaces/RawApp_.md +18 -10
  14. package/.vitepress/dist/api/interfaces/RawCategory.html +18 -10
  15. package/.vitepress/dist/api/interfaces/RawCategory_.md +18 -10
  16. package/.vitepress/dist/api/interfaces/RawCommonProps.html +18 -10
  17. package/.vitepress/dist/api/interfaces/RawCommonProps_.md +18 -10
  18. package/.vitepress/dist/api/interfaces/RawGlobalApp.html +18 -10
  19. package/.vitepress/dist/api/interfaces/RawGlobalApp_.md +18 -10
  20. package/.vitepress/dist/api/interfaces/RawGlobalGroup.html +18 -10
  21. package/.vitepress/dist/api/interfaces/RawGlobalGroup_.md +18 -10
  22. package/.vitepress/dist/api/interfaces/RawGlobalRule.html +18 -10
  23. package/.vitepress/dist/api/interfaces/RawGlobalRuleProps.html +18 -10
  24. package/.vitepress/dist/api/interfaces/RawGlobalRuleProps_.md +18 -10
  25. package/.vitepress/dist/api/interfaces/RawGlobalRule_.md +18 -10
  26. package/.vitepress/dist/api/interfaces/RawGroupProps.html +18 -10
  27. package/.vitepress/dist/api/interfaces/RawGroupProps_.md +18 -10
  28. package/.vitepress/dist/api/interfaces/RawRuleProps.html +18 -10
  29. package/.vitepress/dist/api/interfaces/RawRuleProps_.md +18 -10
  30. package/.vitepress/dist/api/interfaces/RawSubscription.html +18 -10
  31. package/.vitepress/dist/api/interfaces/RawSubscription_.md +18 -10
  32. package/.vitepress/dist/api/type-aliases/IArray.html +18 -10
  33. package/.vitepress/dist/api/type-aliases/IArray_.md +18 -10
  34. package/.vitepress/dist/api/type-aliases/Integer.html +18 -10
  35. package/.vitepress/dist/api/type-aliases/Integer_.md +18 -10
  36. package/.vitepress/dist/api/type-aliases/Position.html +18 -10
  37. package/.vitepress/dist/api/type-aliases/Position_.md +18 -10
  38. package/.vitepress/dist/assets/app.CXezeiMk.js +1 -1
  39. package/.vitepress/dist/assets/chunks/VPLocalSearchBox.CrYuW9OY.js +2 -2
  40. package/.vitepress/dist/assets/chunks/theme.BjsaA_R9.js +1 -1
  41. package/.vitepress/dist/assets/{guide_example.md.Bh6KLg_Y.js → guide_example.md.t_2k_ovM.js} +1 -1
  42. package/.vitepress/dist/assets/{guide_example.md.Bh6KLg_Y.lean.js → guide_example.md.t_2k_ovM.lean.js} +1 -1
  43. package/.vitepress/dist/assets/{guide_external.md.6Gv-iiaH.js → guide_external.md.YZ93e_r7.js} +1 -1
  44. package/.vitepress/dist/assets/{guide_external.md.6Gv-iiaH.lean.js → guide_external.md.YZ93e_r7.lean.js} +1 -1
  45. package/.vitepress/dist/assets/{guide_faq.md.CTyQ_dB0.js → guide_faq.md.C9cFwg3l.js} +1 -1
  46. package/.vitepress/dist/assets/{guide_faq.md.CTyQ_dB0.lean.js → guide_faq.md.C9cFwg3l.lean.js} +1 -1
  47. package/.vitepress/dist/assets/{guide_index.md.BXcJjxDI.js → guide_index.md.Cq5ICNCs.js} +1 -1
  48. package/.vitepress/dist/assets/{guide_index.md.BXcJjxDI.lean.js → guide_index.md.Cq5ICNCs.lean.js} +1 -1
  49. package/.vitepress/dist/assets/{guide_node.md.xFPFk3Zb.js → guide_node.md.yHOx_cRV.js} +1 -1
  50. package/.vitepress/dist/assets/{guide_node.md.xFPFk3Zb.lean.js → guide_node.md.yHOx_cRV.lean.js} +1 -1
  51. package/.vitepress/dist/assets/{guide_optimize.md.D0XzxeiU.js → guide_optimize.md.CAj_ZVr2.js} +1 -1
  52. package/.vitepress/dist/assets/{guide_optimize.md.D0XzxeiU.lean.js → guide_optimize.md.CAj_ZVr2.lean.js} +1 -1
  53. package/.vitepress/dist/assets/{guide_privacy.md.CFc95GsY.js → guide_privacy.md.DZOfTjGh.js} +1 -1
  54. package/.vitepress/dist/assets/{guide_privacy.md.CFc95GsY.lean.js → guide_privacy.md.DZOfTjGh.lean.js} +1 -1
  55. package/.vitepress/dist/assets/{guide_selector.md.DlIc78fn.js → guide_selector.md.DlP6heQt.js} +1 -1
  56. package/.vitepress/dist/assets/{guide_selector.md.DlIc78fn.lean.js → guide_selector.md.DlP6heQt.lean.js} +1 -1
  57. package/.vitepress/dist/assets/{guide_snapshot.md.D7Hkb6YJ.js → guide_snapshot.md.csG9pRY7.js} +1 -1
  58. package/.vitepress/dist/assets/{guide_snapshot.md.D7Hkb6YJ.lean.js → guide_snapshot.md.csG9pRY7.lean.js} +1 -1
  59. package/.vitepress/dist/assets/{guide_subscription.md.DVN15nZu.js → guide_subscription.md.CzmdFvOQ.js} +1 -1
  60. package/.vitepress/dist/assets/{guide_subscription.md.DVN15nZu.lean.js → guide_subscription.md.CzmdFvOQ.lean.js} +1 -1
  61. package/.vitepress/dist/assets/{guide_what-is-gkd.md.C1uTuhGu.js → guide_what-is-gkd.md.CIF244Q0.js} +1 -1
  62. package/.vitepress/dist/assets/{guide_what-is-gkd.md.C1uTuhGu.lean.js → guide_what-is-gkd.md.CIF244Q0.lean.js} +1 -1
  63. package/.vitepress/dist/assets/{index.md.CkPd5MwE.js → index.md.CrgyeR-L.js} +1 -1
  64. package/.vitepress/dist/assets/{index.md.CkPd5MwE.lean.js → index.md.CrgyeR-L.lean.js} +1 -1
  65. package/.vitepress/dist/assets/style.hcAkYZEj.css +1 -1
  66. package/.vitepress/dist/guide/example.html +19 -11
  67. package/.vitepress/dist/guide/example_.md +19 -11
  68. package/.vitepress/dist/guide/external.html +19 -11
  69. package/.vitepress/dist/guide/external_.md +19 -11
  70. package/.vitepress/dist/guide/faq.html +19 -11
  71. package/.vitepress/dist/guide/faq_.md +19 -11
  72. package/.vitepress/dist/guide/index.html +19 -11
  73. package/.vitepress/dist/guide/index_.md +19 -11
  74. package/.vitepress/dist/guide/node.html +19 -11
  75. package/.vitepress/dist/guide/node_.md +19 -11
  76. package/.vitepress/dist/guide/optimize.html +19 -11
  77. package/.vitepress/dist/guide/optimize_.md +19 -11
  78. package/.vitepress/dist/guide/privacy.html +19 -11
  79. package/.vitepress/dist/guide/privacy_.md +19 -11
  80. package/.vitepress/dist/guide/selector.html +19 -11
  81. package/.vitepress/dist/guide/selector_.md +19 -11
  82. package/.vitepress/dist/guide/snapshot.html +19 -11
  83. package/.vitepress/dist/guide/snapshot_.md +19 -11
  84. package/.vitepress/dist/guide/subscription.html +19 -11
  85. package/.vitepress/dist/guide/subscription_.md +19 -11
  86. package/.vitepress/dist/guide/what-is-gkd.html +19 -11
  87. package/.vitepress/dist/guide/what-is-gkd_.md +19 -11
  88. package/.vitepress/dist/hashmap.json +1 -1
  89. package/.vitepress/dist/index.html +19 -11
  90. package/.vitepress/dist/index_.md +19 -11
  91. package/_config.json +1 -1
  92. package/package.json +1 -1
@@ -1 +1 @@
1
- import{_ as l,B as r,o as i,c as d,j as a,a as t,G as n,ao as p}from"./chunks/framework.DZAoeHqG.js";const k=JSON.parse('{"title":"开始使用","description":"","frontmatter":{},"headers":[],"relativePath":"guide/index.md","filePath":"guide/index.md","lastUpdated":1744453064000}'),m={name:"guide/index.md"};function g(c,e,_,u,f,h){const s=r("ApkTable"),o=r("ImageTable");return i(),d("div",null,[e[0]||(e[0]=a("h1",{id:"title",tabindex:"-1"},[t("开始使用 "),a("a",{class:"header-anchor",href:"#title","aria-label":'Permalink to "开始使用 {#title}"'},"​")],-1)),e[1]||(e[1]=a("h2",{id:"install",tabindex:"-1"},[t("安装 "),a("a",{class:"header-anchor",href:"#install","aria-label":'Permalink to "安装 {#install}"'},"​")],-1)),n(s),e[2]||(e[2]=p("",7)),n(o,{images:[["0001.png","0002.png","0003.png","0004.png"],["0005.png","0006.png","0007.png","0008.png"]]}),e[3]||(e[3]=a("p",null,[t("如遇到问题可在 "),a("a",{href:"/guide/faq"},"常见问题"),t(" 查阅解决方案")],-1))])}const x=l(m,[["render",g]]);export{k as __pageData,x as default};
1
+ import{_ as l,B as r,o as i,c as d,j as a,a as t,G as n,ao as p}from"./chunks/framework.DZAoeHqG.js";const k=JSON.parse('{"title":"开始使用","description":"","frontmatter":{},"headers":[],"relativePath":"guide/index.md","filePath":"guide/index.md","lastUpdated":1744454038000}'),m={name:"guide/index.md"};function g(c,e,_,u,f,h){const s=r("ApkTable"),o=r("ImageTable");return i(),d("div",null,[e[0]||(e[0]=a("h1",{id:"title",tabindex:"-1"},[t("开始使用 "),a("a",{class:"header-anchor",href:"#title","aria-label":'Permalink to "开始使用 {#title}"'},"​")],-1)),e[1]||(e[1]=a("h2",{id:"install",tabindex:"-1"},[t("安装 "),a("a",{class:"header-anchor",href:"#install","aria-label":'Permalink to "安装 {#install}"'},"​")],-1)),n(s),e[2]||(e[2]=p("",7)),n(o,{images:[["0001.png","0002.png","0003.png","0004.png"],["0005.png","0006.png","0007.png","0008.png"]]}),e[3]||(e[3]=a("p",null,[t("如遇到问题可在 "),a("a",{href:"/guide/faq"},"常见问题"),t(" 查阅解决方案")],-1))])}const x=l(m,[["render",g]]);export{k as __pageData,x as default};
@@ -1 +1 @@
1
- import{_ as d,o as e,c as o,ao as a}from"./chunks/framework.DZAoeHqG.js";const p=JSON.parse('{"title":"属性方法","description":"","frontmatter":{},"headers":[],"relativePath":"guide/node.md","filePath":"guide/node.md","lastUpdated":1744453064000}'),c={name:"guide/node.md"};function r(n,t,l,i,h,b){return e(),o("div",null,t[0]||(t[0]=[a('<h1 id="title" tabindex="-1">属性方法 <a class="header-anchor" href="#title" aria-label="Permalink to &quot;属性方法 {#title}&quot;">​</a></h1><p>此章节介绍各个类型可用的属性及其方法</p><h2 id="null" tabindex="-1">null <a class="header-anchor" href="#null" aria-label="Permalink to &quot;null&quot;">​</a></h2><div class="note custom-block github-alert"><p class="custom-block-title">提示</p><p><code>null</code> 无任何属性方法, 并且在无特殊说明时, 属性方法当调用者或者参数是 <code>null</code> 时, 均返回 <code>null</code></p></div><p>当 <code>name</code> 是 <code>null</code> 时, <code>name.length</code> 为 <code>null</code></p><p>当 <code>parent</code> 是 <code>null</code> 时, <code>parent.getChild(0)</code> 为 <code>null</code></p><p>当 <code>index</code> 是 <code>null</code> 时, <code>parent.getChild(index)</code> 为 <code>null</code></p><h2 id="boolean" tabindex="-1">boolean <a class="header-anchor" href="#boolean" aria-label="Permalink to &quot;boolean&quot;">​</a></h2><table tabindex="0"><thead><tr><th>方法名</th><th>参数</th><th>返回类型</th><th>描述</th></tr></thead><tbody><tr><td>toInt</td><td></td><td><code>int</code></td><td>转为 0 或 1</td></tr><tr><td>or</td><td><code>boolean</code></td><td><code>boolean</code></td><td>a || b</td></tr><tr><td>and</td><td><code>boolean</code></td><td><code>boolean</code></td><td>a &amp;&amp; b</td></tr><tr><td>not</td><td></td><td><code>boolean</code></td><td>!a</td></tr><tr><td>ifElse</td><td><code>T</code>, <code>T</code></td><td><code>T</code></td><td>if(a) T else T <br> T 是任意类型, 两个参数均支持为 null</td></tr></tbody></table><h2 id="int" tabindex="-1">int <a class="header-anchor" href="#int" aria-label="Permalink to &quot;int&quot;">​</a></h2><table tabindex="0"><thead><tr><th>方法名</th><th>参数</th><th>返回类型</th><th>描述</th></tr></thead><tbody><tr><td>toString</td><td></td><td><a href="#string"><code>string</code></a></td><td>转为10进制字符串</td></tr><tr><td>toString</td><td><code>int</code></td><td><code>string</code></td><td>转为对应进制的字符串</td></tr><tr><td>plus</td><td><code>int</code></td><td><code>string</code></td><td>加上</td></tr><tr><td>minus</td><td><code>int</code></td><td><code>string</code></td><td>减去</td></tr><tr><td>times</td><td><code>int</code></td><td><code>string</code></td><td>乘以</td></tr><tr><td>div</td><td><code>int</code></td><td><code>string</code></td><td>除以</td></tr><tr><td>rem</td><td><code>int</code></td><td><code>string</code></td><td>取余</td></tr><tr><td>more</td><td><code>int</code></td><td><a href="#boolean"><code>boolean</code></a></td><td>a &gt; b</td></tr><tr><td>moreEqual</td><td><code>int</code></td><td><code>boolean</code></td><td>a &gt;= b</td></tr><tr><td>less</td><td><code>int</code></td><td><code>boolean</code></td><td>a &lt; b</td></tr><tr><td>lessEqual</td><td><code>int</code></td><td><code>boolean</code></td><td>a &lt;= b</td></tr></tbody></table><h2 id="string" tabindex="-1">string <a class="header-anchor" href="#string" aria-label="Permalink to &quot;string&quot;">​</a></h2><table tabindex="0"><thead><tr><th>标识符</th><th>属性类型</th><th>描述</th></tr></thead><tbody><tr><td>length</td><td><a href="#int"><code>int</code></a></td><td>字符串长度</td></tr></tbody></table><table tabindex="0"><thead><tr><th>方法名</th><th>参数</th><th>返回类型</th><th>描述</th></tr></thead><tbody><tr><td>get</td><td><a href="#int"><code>int</code></a></td><td><code>string</code></td><td>获取对应索引字符串</td></tr><tr><td>at</td><td><code>int</code></td><td><code>string</code></td><td>同上,但是参数传负数时从最后一个字符取</td></tr><tr><td>substring</td><td><code>int</code></td><td><code>string</code></td><td>截取指定索引到结尾字符串</td></tr><tr><td>substring</td><td><code>int</code>,<code>int</code></td><td><code>string</code></td><td>截取指定间隔字符串</td></tr><tr><td>toInt</td><td></td><td><code>int</code></td><td>转为10进制数字</td></tr><tr><td>toInt</td><td><code>int</code></td><td><code>int</code></td><td>转为指定进制的数字</td></tr><tr><td>indexOf</td><td><code>string</code></td><td><code>int</code></td><td>查找指定字符串的索引</td></tr><tr><td>indexOf</td><td><code>string</code>,<code>int</code></td><td><code>int</code></td><td>从指定索引开始查找指定字符串的索引</td></tr></tbody></table><h2 id="node" tabindex="-1">node <a class="header-anchor" href="#node" aria-label="Permalink to &quot;node&quot;">​</a></h2><div class="important custom-block github-alert"><p class="custom-block-title">重要提示</p><p>此类型下以 _ 开头的属性只能在网页快照调试工具上使用, 真机不可用</p></div><table tabindex="0"><thead><tr><th>标识符</th><th>属性类型</th><th>描述</th></tr></thead><tbody><tr><td>_id</td><td><a href="#int"><code>int</code></a></td><td></td></tr><tr><td>_pid</td><td><code>int</code></td><td></td></tr><tr><td>id</td><td><a href="#string"><code>string</code></a></td><td></td></tr><tr><td>vid</td><td><code>string</code></td><td></td></tr><tr><td>name</td><td><code>string</code></td><td></td></tr><tr><td>text</td><td><code>string</code></td><td></td></tr><tr><td>desc</td><td><code>string</code></td><td></td></tr><tr><td>clickable</td><td><code>boolean</code></td><td></td></tr><tr><td>focusable</td><td><code>boolean</code></td><td></td></tr><tr><td>checkable</td><td><code>boolean</code></td><td></td></tr><tr><td>checked</td><td><code>boolean</code></td><td></td></tr><tr><td>editable</td><td><code>boolean</code></td><td></td></tr><tr><td>longClickable</td><td><code>boolean</code></td><td></td></tr><tr><td>visibleToUser</td><td><code>boolean</code></td><td></td></tr><tr><td>left</td><td><code>int</code></td><td></td></tr><tr><td>top</td><td><code>int</code></td><td></td></tr><tr><td>right</td><td><code>int</code></td><td></td></tr><tr><td>bottom</td><td><code>int</code></td><td></td></tr><tr><td>width</td><td><code>int</code></td><td></td></tr><tr><td>height</td><td><code>int</code></td><td></td></tr><tr><td>childCount</td><td><code>int</code></td><td></td></tr><tr><td>index</td><td><code>int</code></td><td></td></tr><tr><td>depth</td><td><code>int</code></td><td></td></tr><tr><td>parent</td><td><a href="#node"><code>node</code></a></td><td>获取父节点</td></tr></tbody></table><table tabindex="0"><thead><tr><th>方法名</th><th>参数</th><th>返回类型</th><th>描述</th></tr></thead><tbody><tr><td>getChild</td><td><code>int</code></td><td><a href="#node"><code>node</code></a></td><td>获取指定索引的子节点</td></tr></tbody></table><h2 id="context" tabindex="-1">context <a class="header-anchor" href="#context" aria-label="Permalink to &quot;context&quot;">​</a></h2><p>context 具有 <a href="#node">node</a> 的所有属性方法, 下面只介绍额外属性方法</p><table tabindex="0"><thead><tr><th>标识符</th><th>属性类型</th><th>描述</th></tr></thead><tbody><tr><td>prev</td><td><a href="#context"><code>context</code></a></td><td>右侧属性选择器的节点上下文 <br> 最右侧的 prev=null</td></tr><tr><td>current</td><td><a href="#node"><code>node</code></a></td><td>当前节点 <br> current.id=id</td></tr></tbody></table><p>示例</p><div class="language-text vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">text</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span>FrameLayout[prev.name$=&#39;ImageView&#39;] &gt; ImageView[current.name=name]</span></span></code></pre></div><p>中的 prev 就是 FrameLayout 右侧的 ImageView, current 就是 ImageView</p><table tabindex="0"><thead><tr><th>方法名</th><th>参数</th><th>返回类型</th><th>描述</th></tr></thead><tbody><tr><td>getPrev</td><td>int</td><td><a href="#context"><code>context</code></a></td><td>快捷获取深层 prev <br> getPrev(0) -&gt; prev <br> getPrev(1) -&gt; prev.prev <br> getPrev(2) -&gt; prev.prev.prev</td></tr></tbody></table><h2 id="global" tabindex="-1">global <a class="header-anchor" href="#global" aria-label="Permalink to &quot;global&quot;">​</a></h2><p>global 具有 <a href="#context">context</a> 的所有属性方法, 并且没有任何属性方法能获取 global 的引用, 下面只介绍额外属性方法</p><table tabindex="0"><thead><tr><th>方法名</th><th>参数</th><th>返回类型</th><th>描述</th></tr></thead><tbody><tr><td>equal</td><td><code>T</code>, <code>T</code></td><td><a href="#boolean"><code>boolean</code></a></td><td>a==b <br> T 是任意类型, a,b 允许 null</td></tr><tr><td>notEqual</td><td><code>T</code>, <code>T</code></td><td><code>boolean</code></td><td>a!=b</td></tr></tbody></table><p>示例</p><div class="language-text vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">text</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span>[name=&#39;&#39;][prev.name=&#39;&#39;][current.name=&#39;&#39;][parent.name=&#39;&#39;]</span></span></code></pre></div><ul><li>第一个 <code>name</code> 来自 <a href="#global">global</a>, 实际上任何 <code>xxx</code>/<code>xxx()</code> 都来自 global</li><li>第二个 <code>prev.name</code> 来自 <a href="#context">context</a></li><li>后续的 <code>current.name</code>/<code>parent.name</code> 都来自 <a href="#node">node</a></li></ul>',31)]))}const g=d(c,[["render",r]]);export{p as __pageData,g as default};
1
+ import{_ as d,o as e,c as o,ao as a}from"./chunks/framework.DZAoeHqG.js";const p=JSON.parse('{"title":"属性方法","description":"","frontmatter":{},"headers":[],"relativePath":"guide/node.md","filePath":"guide/node.md","lastUpdated":1744454038000}'),c={name:"guide/node.md"};function r(n,t,l,i,h,b){return e(),o("div",null,t[0]||(t[0]=[a('<h1 id="title" tabindex="-1">属性方法 <a class="header-anchor" href="#title" aria-label="Permalink to &quot;属性方法 {#title}&quot;">​</a></h1><p>此章节介绍各个类型可用的属性及其方法</p><h2 id="null" tabindex="-1">null <a class="header-anchor" href="#null" aria-label="Permalink to &quot;null&quot;">​</a></h2><div class="note custom-block github-alert"><p class="custom-block-title">提示</p><p><code>null</code> 无任何属性方法, 并且在无特殊说明时, 属性方法当调用者或者参数是 <code>null</code> 时, 均返回 <code>null</code></p></div><p>当 <code>name</code> 是 <code>null</code> 时, <code>name.length</code> 为 <code>null</code></p><p>当 <code>parent</code> 是 <code>null</code> 时, <code>parent.getChild(0)</code> 为 <code>null</code></p><p>当 <code>index</code> 是 <code>null</code> 时, <code>parent.getChild(index)</code> 为 <code>null</code></p><h2 id="boolean" tabindex="-1">boolean <a class="header-anchor" href="#boolean" aria-label="Permalink to &quot;boolean&quot;">​</a></h2><table tabindex="0"><thead><tr><th>方法名</th><th>参数</th><th>返回类型</th><th>描述</th></tr></thead><tbody><tr><td>toInt</td><td></td><td><code>int</code></td><td>转为 0 或 1</td></tr><tr><td>or</td><td><code>boolean</code></td><td><code>boolean</code></td><td>a || b</td></tr><tr><td>and</td><td><code>boolean</code></td><td><code>boolean</code></td><td>a &amp;&amp; b</td></tr><tr><td>not</td><td></td><td><code>boolean</code></td><td>!a</td></tr><tr><td>ifElse</td><td><code>T</code>, <code>T</code></td><td><code>T</code></td><td>if(a) T else T <br> T 是任意类型, 两个参数均支持为 null</td></tr></tbody></table><h2 id="int" tabindex="-1">int <a class="header-anchor" href="#int" aria-label="Permalink to &quot;int&quot;">​</a></h2><table tabindex="0"><thead><tr><th>方法名</th><th>参数</th><th>返回类型</th><th>描述</th></tr></thead><tbody><tr><td>toString</td><td></td><td><a href="#string"><code>string</code></a></td><td>转为10进制字符串</td></tr><tr><td>toString</td><td><code>int</code></td><td><code>string</code></td><td>转为对应进制的字符串</td></tr><tr><td>plus</td><td><code>int</code></td><td><code>string</code></td><td>加上</td></tr><tr><td>minus</td><td><code>int</code></td><td><code>string</code></td><td>减去</td></tr><tr><td>times</td><td><code>int</code></td><td><code>string</code></td><td>乘以</td></tr><tr><td>div</td><td><code>int</code></td><td><code>string</code></td><td>除以</td></tr><tr><td>rem</td><td><code>int</code></td><td><code>string</code></td><td>取余</td></tr><tr><td>more</td><td><code>int</code></td><td><a href="#boolean"><code>boolean</code></a></td><td>a &gt; b</td></tr><tr><td>moreEqual</td><td><code>int</code></td><td><code>boolean</code></td><td>a &gt;= b</td></tr><tr><td>less</td><td><code>int</code></td><td><code>boolean</code></td><td>a &lt; b</td></tr><tr><td>lessEqual</td><td><code>int</code></td><td><code>boolean</code></td><td>a &lt;= b</td></tr></tbody></table><h2 id="string" tabindex="-1">string <a class="header-anchor" href="#string" aria-label="Permalink to &quot;string&quot;">​</a></h2><table tabindex="0"><thead><tr><th>标识符</th><th>属性类型</th><th>描述</th></tr></thead><tbody><tr><td>length</td><td><a href="#int"><code>int</code></a></td><td>字符串长度</td></tr></tbody></table><table tabindex="0"><thead><tr><th>方法名</th><th>参数</th><th>返回类型</th><th>描述</th></tr></thead><tbody><tr><td>get</td><td><a href="#int"><code>int</code></a></td><td><code>string</code></td><td>获取对应索引字符串</td></tr><tr><td>at</td><td><code>int</code></td><td><code>string</code></td><td>同上,但是参数传负数时从最后一个字符取</td></tr><tr><td>substring</td><td><code>int</code></td><td><code>string</code></td><td>截取指定索引到结尾字符串</td></tr><tr><td>substring</td><td><code>int</code>,<code>int</code></td><td><code>string</code></td><td>截取指定间隔字符串</td></tr><tr><td>toInt</td><td></td><td><code>int</code></td><td>转为10进制数字</td></tr><tr><td>toInt</td><td><code>int</code></td><td><code>int</code></td><td>转为指定进制的数字</td></tr><tr><td>indexOf</td><td><code>string</code></td><td><code>int</code></td><td>查找指定字符串的索引</td></tr><tr><td>indexOf</td><td><code>string</code>,<code>int</code></td><td><code>int</code></td><td>从指定索引开始查找指定字符串的索引</td></tr></tbody></table><h2 id="node" tabindex="-1">node <a class="header-anchor" href="#node" aria-label="Permalink to &quot;node&quot;">​</a></h2><div class="important custom-block github-alert"><p class="custom-block-title">重要提示</p><p>此类型下以 _ 开头的属性只能在网页快照调试工具上使用, 真机不可用</p></div><table tabindex="0"><thead><tr><th>标识符</th><th>属性类型</th><th>描述</th></tr></thead><tbody><tr><td>_id</td><td><a href="#int"><code>int</code></a></td><td></td></tr><tr><td>_pid</td><td><code>int</code></td><td></td></tr><tr><td>id</td><td><a href="#string"><code>string</code></a></td><td></td></tr><tr><td>vid</td><td><code>string</code></td><td></td></tr><tr><td>name</td><td><code>string</code></td><td></td></tr><tr><td>text</td><td><code>string</code></td><td></td></tr><tr><td>desc</td><td><code>string</code></td><td></td></tr><tr><td>clickable</td><td><code>boolean</code></td><td></td></tr><tr><td>focusable</td><td><code>boolean</code></td><td></td></tr><tr><td>checkable</td><td><code>boolean</code></td><td></td></tr><tr><td>checked</td><td><code>boolean</code></td><td></td></tr><tr><td>editable</td><td><code>boolean</code></td><td></td></tr><tr><td>longClickable</td><td><code>boolean</code></td><td></td></tr><tr><td>visibleToUser</td><td><code>boolean</code></td><td></td></tr><tr><td>left</td><td><code>int</code></td><td></td></tr><tr><td>top</td><td><code>int</code></td><td></td></tr><tr><td>right</td><td><code>int</code></td><td></td></tr><tr><td>bottom</td><td><code>int</code></td><td></td></tr><tr><td>width</td><td><code>int</code></td><td></td></tr><tr><td>height</td><td><code>int</code></td><td></td></tr><tr><td>childCount</td><td><code>int</code></td><td></td></tr><tr><td>index</td><td><code>int</code></td><td></td></tr><tr><td>depth</td><td><code>int</code></td><td></td></tr><tr><td>parent</td><td><a href="#node"><code>node</code></a></td><td>获取父节点</td></tr></tbody></table><table tabindex="0"><thead><tr><th>方法名</th><th>参数</th><th>返回类型</th><th>描述</th></tr></thead><tbody><tr><td>getChild</td><td><code>int</code></td><td><a href="#node"><code>node</code></a></td><td>获取指定索引的子节点</td></tr></tbody></table><h2 id="context" tabindex="-1">context <a class="header-anchor" href="#context" aria-label="Permalink to &quot;context&quot;">​</a></h2><p>context 具有 <a href="#node">node</a> 的所有属性方法, 下面只介绍额外属性方法</p><table tabindex="0"><thead><tr><th>标识符</th><th>属性类型</th><th>描述</th></tr></thead><tbody><tr><td>prev</td><td><a href="#context"><code>context</code></a></td><td>右侧属性选择器的节点上下文 <br> 最右侧的 prev=null</td></tr><tr><td>current</td><td><a href="#node"><code>node</code></a></td><td>当前节点 <br> current.id=id</td></tr></tbody></table><p>示例</p><div class="language-text vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">text</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span>FrameLayout[prev.name$=&#39;ImageView&#39;] &gt; ImageView[current.name=name]</span></span></code></pre></div><p>中的 prev 就是 FrameLayout 右侧的 ImageView, current 就是 ImageView</p><table tabindex="0"><thead><tr><th>方法名</th><th>参数</th><th>返回类型</th><th>描述</th></tr></thead><tbody><tr><td>getPrev</td><td>int</td><td><a href="#context"><code>context</code></a></td><td>快捷获取深层 prev <br> getPrev(0) -&gt; prev <br> getPrev(1) -&gt; prev.prev <br> getPrev(2) -&gt; prev.prev.prev</td></tr></tbody></table><h2 id="global" tabindex="-1">global <a class="header-anchor" href="#global" aria-label="Permalink to &quot;global&quot;">​</a></h2><p>global 具有 <a href="#context">context</a> 的所有属性方法, 并且没有任何属性方法能获取 global 的引用, 下面只介绍额外属性方法</p><table tabindex="0"><thead><tr><th>方法名</th><th>参数</th><th>返回类型</th><th>描述</th></tr></thead><tbody><tr><td>equal</td><td><code>T</code>, <code>T</code></td><td><a href="#boolean"><code>boolean</code></a></td><td>a==b <br> T 是任意类型, a,b 允许 null</td></tr><tr><td>notEqual</td><td><code>T</code>, <code>T</code></td><td><code>boolean</code></td><td>a!=b</td></tr></tbody></table><p>示例</p><div class="language-text vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">text</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span>[name=&#39;&#39;][prev.name=&#39;&#39;][current.name=&#39;&#39;][parent.name=&#39;&#39;]</span></span></code></pre></div><ul><li>第一个 <code>name</code> 来自 <a href="#global">global</a>, 实际上任何 <code>xxx</code>/<code>xxx()</code> 都来自 global</li><li>第二个 <code>prev.name</code> 来自 <a href="#context">context</a></li><li>后续的 <code>current.name</code>/<code>parent.name</code> 都来自 <a href="#node">node</a></li></ul>',31)]))}const g=d(c,[["render",r]]);export{p as __pageData,g as default};
@@ -1 +1 @@
1
- import{_ as d,o as e,c as o,ao as a}from"./chunks/framework.DZAoeHqG.js";const p=JSON.parse('{"title":"属性方法","description":"","frontmatter":{},"headers":[],"relativePath":"guide/node.md","filePath":"guide/node.md","lastUpdated":1744453064000}'),c={name:"guide/node.md"};function r(n,t,l,i,h,b){return e(),o("div",null,t[0]||(t[0]=[a("",31)]))}const g=d(c,[["render",r]]);export{p as __pageData,g as default};
1
+ import{_ as d,o as e,c as o,ao as a}from"./chunks/framework.DZAoeHqG.js";const p=JSON.parse('{"title":"属性方法","description":"","frontmatter":{},"headers":[],"relativePath":"guide/node.md","filePath":"guide/node.md","lastUpdated":1744454038000}'),c={name:"guide/node.md"};function r(n,t,l,i,h,b){return e(),o("div",null,t[0]||(t[0]=[a("",31)]))}const g=d(c,[["render",r]]);export{p as __pageData,g as default};
@@ -1 +1 @@
1
- import{_ as o,o as d,c as t,ao as c}from"./chunks/framework.DZAoeHqG.js";const b=JSON.parse('{"title":"查询优化","description":"","frontmatter":{},"headers":[],"relativePath":"guide/optimize.md","filePath":"guide/optimize.md","lastUpdated":1744453064000}'),i={name:"guide/optimize.md"};function l(a,e,n,r,p,u){return d(),t("div",null,e[0]||(e[0]=[c('<h1 id="title" tabindex="-1">查询优化 <a class="header-anchor" href="#title" aria-label="Permalink to &quot;查询优化 {#title}&quot;">​</a></h1><p>一些关于查询优化的说明</p><h2 id="query-root" tabindex="-1">查找根节点 <a class="header-anchor" href="#query-root" aria-label="Permalink to &quot;查找根节点 {#query-root}&quot;">​</a></h2><p>选择器通过满足下列格式的属性选择器标识根节点</p><ul><li><code>[parent=null]</code></li><li><code>A[id=&#39;xxx&#39;][parent=null][text=&#39;xxx&#39;]</code></li></ul><p>即只需存在一个 <code>[parent=null]</code> 就满足条件, 以下是对此的优化</p><h3 id="enforce" tabindex="-1">主动查询 <a class="header-anchor" href="#enforce" aria-label="Permalink to &quot;主动查询 {#enforce}&quot;">​</a></h3><p>一般的选择器如 <code>LinearLayout &gt; TextView</code>, 选择器需要从 根节点/事件节点 使用深度先序顺序遍历子孙节点并判断是否满足条件</p><p>如果屏幕上有 n 个节点, 判断逻辑需要运行 n 次</p><p>但是如果选择器是类似 <code>TextView &lt;3 LinearLayout &lt;2 [parent=null]</code>, 由于在右侧标明了从根节点查找</p><p>因此上面的 <code>深度先序顺序遍历子孙节点</code> 将省略, 只进行一次判断, 不需要判断 n 次</p><p>但是如果你在子查询里使用 <code>&lt;&lt;n</code>, 例如 <code>TextView &lt;&lt;n [parent=null]</code></p><p>根据上面说明的优化, 虽然也只有一次判断, 但是由于 <code>&lt;&lt;n</code> 导致内部的子判断也是 n 次, 实际上没有优化判断次数</p><div class="tip custom-block github-alert"><p class="custom-block-title">提示</p><p>实际上从根节点开始匹配并且目标不是根节点的选择器如 <code>A &gt; B</code> 都可等价为 <code>A &gt; @B &lt;&lt;n [parent=null]</code></p></div><h3 id="ancestor" tabindex="-1">祖先节点 <a class="header-anchor" href="#ancestor" aria-label="Permalink to &quot;祖先节点 {#ancestor}&quot;">​</a></h3><p>在示例 <code>A &lt;3 [parent=null] &gt;n TextView</code> 中, 选择器找到 <code>TextView</code> 后, 根据 <code>&gt;n</code> 表示任意祖先节点</p><p>在默认情况下, 如果 <code>TextView</code> 的层级很深, 那必然需要多次调用 获取父节点 才能最终匹配</p><p>但是如果左侧的选择器是 <code>[parent=null] &gt;n</code>, 这表示找到根节点, 选择器此时不会去执行多次调用 获取父节点</p><p>在 GKD 内部, 根节点可以直接通过 API 获取, 因此 <code>[parent=null] &gt;n</code> 只需要调用一次方法</p><h2 id="fast-query" tabindex="-1">快速查询 <a class="header-anchor" href="#fast-query" aria-label="Permalink to &quot;快速查询 {#fast-query}&quot;">​</a></h2><div class="note custom-block github-alert"><p class="custom-block-title">注意</p><p>此优化需要设置 <a href="/api/interfaces/RawCommonProps#fastquery">fastQuery</a> 来启用<br> 并且节点在快照面板被标识 <code>可快速查找</code>, 否则查找不到</p></div><h3 id="query-scene" tabindex="-1">查询场景 <a class="header-anchor" href="#query-scene" aria-label="Permalink to &quot;查询场景 {#query-scene}&quot;">​</a></h3><p>一般情况下, 选择器 <code>[vid=&quot;name&quot;]</code> 需要从 根节点/事件节点 使用深度先序顺序遍历子孙节点并判断是否满足条件</p><p>但是 Android 提供了如下两个快速获取节点的 Api</p><ul><li><a href="https://developer.android.google.cn/reference/android/view/accessibility/AccessibilityNodeInfo#findAccessibilityNodeInfosByViewId(java.lang.String)" target="_blank" rel="noreferrer">findAccessibilityNodeInfosByViewId</a></li><li><a href="https://developer.android.google.cn/reference/android/view/accessibility/AccessibilityNodeInfo#findAccessibilityNodeInfosByText(java.lang.String)" target="_blank" rel="noreferrer">findAccessibilityNodeInfosByText</a></li></ul><p>这使得可以通过 <code>id</code>/<code>vid</code>/<code>text</code> 直接获取子孙节点</p><p>对此我们需要规定符合特定条件的选择器来调用这些 Api 从而跳过手动遍历子孙节点</p><h3 id="rule-format" tabindex="-1">规定格式 <a class="header-anchor" href="#rule-format" aria-label="Permalink to &quot;规定格式 {#rule-format}&quot;">​</a></h3><p>所有 <code>末尾属性选择器</code>的<code>第一个属性选择表达式</code>符合下面的结构之一</p><ul><li><code>[id=&#39;abc&#39;]</code></li><li><code>[vid=&#39;abc&#39;]</code></li><li><code>[text=&#39;abc&#39;]</code></li><li><code>[text^=&#39;abc&#39;]</code></li><li><code>[text*=&#39;abc&#39;]</code></li><li><code>[text$=&#39;abc&#39;]</code></li></ul><p>或者使用 <code>||</code> 将它们连接形成的逻辑表达式也符合条件, 即如下格式</p><ul><li><code>[id=&#39;abc&#39; || id=&#39;abc2&#39;]</code></li><li><code>[id=&#39;abc&#39; || vid=&#39;abc&#39; || text=&#39;abc&#39; || text^=&#39;abc&#39; || text*=&#39;abc&#39; || text$=&#39;abc&#39;]</code></li></ul><p>这些规定的格式都是为了提取 <code>id</code>/<code>vid</code>/<code>text</code> 来调用上面的两个 Android Api</p><p>下面给出实际示例: ✅ 表示符合格式, ❎ 表示不符合格式</p><ul><li><code>A &gt; B + C[id=&#39;x&#39;][childCount=2]</code> ✅</li><li><code>A &gt; B + C[childCount=2][id=&#39;x&#39;]</code> ❎</li><li><code>A &gt; B + C[id=&#39;x&#39; || text=&#39;manbaout&#39; || text*=&#39;ikun&#39;][childCount=2]</code> ✅</li><li><code>A &gt; B + C[childCount=2][id=&#39;x&#39; || text=&#39;manbaout&#39; || text*=&#39;ikun&#39;]</code> ❎</li></ul><p>这样一个选择器只能在右侧使用快速查询, 为了在中间的子选择器也能使用</p><p>额外规定如果属性选择器如果符合上面格式并且右侧是 <code>&lt;&lt;n</code>, 也能在局部使用快速查找</p><p>示例 <code>A &gt; B + C[id=&#39;x&#39;][childCount=2] &lt;&lt;n D</code> 中的 <code>C[id=&#39;x&#39;][childCount=2] &lt;&lt;n</code> 可以使用局部快速查找</p><p>下面给出满足局部查询优化的示例: ✅ 表示符合格式, ❎ 表示不符合格式</p><ul><li><code>A &gt; B + C[id=&#39;x&#39;][childCount=2] &lt;&lt;n D</code> ✅</li><li><code>A &gt; B + C[childCount=2][id=&#39;x&#39;] &lt;&lt;n D</code> ❎</li></ul><p>上面介绍的是只有一个局部选择器的情况, 下面给出多个局部快速查找的的示例</p><p>如 <code>A &gt; C[id=&#39;x&#39;] &lt;&lt;n D[id=&#39;y&#39;] &lt;&lt;n E</code>, 其中的 <code>C[id=&#39;x&#39;] &lt;&lt;n</code> 和 <code>D[id=&#39;y&#39;] &lt;&lt;n</code> 都可以使用局部快速查找</p><p>如果使用 <code>||</code>, <code>&amp;&amp;</code> 连接多个选择器时, 快速查询为它们的并集</p><p>如果选择器被 <code>!(xxx)</code> 包裹, 则不符合快速查询格式, 也就是 <code>!</code> 会取消其内部所有的快速查询</p><ul><li><code>(A + B[id=&#39;b&#39;]) || (C + D[id=&#39;d&#39;])</code> 的快速查询为 <code>id=&#39;b&#39;</code>, <code>id=&#39;d&#39;</code></li><li><code>(A + B[id=&#39;b&#39;]) &amp;&amp; (C + D[id=&#39;d&#39;])</code> 的快速查询为 <code>id=&#39;b&#39;</code>, <code>id=&#39;d&#39;</code></li><li><code>(A + B[id=&#39;b&#39;]) &amp;&amp; !(C + D[id=&#39;d&#39;])</code> 的快速查询为 <code>id=&#39;b&#39;</code></li></ul><p>需要注意的是它们内部的 局部快速查找 仍然独立成立</p><ul><li><code>(X[id=&#39;x&#39;] &lt;&lt;n A + B[id=&#39;b&#39;]) &amp;&amp; !(Y[id=&#39;y&#39;] &lt;&lt;n C + D[id=&#39;d&#39;])</code> 的快速查询为 <code>id=&#39;b&#39;</code>, 局部快速查询分别是 <code>id=&#39;x&#39;</code>, <code>id=&#39;y&#39;</code></li></ul><div class="note custom-block github-alert"><p class="custom-block-title">注意</p><p>如果选择器不存在满足快速查找的格式, fastQuery 是否开启都不影响查询复杂度</p></div><h3 id="optimize-example" tabindex="-1">优化示例 <a class="header-anchor" href="#optimize-example" aria-label="Permalink to &quot;优化示例 {#optimize-example}&quot;">​</a></h3><p>以 <a href="https://i.gkd.li/i/16201605?gkd=W3ZpZD0iaW1hZ2UiXSA8PG4gW3ZpZD0icmVjeWNsZXJWaWV3Il0gPDxuIFt2aWQ9ImNvbnRlbnRfbGF5b3V0Il0" target="_blank" rel="noreferrer"><code>[vid=&quot;image&quot;] &lt;&lt;n [vid=&quot;recyclerView&quot;] &lt;&lt;n [vid=&quot;content_layout&quot;]</code></a> 为例</p><p>上面的选择器存在 3 个快速查找优化</p><ul><li><code>[vid=&quot;content_layout&quot;]</code> 从 根节点 优化查询找到 <code>content_layout</code></li><li><code>[vid=&quot;recyclerView&quot;]</code> 从 <code>content_layout</code> 优化查询找到 <code>recyclerView</code></li><li><code>[vid=&quot;image&quot;]</code> 从 <code>recyclerView</code> 优化查询找到 <code>image</code></li></ul><p>也就是只需要调用 <code>findAccessibilityNodeInfosByViewId</code> 3 次即可得到目标节点</p><p>如果是从根节点开始查询, 根据快照内的 选择器路径视图</p><ul><li><code>[vid=&quot;content_layout&quot;]</code> 从 根节点 需要 7 次找到 <code>content_layout</code></li><li><code>[vid=&quot;recyclerView&quot;]</code> 从 <code>content_layout</code> 需要 48 次找到 <code>recyclerView</code></li><li><code>[vid=&quot;image&quot;]</code> 从 <code>recyclerView</code> 需要 44 次找到 <code>image</code></li></ul>',55)]))}const h=o(i,[["render",l]]);export{b as __pageData,h as default};
1
+ import{_ as o,o as d,c as t,ao as c}from"./chunks/framework.DZAoeHqG.js";const b=JSON.parse('{"title":"查询优化","description":"","frontmatter":{},"headers":[],"relativePath":"guide/optimize.md","filePath":"guide/optimize.md","lastUpdated":1744454038000}'),i={name:"guide/optimize.md"};function l(a,e,n,r,p,u){return d(),t("div",null,e[0]||(e[0]=[c('<h1 id="title" tabindex="-1">查询优化 <a class="header-anchor" href="#title" aria-label="Permalink to &quot;查询优化 {#title}&quot;">​</a></h1><p>一些关于查询优化的说明</p><h2 id="query-root" tabindex="-1">查找根节点 <a class="header-anchor" href="#query-root" aria-label="Permalink to &quot;查找根节点 {#query-root}&quot;">​</a></h2><p>选择器通过满足下列格式的属性选择器标识根节点</p><ul><li><code>[parent=null]</code></li><li><code>A[id=&#39;xxx&#39;][parent=null][text=&#39;xxx&#39;]</code></li></ul><p>即只需存在一个 <code>[parent=null]</code> 就满足条件, 以下是对此的优化</p><h3 id="enforce" tabindex="-1">主动查询 <a class="header-anchor" href="#enforce" aria-label="Permalink to &quot;主动查询 {#enforce}&quot;">​</a></h3><p>一般的选择器如 <code>LinearLayout &gt; TextView</code>, 选择器需要从 根节点/事件节点 使用深度先序顺序遍历子孙节点并判断是否满足条件</p><p>如果屏幕上有 n 个节点, 判断逻辑需要运行 n 次</p><p>但是如果选择器是类似 <code>TextView &lt;3 LinearLayout &lt;2 [parent=null]</code>, 由于在右侧标明了从根节点查找</p><p>因此上面的 <code>深度先序顺序遍历子孙节点</code> 将省略, 只进行一次判断, 不需要判断 n 次</p><p>但是如果你在子查询里使用 <code>&lt;&lt;n</code>, 例如 <code>TextView &lt;&lt;n [parent=null]</code></p><p>根据上面说明的优化, 虽然也只有一次判断, 但是由于 <code>&lt;&lt;n</code> 导致内部的子判断也是 n 次, 实际上没有优化判断次数</p><div class="tip custom-block github-alert"><p class="custom-block-title">提示</p><p>实际上从根节点开始匹配并且目标不是根节点的选择器如 <code>A &gt; B</code> 都可等价为 <code>A &gt; @B &lt;&lt;n [parent=null]</code></p></div><h3 id="ancestor" tabindex="-1">祖先节点 <a class="header-anchor" href="#ancestor" aria-label="Permalink to &quot;祖先节点 {#ancestor}&quot;">​</a></h3><p>在示例 <code>A &lt;3 [parent=null] &gt;n TextView</code> 中, 选择器找到 <code>TextView</code> 后, 根据 <code>&gt;n</code> 表示任意祖先节点</p><p>在默认情况下, 如果 <code>TextView</code> 的层级很深, 那必然需要多次调用 获取父节点 才能最终匹配</p><p>但是如果左侧的选择器是 <code>[parent=null] &gt;n</code>, 这表示找到根节点, 选择器此时不会去执行多次调用 获取父节点</p><p>在 GKD 内部, 根节点可以直接通过 API 获取, 因此 <code>[parent=null] &gt;n</code> 只需要调用一次方法</p><h2 id="fast-query" tabindex="-1">快速查询 <a class="header-anchor" href="#fast-query" aria-label="Permalink to &quot;快速查询 {#fast-query}&quot;">​</a></h2><div class="note custom-block github-alert"><p class="custom-block-title">注意</p><p>此优化需要设置 <a href="/api/interfaces/RawCommonProps#fastquery">fastQuery</a> 来启用<br> 并且节点在快照面板被标识 <code>可快速查找</code>, 否则查找不到</p></div><h3 id="query-scene" tabindex="-1">查询场景 <a class="header-anchor" href="#query-scene" aria-label="Permalink to &quot;查询场景 {#query-scene}&quot;">​</a></h3><p>一般情况下, 选择器 <code>[vid=&quot;name&quot;]</code> 需要从 根节点/事件节点 使用深度先序顺序遍历子孙节点并判断是否满足条件</p><p>但是 Android 提供了如下两个快速获取节点的 Api</p><ul><li><a href="https://developer.android.google.cn/reference/android/view/accessibility/AccessibilityNodeInfo#findAccessibilityNodeInfosByViewId(java.lang.String)" target="_blank" rel="noreferrer">findAccessibilityNodeInfosByViewId</a></li><li><a href="https://developer.android.google.cn/reference/android/view/accessibility/AccessibilityNodeInfo#findAccessibilityNodeInfosByText(java.lang.String)" target="_blank" rel="noreferrer">findAccessibilityNodeInfosByText</a></li></ul><p>这使得可以通过 <code>id</code>/<code>vid</code>/<code>text</code> 直接获取子孙节点</p><p>对此我们需要规定符合特定条件的选择器来调用这些 Api 从而跳过手动遍历子孙节点</p><h3 id="rule-format" tabindex="-1">规定格式 <a class="header-anchor" href="#rule-format" aria-label="Permalink to &quot;规定格式 {#rule-format}&quot;">​</a></h3><p>所有 <code>末尾属性选择器</code>的<code>第一个属性选择表达式</code>符合下面的结构之一</p><ul><li><code>[id=&#39;abc&#39;]</code></li><li><code>[vid=&#39;abc&#39;]</code></li><li><code>[text=&#39;abc&#39;]</code></li><li><code>[text^=&#39;abc&#39;]</code></li><li><code>[text*=&#39;abc&#39;]</code></li><li><code>[text$=&#39;abc&#39;]</code></li></ul><p>或者使用 <code>||</code> 将它们连接形成的逻辑表达式也符合条件, 即如下格式</p><ul><li><code>[id=&#39;abc&#39; || id=&#39;abc2&#39;]</code></li><li><code>[id=&#39;abc&#39; || vid=&#39;abc&#39; || text=&#39;abc&#39; || text^=&#39;abc&#39; || text*=&#39;abc&#39; || text$=&#39;abc&#39;]</code></li></ul><p>这些规定的格式都是为了提取 <code>id</code>/<code>vid</code>/<code>text</code> 来调用上面的两个 Android Api</p><p>下面给出实际示例: ✅ 表示符合格式, ❎ 表示不符合格式</p><ul><li><code>A &gt; B + C[id=&#39;x&#39;][childCount=2]</code> ✅</li><li><code>A &gt; B + C[childCount=2][id=&#39;x&#39;]</code> ❎</li><li><code>A &gt; B + C[id=&#39;x&#39; || text=&#39;manbaout&#39; || text*=&#39;ikun&#39;][childCount=2]</code> ✅</li><li><code>A &gt; B + C[childCount=2][id=&#39;x&#39; || text=&#39;manbaout&#39; || text*=&#39;ikun&#39;]</code> ❎</li></ul><p>这样一个选择器只能在右侧使用快速查询, 为了在中间的子选择器也能使用</p><p>额外规定如果属性选择器如果符合上面格式并且右侧是 <code>&lt;&lt;n</code>, 也能在局部使用快速查找</p><p>示例 <code>A &gt; B + C[id=&#39;x&#39;][childCount=2] &lt;&lt;n D</code> 中的 <code>C[id=&#39;x&#39;][childCount=2] &lt;&lt;n</code> 可以使用局部快速查找</p><p>下面给出满足局部查询优化的示例: ✅ 表示符合格式, ❎ 表示不符合格式</p><ul><li><code>A &gt; B + C[id=&#39;x&#39;][childCount=2] &lt;&lt;n D</code> ✅</li><li><code>A &gt; B + C[childCount=2][id=&#39;x&#39;] &lt;&lt;n D</code> ❎</li></ul><p>上面介绍的是只有一个局部选择器的情况, 下面给出多个局部快速查找的的示例</p><p>如 <code>A &gt; C[id=&#39;x&#39;] &lt;&lt;n D[id=&#39;y&#39;] &lt;&lt;n E</code>, 其中的 <code>C[id=&#39;x&#39;] &lt;&lt;n</code> 和 <code>D[id=&#39;y&#39;] &lt;&lt;n</code> 都可以使用局部快速查找</p><p>如果使用 <code>||</code>, <code>&amp;&amp;</code> 连接多个选择器时, 快速查询为它们的并集</p><p>如果选择器被 <code>!(xxx)</code> 包裹, 则不符合快速查询格式, 也就是 <code>!</code> 会取消其内部所有的快速查询</p><ul><li><code>(A + B[id=&#39;b&#39;]) || (C + D[id=&#39;d&#39;])</code> 的快速查询为 <code>id=&#39;b&#39;</code>, <code>id=&#39;d&#39;</code></li><li><code>(A + B[id=&#39;b&#39;]) &amp;&amp; (C + D[id=&#39;d&#39;])</code> 的快速查询为 <code>id=&#39;b&#39;</code>, <code>id=&#39;d&#39;</code></li><li><code>(A + B[id=&#39;b&#39;]) &amp;&amp; !(C + D[id=&#39;d&#39;])</code> 的快速查询为 <code>id=&#39;b&#39;</code></li></ul><p>需要注意的是它们内部的 局部快速查找 仍然独立成立</p><ul><li><code>(X[id=&#39;x&#39;] &lt;&lt;n A + B[id=&#39;b&#39;]) &amp;&amp; !(Y[id=&#39;y&#39;] &lt;&lt;n C + D[id=&#39;d&#39;])</code> 的快速查询为 <code>id=&#39;b&#39;</code>, 局部快速查询分别是 <code>id=&#39;x&#39;</code>, <code>id=&#39;y&#39;</code></li></ul><div class="note custom-block github-alert"><p class="custom-block-title">注意</p><p>如果选择器不存在满足快速查找的格式, fastQuery 是否开启都不影响查询复杂度</p></div><h3 id="optimize-example" tabindex="-1">优化示例 <a class="header-anchor" href="#optimize-example" aria-label="Permalink to &quot;优化示例 {#optimize-example}&quot;">​</a></h3><p>以 <a href="https://i.gkd.li/i/16201605?gkd=W3ZpZD0iaW1hZ2UiXSA8PG4gW3ZpZD0icmVjeWNsZXJWaWV3Il0gPDxuIFt2aWQ9ImNvbnRlbnRfbGF5b3V0Il0" target="_blank" rel="noreferrer"><code>[vid=&quot;image&quot;] &lt;&lt;n [vid=&quot;recyclerView&quot;] &lt;&lt;n [vid=&quot;content_layout&quot;]</code></a> 为例</p><p>上面的选择器存在 3 个快速查找优化</p><ul><li><code>[vid=&quot;content_layout&quot;]</code> 从 根节点 优化查询找到 <code>content_layout</code></li><li><code>[vid=&quot;recyclerView&quot;]</code> 从 <code>content_layout</code> 优化查询找到 <code>recyclerView</code></li><li><code>[vid=&quot;image&quot;]</code> 从 <code>recyclerView</code> 优化查询找到 <code>image</code></li></ul><p>也就是只需要调用 <code>findAccessibilityNodeInfosByViewId</code> 3 次即可得到目标节点</p><p>如果是从根节点开始查询, 根据快照内的 选择器路径视图</p><ul><li><code>[vid=&quot;content_layout&quot;]</code> 从 根节点 需要 7 次找到 <code>content_layout</code></li><li><code>[vid=&quot;recyclerView&quot;]</code> 从 <code>content_layout</code> 需要 48 次找到 <code>recyclerView</code></li><li><code>[vid=&quot;image&quot;]</code> 从 <code>recyclerView</code> 需要 44 次找到 <code>image</code></li></ul>',55)]))}const h=o(i,[["render",l]]);export{b as __pageData,h as default};
@@ -1 +1 @@
1
- import{_ as o,o as d,c as t,ao as c}from"./chunks/framework.DZAoeHqG.js";const b=JSON.parse('{"title":"查询优化","description":"","frontmatter":{},"headers":[],"relativePath":"guide/optimize.md","filePath":"guide/optimize.md","lastUpdated":1744453064000}'),i={name:"guide/optimize.md"};function l(a,e,n,r,p,u){return d(),t("div",null,e[0]||(e[0]=[c("",55)]))}const h=o(i,[["render",l]]);export{b as __pageData,h as default};
1
+ import{_ as o,o as d,c as t,ao as c}from"./chunks/framework.DZAoeHqG.js";const b=JSON.parse('{"title":"查询优化","description":"","frontmatter":{},"headers":[],"relativePath":"guide/optimize.md","filePath":"guide/optimize.md","lastUpdated":1744454038000}'),i={name:"guide/optimize.md"};function l(a,e,n,r,p,u){return d(),t("div",null,e[0]||(e[0]=[c("",55)]))}const h=o(i,[["render",l]]);export{b as __pageData,h as default};
@@ -1 +1 @@
1
- import{_ as t,o as r,c as i,j as e,a as s}from"./chunks/framework.DZAoeHqG.js";const u=JSON.parse('{"title":"隐私政策","description":"","frontmatter":{},"headers":[],"relativePath":"guide/privacy.md","filePath":"guide/privacy.md","lastUpdated":1744453064000}'),o={name:"guide/privacy.md"};function d(n,a,c,l,p,m){return r(),i("div",null,a[0]||(a[0]=[e("h1",{id:"title",tabindex:"-1"},[s("隐私政策 "),e("a",{class:"header-anchor",href:"#title","aria-label":'Permalink to "隐私政策 {#title}"'},"​")],-1),e("p",null,"GKD 不收集或分享任何用户个人数据",-1)]))}const _=t(o,[["render",d]]);export{u as __pageData,_ as default};
1
+ import{_ as t,o as r,c as i,j as e,a as s}from"./chunks/framework.DZAoeHqG.js";const u=JSON.parse('{"title":"隐私政策","description":"","frontmatter":{},"headers":[],"relativePath":"guide/privacy.md","filePath":"guide/privacy.md","lastUpdated":1744454038000}'),o={name:"guide/privacy.md"};function d(n,a,c,l,p,m){return r(),i("div",null,a[0]||(a[0]=[e("h1",{id:"title",tabindex:"-1"},[s("隐私政策 "),e("a",{class:"header-anchor",href:"#title","aria-label":'Permalink to "隐私政策 {#title}"'},"​")],-1),e("p",null,"GKD 不收集或分享任何用户个人数据",-1)]))}const _=t(o,[["render",d]]);export{u as __pageData,_ as default};
@@ -1 +1 @@
1
- import{_ as t,o as r,c as i,j as e,a as s}from"./chunks/framework.DZAoeHqG.js";const u=JSON.parse('{"title":"隐私政策","description":"","frontmatter":{},"headers":[],"relativePath":"guide/privacy.md","filePath":"guide/privacy.md","lastUpdated":1744453064000}'),o={name:"guide/privacy.md"};function d(n,a,c,l,p,m){return r(),i("div",null,a[0]||(a[0]=[e("h1",{id:"title",tabindex:"-1"},[s("隐私政策 "),e("a",{class:"header-anchor",href:"#title","aria-label":'Permalink to "隐私政策 {#title}"'},"​")],-1),e("p",null,"GKD 不收集或分享任何用户个人数据",-1)]))}const _=t(o,[["render",d]]);export{u as __pageData,_ as default};
1
+ import{_ as t,o as r,c as i,j as e,a as s}from"./chunks/framework.DZAoeHqG.js";const u=JSON.parse('{"title":"隐私政策","description":"","frontmatter":{},"headers":[],"relativePath":"guide/privacy.md","filePath":"guide/privacy.md","lastUpdated":1744454038000}'),o={name:"guide/privacy.md"};function d(n,a,c,l,p,m){return r(),i("div",null,a[0]||(a[0]=[e("h1",{id:"title",tabindex:"-1"},[s("隐私政策 "),e("a",{class:"header-anchor",href:"#title","aria-label":'Permalink to "隐私政策 {#title}"'},"​")],-1),e("p",null,"GKD 不收集或分享任何用户个人数据",-1)]))}const _=t(o,[["render",d]]);export{u as __pageData,_ as default};
@@ -1,4 +1,4 @@
1
- import{_ as s,B as n,o as c,c as r,ao as l,j as t,G as a,w as p}from"./chunks/framework.DZAoeHqG.js";const A=JSON.parse('{"title":"选择器","description":"","frontmatter":{},"headers":[],"relativePath":"guide/selector.md","filePath":"guide/selector.md","lastUpdated":1744453064000}'),g={name:"guide/selector.md"},h={class:"vp-raw"},x={tabindex:"0"},u={style:{"text-align":"center"}},y={style:{"text-align":"center"}},k={style:{"text-align":"center"}},b={style:{"text-align":"center"}},m={style:{"text-align":"center"}};function E(v,e,f,_,C,q){const i=n("IdentifierField"),d=n("GImg"),o=n("NImageGroup");return c(),r("div",null,[e[16]||(e[16]=l('<h1 id="title" tabindex="-1">选择器 <a class="header-anchor" href="#title" aria-label="Permalink to &quot;选择器 {#title}&quot;">​</a></h1><p>一个类似 CSS 选择器的选择器, 能联系节点上下文信息, 更容易也更精确找到目标节点</p><h2 id="why" tabindex="-1">为什么需要选择器 <a class="header-anchor" href="#why" aria-label="Permalink to &quot;为什么需要选择器 {#why}&quot;">​</a></h2><p>我们的目的是查询获取屏幕上的某个节点, 最简单的情况下, 目标节点已经有唯一的 id/text 标识</p><p>这时只需要配置 id/text 就能简单执行点击, 但实际情况很多节点都缺失了这些信息, 无法简单查询获取</p><p>节点还存在很多额外信息没有被利用到, 节点所有属性可以在 <a href="https://i.gkd.li/i/14045424" target="_blank" rel="noreferrer">快照-14045424</a> 的属性面板看到</p><p>另一种常见的情况是节点没有有效属性, 这时我们需要根据周围的节点去定位</p><p>选择器就是为了解决这些问题而开发的, 下面是对选择器语法的详细介绍</p><div class="tip custom-block github-alert"><p class="custom-block-title">提示</p><p>本文是从 <strong>抽象语法树(AST)</strong> 的角度去完备地介绍选择器结构<br> 如果你是初学者, 建议你直接看 <a href="./example">选择器-示例</a></p></div><h2 id="syntax" tabindex="-1">语法 <a class="header-anchor" href="#syntax" aria-label="Permalink to &quot;语法 {#syntax}&quot;">​</a></h2><p>与 CSS 类似, 一个选择器由 属性选择器 和 关系选择器 交叉组成, 并且开头末尾必须是 属性选择器</p><p>示例 <code>div &gt; img</code> 的结构是 <code>属性选择器 关系选择器 属性选择器</code>, 它表示选择父节点是 <code>div</code> 的 <code>img</code> 节点, 这与 <a href="https://developer.mozilla.org/zh-CN/docs/Web/CSS/Child_combinator" target="_blank" rel="noreferrer">相同 CSS 语法</a> 语义一致</p><p>另外 属性选择器 和 关系选择器 之间必须<strong>强制</strong>用 空白字符(空格/换行/回车/制表) 隔开</p><p>即 <code>div&gt;img</code> 非法, 必须写成 <code>div &gt; img</code></p><p>下面分别介绍 <a href="#attr">属性选择器</a> 和 <a href="#connect">关系选择器</a></p><h2 id="attr" tabindex="-1">属性选择 <a class="header-anchor" href="#attr" aria-label="Permalink to &quot;属性选择 {#attr}&quot;">​</a></h2><p>它和 CSS 语法的 属性选择器很相似, 但更强大, 如下是一个示例</p><p><code>@TextView[a=1][b^=&#39;2&#39;][c*=&#39;a&#39;||d.length&gt;7&amp;&amp;e=false][!(f=true)][g.plus(1)&gt;0]</code></p><p><code>@</code> 表示选择此节点, 一条规则最后属性选择器 <code>@</code> 生效, 如果没有 <code>@</code>, 取最后一个属性选择器</p><p><code>TextView</code> 代表节点的 name 属性, 而且与 CSS 相似, <code>*</code> 表示匹配任意属性</p><p>由于该选择器主要用于 Android 平台, 节点的 name 都是 java 类如 android.text.TextView 这种形式</p><p>为了方便书写规则, 上述 <code>TextView</code> 等价 <code>[name=&#39;TextView&#39;||name$=&#39;.TextView&#39;]</code></p><p><code>[]</code> 内部是一个 逻辑表达式/布尔表达式/取反表达式</p><ul><li>逻辑表达式 -&gt; <code>name=&#39;TextView&#39;||name$=&#39;.TextView&#39;</code></li><li>布尔表达式 -&gt; <code>name=&#39;TextView&#39;</code></li><li>取反表达式 -&gt; <code>!(name$=&#39;TextView&#39;)</code>, <code>!(name=&#39;TextView&#39;||name$=&#39;.TextView&#39;)</code></li></ul><p>注意 取反表达式中的 <code>!</code> 后面必须是 <code>(...)</code>, <code>!!(...)</code> 是非法的</p><p>逻辑表达式 有两个操作符 <code>||</code> 和 <code>&amp;&amp;</code>. <code>&amp;&amp;</code> 优先级更高</p><p>即 <code>[a&gt;1||b&gt;1&amp;&amp;c&gt;1||d&gt;1]</code> 等价于 <code>[a&gt;1||(b&gt;1&amp;&amp;c&gt;1)||d&gt;1]</code></p><p>并列的 <code>[]</code> 视为使用 <code>&amp;&amp;</code> 的逻辑表达式, 即 <code>[a=1][b=1]</code> 等价于 <code>[a=1&amp;&amp;b=1]</code></p><p>布尔表达式 由 <code>左值 操作符 右值</code> 构成, 左值/右值 是一个 值表达式</p><p>下面分别介绍 <a href="#value-exp">值表达式</a> 和 <a href="#attr-operator">操作符</a></p><h2 id="value-exp" tabindex="-1">值表达式 <a class="header-anchor" href="#value-exp" aria-label="Permalink to &quot;值表达式 {#value-exp}&quot;">​</a></h2><p>值表达式分两类: 变量 和 字面量, 下图是关系表格</p>',32)),e[17]||(e[17]=t("table",null,[t("thead",null,[t("tr",null,[t("th",{colspan:"2"},[t("div",{"text-center":""},"值表达式")]),t("th",null,"示例")])]),t("tbody",null,[t("tr",null,[t("td",{rowspan:"3"},"变量"),t("td",null,"标识符"),t("td",null,[t("code",null," a ")])]),t("tr",null,[t("td",null,"成员表达式"),t("td",null,[t("code",null,"a.b")])]),t("tr",null,[t("td",null,"调用表达式"),t("td",null,[t("code",null,"a(b,c)")])]),t("tr",null,[t("td",{rowspan:"4"},"字面量"),t("td",null,"null"),t("td",null,[t("code",null,"null")])]),t("tr",null,[t("td",null,"boolean"),t("td",null,[t("code",null,"false")])]),t("tr",null,[t("td",null,"int"),t("td",null,[t("code",null,"114514")])]),t("tr",null,[t("td",null,"string"),t("td",null,[t("code",null,"'ikun'")])])])],-1)),e[18]||(e[18]=l('<p>每个值表达式都有其类型, 可细分为两类</p><p>基础类型: <code>null</code> <code>boolean</code> <code>int</code> <code>string</code></p><p>对象类型: <code>object</code></p><p>对象类型可细分为 <code>context</code> 和 <code>node</code> 两种类型</p><p>比如选择器 <code>[parent=null]</code> 代表选择一个父节点是 null 的节点, 即根节点</p><p>上面的 <code>parent</code> 属于 值表达式/变量/标识符, 类型是 <code>node</code></p><p><code>context</code> 类型指代当前节点的上下文, 当想使用不属于 <code>node</code> 上的属性方法时就需要 <code>context</code></p><p>你可以在 <a href="./node">选择器-属性方法</a> 查看所有类型和对应属性方法</p><h3 id="var" tabindex="-1">变量 <a class="header-anchor" href="#var" aria-label="Permalink to &quot;变量 {#var}&quot;">​</a></h3><p>首先需要了解 <strong>标识符</strong>: 正则匹配 <code>^[_a-zA-Z][a-zA-Z0-9_]*$</code> 并且不是 <code>null</code>/<code>true</code>/<code>false</code> 的字符串</p><p>示例合法变量名: <code>a</code> <code>ikun</code> <code>manbaout</code>, 下面可输入字符测试否是合法</p>',11)),t("div",h,[a(i)]),e[19]||(e[19]=l('<hr><p>接下来了解 <strong>成员表达式</strong> <code>a.b</code>, 它被 <code>.</code> 分为两个部分, 前部分是另一个变量, 后部分是一个标识符作为属性</p><p>同理 <code>a.b.c</code> 也是一个 成员表达式, 其中 <code>a.b</code> 是它的变量部分, <code>c</code> 是一个合法的标识符作为属性</p><p>根据上面标识符的规则, <code>a.1</code>, <code>a.null</code>, <code>a.true</code> 都是非法成员表达式</p><hr><p>最后了解 <strong>调用表达式</strong> <code>a(b,c)</code>, 它由两个部分构成, <code>(</code> 的左侧 <code>a</code> 是一个变量作为 调用者</p><p><code>(b,c)</code> 作为调用参数(值类型), 调用参数数量可以是 0 或任意个, 即 <code>a()</code> 也是合法的</p><p>合法的其它例子: <code>a.b(c,d).e(f).g(1,2,true)</code></p><p>需要注意调用者不能是 调用表达式, 即 <code>a()()</code> 非法</p><h3 id="literal" tabindex="-1">字面量 <a class="header-anchor" href="#literal" aria-label="Permalink to &quot;字面量 {#literal}&quot;">​</a></h3><p>根据上面的表格, 字面量有 4 种: <code>null</code>, <code>boolean</code>, <code>int</code>, <code>string</code></p><ul><li>null</li><li>boolean 使用 <code>true</code>/<code>false</code></li><li>int 匹配 <code>^-?[0-9]$</code>, 即十进制整数, 示例 <code>-1</code>,<code>0</code>,<code>1</code>, 不支持 <code>+1</code> 这种写法</li><li>string 使用 &#39; ` &quot; 之一成对包裹, 内部字符转义使用 <code>\\</code><br> 所有的转义字符示例 <code>\\\\</code>, <code>\\&#39;</code>, <code>\\&quot;</code>, <code>\\`</code>, <code>\\n</code>, <code>\\r</code>, <code>\\t</code>, <code>\\b</code>, <code>\\xfF</code>, <code>\\uffFF</code><br> 不支持多行字符, 处于 <code>[0, 0x1F]</code> 的控制字符必须使用转义字符表示</li></ul><p>此外使用 string 时需要了解 <a href="#nest-escape">嵌套转义字符</a> 以避免出现错误</p><h2 id="attr-operator" tabindex="-1">操作符 <a class="header-anchor" href="#attr-operator" aria-label="Permalink to &quot;操作符 {#attr-operator}&quot;">​</a></h2><p>操作符 用于连接两个 值表达式</p><table tabindex="0"><thead><tr><th style="text-align:center;">操作符</th><th style="text-align:center;">名称</th><th style="text-align:center;">说明</th></tr></thead><tbody><tr><td style="text-align:center;">=</td><td style="text-align:center;">equal</td><td style="text-align:center;">等于</td></tr><tr><td style="text-align:center;">!=</td><td style="text-align:center;">notEqual</td><td style="text-align:center;">不等于</td></tr><tr><td style="text-align:center;">&gt;</td><td style="text-align:center;">more</td><td style="text-align:center;">大于</td></tr><tr><td style="text-align:center;">&gt;=</td><td style="text-align:center;">moreEqual</td><td style="text-align:center;">大于或等于</td></tr><tr><td style="text-align:center;">&lt;</td><td style="text-align:center;">less</td><td style="text-align:center;">小于</td></tr><tr><td style="text-align:center;">&lt;=</td><td style="text-align:center;">lessEqual</td><td style="text-align:center;">小于或等于</td></tr><tr><td style="text-align:center;">^=</td><td style="text-align:center;">startsWith</td><td style="text-align:center;">以...开头</td></tr><tr><td style="text-align:center;">!^=</td><td style="text-align:center;">notStartsWith</td><td style="text-align:center;">不以...开头</td></tr><tr><td style="text-align:center;">*=</td><td style="text-align:center;">contains</td><td style="text-align:center;">包含...</td></tr><tr><td style="text-align:center;">!*=</td><td style="text-align:center;">notContains</td><td style="text-align:center;">不包含...</td></tr><tr><td style="text-align:center;">$=</td><td style="text-align:center;">endsWith</td><td style="text-align:center;">以...结尾</td></tr><tr><td style="text-align:center;">!$=</td><td style="text-align:center;">notEndsWith</td><td style="text-align:center;">不以...结尾</td></tr><tr><td style="text-align:center;">~=</td><td style="text-align:center;">matches</td><td style="text-align:center;">正则匹配...</td></tr><tr><td style="text-align:center;">!~=</td><td style="text-align:center;">notMatches</td><td style="text-align:center;">正则不匹配...</td></tr></tbody></table><p>附加说明: <code>matches</code>/<code>notMatches</code> 要求 值 必须是合法的 <a href="https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html" target="_blank" rel="noreferrer">Java/Kotlin 正则表达式</a>, 否则提示语法错误</p><p>并且 <code>matches</code>/<code>notMatches</code> 右侧的值只能是 字面量/字符串</p><p>操作符只能使用在对应的类型的值, 比如 <code>a&gt;&#39;&#39;</code> 类型不匹配, 将提示 <code>非法类型</code></p><p>下面表格中 <code>-</code> 表示类型不匹配</p><table tabindex="0"><thead><tr><th style="text-align:center;"></th><th style="text-align:center;">null</th><th style="text-align:center;">boolean</th><th style="text-align:center;">int</th><th style="text-align:center;">string</th></tr></thead><tbody><tr><td style="text-align:center;">=</td><td style="text-align:center;">✔</td><td style="text-align:center;">✔</td><td style="text-align:center;">✔</td><td style="text-align:center;">✔</td></tr><tr><td style="text-align:center;">!=</td><td style="text-align:center;">✔</td><td style="text-align:center;">✔</td><td style="text-align:center;">✔</td><td style="text-align:center;">✔</td></tr><tr><td style="text-align:center;">&gt;</td><td style="text-align:center;">-</td><td style="text-align:center;">-</td><td style="text-align:center;">✔</td><td style="text-align:center;">-</td></tr><tr><td style="text-align:center;">&gt;=</td><td style="text-align:center;">-</td><td style="text-align:center;">-</td><td style="text-align:center;">✔</td><td style="text-align:center;">-</td></tr><tr><td style="text-align:center;">&lt;</td><td style="text-align:center;">-</td><td style="text-align:center;">-</td><td style="text-align:center;">✔</td><td style="text-align:center;">-</td></tr><tr><td style="text-align:center;">&lt;=</td><td style="text-align:center;">-</td><td style="text-align:center;">-</td><td style="text-align:center;">✔</td><td style="text-align:center;">-</td></tr><tr><td style="text-align:center;">^=</td><td style="text-align:center;">-</td><td style="text-align:center;">-</td><td style="text-align:center;">-</td><td style="text-align:center;">✔</td></tr><tr><td style="text-align:center;">!^=</td><td style="text-align:center;">-</td><td style="text-align:center;">-</td><td style="text-align:center;">-</td><td style="text-align:center;">✔</td></tr><tr><td style="text-align:center;">*=</td><td style="text-align:center;">-</td><td style="text-align:center;">-</td><td style="text-align:center;">-</td><td style="text-align:center;">✔</td></tr><tr><td style="text-align:center;">!*=</td><td style="text-align:center;">-</td><td style="text-align:center;">-</td><td style="text-align:center;">-</td><td style="text-align:center;">✔</td></tr><tr><td style="text-align:center;">$=</td><td style="text-align:center;">-</td><td style="text-align:center;">-</td><td style="text-align:center;">-</td><td style="text-align:center;">✔</td></tr><tr><td style="text-align:center;">!$=</td><td style="text-align:center;">-</td><td style="text-align:center;">-</td><td style="text-align:center;">-</td><td style="text-align:center;">✔</td></tr><tr><td style="text-align:center;">~=</td><td style="text-align:center;">-</td><td style="text-align:center;">-</td><td style="text-align:center;">-</td><td style="text-align:center;">✔</td></tr><tr><td style="text-align:center;">!~=</td><td style="text-align:center;">-</td><td style="text-align:center;">-</td><td style="text-align:center;">-</td><td style="text-align:center;">✔</td></tr></tbody></table><p>除 <code>=</code>/<code>!=</code> 以外的操作符, 当节点属性是 null 时表达式为 <code>false</code></p><ul><li>a &gt; 233</li><li>a &gt;= 233</li><li>a &lt; 233</li><li>a &lt;= 233</li><li>a ^= &#39;xxx&#39;</li><li>a !^= &#39;xxx&#39;</li><li>a *= &#39;xxx&#39;</li><li>a !* &#39;xxx&#39;</li><li>a $= &#39;xxx&#39;</li><li>a !$= &#39;xxx&#39;</li><li>a ~= &#39;xxx&#39;</li><li>a !~= &#39;xxx&#39;</li></ul><p>即当 a 是 <code>null</code> 时以上表达式为 <code>false</code></p><p>你可能会对 <code>a !$= &#39;xxx&#39;</code> 在 a 是 <code>null</code> 表达式为 <code>false</code> 感到奇怪</p><p>那你可以换一种写法使用 <code>a=null || a!$=&#39;xxx&#39;</code> 或 <code>!(a$=&#39;xxx&#39;)</code></p><h2 id="connect" tabindex="-1">关系选择 <a class="header-anchor" href="#connect" aria-label="Permalink to &quot;关系选择 {#connect}&quot;">​</a></h2><p>关系选择器 由 关系操作符 和 关系表达式 构成, 用于连接两个属性选择器</p><p>简单示例: <code>div &gt; a</code>, 它 表示/约束 两个节点之间的关系, 下面介绍 <a href="#connect-operator">关系操作符</a> 和 <a href="#connect-exp">关系表达式</a></p><h3 id="connect-exp" tabindex="-1">关系表达式 <a class="header-anchor" href="#connect-exp" aria-label="Permalink to &quot;关系表达式 {#connect-exp}&quot;">​</a></h3><p>关系表达式 表示查找节点的范围, 有两种</p><ul><li>元组表达式 <code>(a1,a2,a3,a_n)</code>, 其中 a1, a2, a3, a_n 是常量有序递增正整数, 示例 <code>(1)</code>, <code>(2,3,5)</code></li><li>多项式表达式 <code>(an+b)</code>, 其中 a 和 b 是常量整数, 它是元组表达式的另一种表示, 这个元组的数满足集合 <code>{an+b|an+b&gt;=1,n&gt;=1}</code> 如果集合为空集则表达式非法<br> 当 a&lt;=0 时, 它具有等价的元组表达式<br> 示例 <code>(-n+4)</code> 等价于 <code>(1,2,3)</code><br> 示例 <code>(-3n+10)</code> 等价于 <code>(1,4,7)</code><br> 当 a&gt;0 时, 它表示无限的元组表达式<br> 示例 <code>(n)</code>, 它表示 <code>(1,2,3,...)</code> 一个无限的元组<br> 示例 <code>(2n-1)</code>, 它表示 <code>(1,3,5,...)</code> 一个无限的元组</li></ul><h3 id="connect-operator" tabindex="-1">关系操作符 <a class="header-anchor" href="#connect-operator" aria-label="Permalink to &quot;关系操作符 {#connect-operator}&quot;">​</a></h3><p>关系操作符 表示查找节点的方向, 有 6 种关系操作符</p><ul><li>前 5 种为 <code>+</code>, <code>-</code>, <code>&gt;</code>, <code>&lt;</code>, <code>&lt;&lt;</code>, 表示节点的相对位置</li></ul>',35)),a(o,null,{default:p(()=>[t("table",x,[e[15]||(e[15]=t("thead",null,[t("tr",null,[t("th",{style:{"text-align":"center"}},"操作符"),t("th",{style:{"text-align":"center"}},"名称"),t("th",{style:{"text-align":"center"}},"图例"),t("th",{style:{"text-align":"center"}},"选择器")])],-1)),t("tbody",null,[t("tr",null,[e[0]||(e[0]=t("td",{style:{"text-align":"center"}},"+",-1)),e[1]||(e[1]=t("td",{style:{"text-align":"center"}},"前置兄弟节点",-1)),t("td",u,[a(d,{src:"0020.png",width:"250"})]),e[2]||(e[2]=t("td",{style:{"text-align":"center"}},[t("a",{href:"https://i.gkd.li/i/14045424?gkd=KiArIFtfaWQ9MzNd",target:"_blank",rel:"noreferrer"},"* + [_id=33]")],-1))]),t("tr",null,[e[3]||(e[3]=t("td",{style:{"text-align":"center"}},"-",-1)),e[4]||(e[4]=t("td",{style:{"text-align":"center"}},"后置兄弟节点",-1)),t("td",y,[a(d,{src:"0021.png",width:"250"})]),e[5]||(e[5]=t("td",{style:{"text-align":"center"}},[t("a",{href:"https://i.gkd.li/i/14045424?gkd=KiAtIFtfaWQ9MzJd",target:"_blank",rel:"noreferrer"},"* - [_id=32]")],-1))]),t("tr",null,[e[6]||(e[6]=t("td",{style:{"text-align":"center"}},">",-1)),e[7]||(e[7]=t("td",{style:{"text-align":"center"}},"祖先节点",-1)),t("td",k,[a(d,{src:"0022.png",width:"250"})]),e[8]||(e[8]=t("td",{style:{"text-align":"center"}},[t("a",{href:"https://i.gkd.li/i/14045424?gkd=KiA-IFtfaWQ9OTBd",target:"_blank",rel:"noreferrer"},"* > [_id=90]")],-1))]),t("tr",null,[e[9]||(e[9]=t("td",{style:{"text-align":"center"}},"<",-1)),e[10]||(e[10]=t("td",{style:{"text-align":"center"}},"直接子节点",-1)),t("td",b,[a(d,{src:"0023.png",width:"250"})]),e[11]||(e[11]=t("td",{style:{"text-align":"center"}},[t("a",{href:"https://i.gkd.li/i/14045424?gkd=KiA8IFtfaWQ9ODld",target:"_blank",rel:"noreferrer"},"* < [_id=89]")],-1))]),t("tr",null,[e[12]||(e[12]=t("td",{style:{"text-align":"center"}},"<<",-1)),e[13]||(e[13]=t("td",{style:{"text-align":"center"}},"子孙节点(深度先序)",-1)),t("td",m,[a(d,{src:"0024.png",width:"250"})]),e[14]||(e[14]=t("td",{style:{"text-align":"center"}},[t("a",{href:"https://i.gkd.li/i/14045424?gkd=KiA8PDIgW19pZD0yOV0",target:"_blank",rel:"noreferrer"},"* <<2 [_id=29]")],-1))])])])]),_:1}),e[20]||(e[20]=l('<ul><li>另外一种为 <code>-&gt;</code>, 用于查询过程中, 表示当前节点的之前节点</li></ul><p>比如 <code>C -&gt;1 B &gt; A</code>, 由 A 找到 B 后, 此时的 C 就是 B 的之前节点即 A, 这适合想快速去之前节点重新查询关系的场景</p><p>将 关系操作符 和 关系表达式 连接起来就得到了 关系选择器</p><p><code>A +(a1,a2,a3,a_n) B</code> : A 是 B 的前置兄弟节点, 并且 A.index 满足 B.index-(a_m), 其中 a_m 是元组的任意一个数字</p><p><code>A -(a1,a2,a3,a_n) B</code> : A 是 B 的后置兄弟节点, 并且 A.index 满足 B.index+(a_m)</p><p><code>A &gt;(a1,a2,a3,a_n) B</code> : A 是 B 的祖先节点, 并且 A.depth 满足 B.depth-(a_m), 根节点的 depth=0</p><p><code>A &lt;(a1,a2,a3,a_n) B</code> : A 是 B 的直接子节点, 并且 A.index 满足 a_m-1</p><p><code>A &lt;&lt;(a1,a2,a3,a_n) B</code> : A 是 B 的子孙节点, 并且 A.order 满足 a_m-1, A.order 是深度优先先序遍历的索引</p><p><code>N -&gt;(a1,a2,a3,a_n) B &gt; A</code>, N 是 B 的之前(右侧)的已查询节点, 并且 N.index 满足 B.index-(a_m), index 顺序为 (n,n-1,...,2,1,0)</p><h3 id="connect-shortcut" tabindex="-1">表达式简写 <a class="header-anchor" href="#connect-shortcut" aria-label="Permalink to &quot;表达式简写 {#connect-shortcut}&quot;">​</a></h3><p>一般情况下, 并不需要写严格完整的表达式, 使用简化写法更方便快捷</p><p>下面是一些特殊情况下的简写表示</p><p>当 a=0 或 b=0 时, 括号可以省略, 以 <code>+</code> 为例</p><ul><li><code>A +(3n+0) B</code> -&gt; <code>A +(3n) B</code> -&gt; <code>A +3n B</code></li><li><code>A +(0n+3) B</code> -&gt; <code>A +(+3) B</code> -&gt; <code>A +3 B</code></li></ul><p>当 a=0 且 b=1 时, an+b 可以省略, 以 <code>+</code> 为例</p><ul><li><code>A +(0n+1) B</code> -&gt; <code>A + B</code></li></ul><p>此外 <code>A + B</code>,<code>A &gt; B</code> 都与等价的 CSS 语法语义相同</p><p>当 a=1 且 b=0 且操作符是 <code>&gt;</code>, 可以进一步简写, 比如</p><ul><li><code>A &gt;(1n+0) B</code> -&gt; <code>A &gt;n B</code> -&gt; <code>A B</code></li></ul><p>这与等价的 CSS 语法语义相同</p><h2 id="advanced" tabindex="-1">进阶 <a class="header-anchor" href="#advanced" aria-label="Permalink to &quot;进阶 {#advanced}&quot;">​</a></h2><p>正如上述的 <a href="#attr">属性选择</a> 一样, 选择器也可以组合为 逻辑表达式/取反表达式</p><p>只有一个格式要求: 单个选择器必须强制使用 <code>()</code> 包裹</p><ul><li><p>一般选择器 -&gt; <code>A + B</code> 或 <code>(A + B)</code></p></li><li><p>逻辑或表达式 -&gt; <code>(A + B) || (M &gt; N)</code>, 表示匹配节点满足 <code>A + B</code> 或 <code>M &gt; N</code></p><p>匹配结果遵循 <code>||</code> 的短路原则, 优先判断左侧的 <code>A + B</code>, 如果满足 <code>A + B</code> 那就是 <code>B</code>. 不满足 <code>A + B</code> 时, 判断右侧表达式, 如果满足 <code>M &gt; N</code> 那就是 <code>N</code>, 当然也可以在内部使用 <code>@</code> 自定义目标节点</p></li><li><p>逻辑且表达式 -&gt; <code>(A + B) &amp;&amp; (M &gt; N)</code>, 表示匹配节点满足 <code>A + B</code> 和 <code>M &gt; N</code>, 结果取右侧表达式结果, 结果为 <code>N</code>, 当然也可以在内部使用 <code>@</code> 自定义目标节点</p></li><li><p>取反表达式 -&gt; <code>!(A + B)</code>, 表示匹配节点应不满足 <code>A + B</code>, 需要注意的是 <code>!(@A + B)</code> 中的 <code>@</code> 是无效的, 它的匹配结果只能是末尾节点 <code>B</code></p></li></ul><p>此外 逻辑且表达式 的优先级更高, 即 <code>(A + B) || (C + B) &amp;&amp; (M &gt; N)</code> 等价于 <code>(A + B) || ((C + B) &amp;&amp; (M &gt; N))</code></p><h2 id="example" tabindex="-1">示例 <a class="header-anchor" href="#example" aria-label="Permalink to &quot;示例 {#example}&quot;">​</a></h2><div class="language-txt vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">txt</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span>@LinearLayout &gt; TextView[id=`com.byted.pangle:id/tt_item_tv`][text=`不感兴趣`]</span></span></code></pre></div><p>选择器从末尾开始查找, 首先找到 id=`com.byted.pangle:id/tt_item_tv` 和 text=`不感兴趣` 的 TextView, 并且父节点是 LinearLayout 的节点</p><p>此时我们得到两个节点 [LinearLayout, TextView] 根据 <code>@</code> 知道目标节点是 LinearLayout</p><p>实际上它与</p><div class="language-txt vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">txt</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span>TextView[id=`com.byted.pangle:id/tt_item_tv`][text=`不感兴趣`] &lt;n LinearLayout</span></span></code></pre></div><p>的目标匹配节点是等价的, 但是在查询算法时间复杂度上, 后者更慢</p><p>如下是网页无障碍快照审查工具, 使用它的搜索框的选择器查询可以实时测试编写的选择器</p><ul><li><a href="https://i.gkd.li/i/14045424" target="_blank" rel="noreferrer">快照-14045424</a></li><li><a href="https://i.gkd.li/i/14034770" target="_blank" rel="noreferrer">快照-14034770</a></li><li><a href="https://i.gkd.li/i/14031920" target="_blank" rel="noreferrer">快照-14031920</a></li><li><a href="https://i.gkd.li/i/14018243" target="_blank" rel="noreferrer">快照-14018243</a></li><li><a href="https://i.gkd.li/i/14011298" target="_blank" rel="noreferrer">快照-14011298</a></li><li><a href="https://i.gkd.li/i/13999908" target="_blank" rel="noreferrer">快照-13999908</a></li></ul><h2 id="note" tabindex="-1">一些注意 <a class="header-anchor" href="#note" aria-label="Permalink to &quot;一些注意 {#note}&quot;">​</a></h2><h3 id="match-order" tabindex="-1">匹配顺序 <a class="header-anchor" href="#match-order" aria-label="Permalink to &quot;匹配顺序 {#match-order}&quot;">​</a></h3><p>选择器的匹配顺序是 <strong>从右往左匹配</strong></p><p>例如 <code>FrameLayout &gt; TextView</code>, 它是先从 根节点/事件节点 找到 <code>TextView</code>, 然后再判断 parent 是不是 <code>FrameLayout</code></p><p>并不是先找到 <code>FrameLayout</code> 然后再判断 child 是否有 <code>TextView</code></p><p>同时在 <a href="https://i.gkd.li/i/15747758?gkd=RnJhbWVMYXlvdXQgPiBUZXh0Vmlldw" target="_blank" rel="noreferrer">选择器路径视图</a> 也会标明匹配顺序和查找方向</p><h3 id="regex-optimization" tabindex="-1">正则表达式优化 <a class="header-anchor" href="#regex-optimization" aria-label="Permalink to &quot;正则表达式优化 {#regex-optimization}&quot;">​</a></h3><p>对 <code>matches</code>/<code>notMatches</code> 的优化: 如果正则表达式满足下面的条件, 选择器将使用内置的简单的函数匹配, 而不是真正地去运行一个正则表达式</p><ul><li><code>[text~=&quot;(?is)abc.*&quot;]</code> -&gt; <code>startsWith(&#39;abc&#39;, ignoreCase = true)</code></li><li><code>[text~=&quot;(?is).*abc.*&quot;]</code> -&gt; <code>contains(&#39;abc&#39;, ignoreCase = true)</code></li><li><code>[text~=&quot;(?is).*abc&quot;]</code> -&gt; <code>endsWith(&#39;abc&#39;, ignoreCase = true)</code></li><li><code>[text!~=&quot;(?is)abc.*&quot;]</code> -&gt; <code>notStartsWith(&#39;abc&#39;, ignoreCase = true)</code></li><li><code>[text!~=&quot;(?is).*abc.*&quot;]</code> -&gt; <code>notContains(&#39;abc&#39;, ignoreCase = true)</code></li><li><code>[text!~=&quot;(?is).*abc&quot;]</code> -&gt; <code>notEndsWith(&#39;abc&#39;, ignoreCase = true)</code></li></ul><p>上面的 <code>abc</code> 指代不包含 <code>\\^$.?*|+()[]{}</code> 这类特殊正则字符的任意字符串, 如 <code>ikun</code> 符合, <code>ikun?</code> 不符合, <code>ignoreCase = true</code> 表示忽略大小写</p><p>简单来说就是如果你只想忽略大小写去简单匹配或不匹配一些字符串, 那么直接使用上面的格式</p><h3 id="regex-multiplatform" tabindex="-1">正则表达式一致性 <a class="header-anchor" href="#regex-multiplatform" aria-label="Permalink to &quot;正则表达式一致性 {#regex-multiplatform}&quot;">​</a></h3><p>由于 选择器 需要同时满足 浏览器/Js(审查工具), Android/Java(GKD) 运行, 而这两个平台的正则表达式的底层实现和语法表示略有不同</p><p>因此为了在 Js 端实现和 Java 一致的正则表达式规范, 网页审查工具借助 <a href="https://kotlinlang.org/docs/wasm-overview.html" target="_blank" rel="noreferrer">Kotlin Wasm</a> 将正则表达式的 matches 函数接口编译为 wasm 提供给 Js 调用</p><p>Kotlin Wasm 需要你的浏览器支持 <a href="https://developer.chrome.com/blog/wasmgc?hl=zh-cn" target="_blank" rel="noreferrer">WasmGC</a>, 也就是版本需要满足下列条件</p>',49)),a(d,{src:"0025.png"}),e[21]||(e[21]=l(`<p>如果你在 nodejs 使用 gkd 选择器, 需要 node&gt;=22 版本</p><p>如果你的 浏览器/nodejs 版本不满足, 正则表达式将自动回退到 Js 端实现, 以下是在 Js 端使用正则表达式需要注意的地方</p><p>比如上面的正则表达式优化例子中开头的 <code>(?is)</code> 是 Java 正则表达式的 inline flags 语法, 但实际上 Js 并不支持这样写, 只是选择器内部做了一些兼容让它支持</p><p>并且选择器的 Js 端只兼容在开头的 flags, 在内部的 flags 不支持, 此外 Java 和 Js 支持的 flags 也有不同, 某些特殊的表达式表现也不一致</p><p>总之不要使用太过复杂(多复杂我也不知道)的正则表达式, 某些正则表达式有可能在审查工具上匹配, 但是在 GKD 上不匹配</p><p>如果你能确保正则表达式在 Js 和 Java/Kotlin 的匹配行为一致, 那就没问题</p><p>总之两种情况</p><ul><li>高版本 浏览器/nodejs 可以使用 <strong>完整一致</strong> 的正则表达式</li><li>低版本 浏览器/nodejs 使用 <strong>小范围不完整不一致</strong> 的正则表达式</li></ul><p>如果你的选择器没有使用正则表达式则不用关心此问题</p><h3 id="nest-escape" tabindex="-1">嵌套转义字符 <a class="header-anchor" href="#nest-escape" aria-label="Permalink to &quot;嵌套转义字符 {#nest-escape}&quot;">​</a></h3><p>转义字符一般用于表示不能直接显示具体特殊意义的字符</p><p>对于 GKD 的选择器而言, 转义字符使用示例如下</p><div class="language-txt vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">txt</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span>TextView[text=&quot;\\\\\\n&quot;]</span></span></code></pre></div><p>这表示选择一个 TextView, 它的 text 长度为 2, 开头是 <code>\\</code>, 末尾是换行</p><p>这和平常写的代码里面的 string 声明用法一模一样</p><p>但是选择器最终放在规则的字段里面, 也就是这个选择器字符串处于 JSON 的 string 内</p><p>如果你想当然地直接手动复制插入到编辑器里出现下面这种规则</p><div class="language-json5 vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">json5</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">{</span></span>
1
+ import{_ as s,B as n,o as c,c as r,ao as l,j as t,G as a,w as p}from"./chunks/framework.DZAoeHqG.js";const A=JSON.parse('{"title":"选择器","description":"","frontmatter":{},"headers":[],"relativePath":"guide/selector.md","filePath":"guide/selector.md","lastUpdated":1744454038000}'),g={name:"guide/selector.md"},h={class:"vp-raw"},x={tabindex:"0"},u={style:{"text-align":"center"}},y={style:{"text-align":"center"}},k={style:{"text-align":"center"}},b={style:{"text-align":"center"}},m={style:{"text-align":"center"}};function E(v,e,f,_,C,q){const i=n("IdentifierField"),d=n("GImg"),o=n("NImageGroup");return c(),r("div",null,[e[16]||(e[16]=l('<h1 id="title" tabindex="-1">选择器 <a class="header-anchor" href="#title" aria-label="Permalink to &quot;选择器 {#title}&quot;">​</a></h1><p>一个类似 CSS 选择器的选择器, 能联系节点上下文信息, 更容易也更精确找到目标节点</p><h2 id="why" tabindex="-1">为什么需要选择器 <a class="header-anchor" href="#why" aria-label="Permalink to &quot;为什么需要选择器 {#why}&quot;">​</a></h2><p>我们的目的是查询获取屏幕上的某个节点, 最简单的情况下, 目标节点已经有唯一的 id/text 标识</p><p>这时只需要配置 id/text 就能简单执行点击, 但实际情况很多节点都缺失了这些信息, 无法简单查询获取</p><p>节点还存在很多额外信息没有被利用到, 节点所有属性可以在 <a href="https://i.gkd.li/i/14045424" target="_blank" rel="noreferrer">快照-14045424</a> 的属性面板看到</p><p>另一种常见的情况是节点没有有效属性, 这时我们需要根据周围的节点去定位</p><p>选择器就是为了解决这些问题而开发的, 下面是对选择器语法的详细介绍</p><div class="tip custom-block github-alert"><p class="custom-block-title">提示</p><p>本文是从 <strong>抽象语法树(AST)</strong> 的角度去完备地介绍选择器结构<br> 如果你是初学者, 建议你直接看 <a href="./example">选择器-示例</a></p></div><h2 id="syntax" tabindex="-1">语法 <a class="header-anchor" href="#syntax" aria-label="Permalink to &quot;语法 {#syntax}&quot;">​</a></h2><p>与 CSS 类似, 一个选择器由 属性选择器 和 关系选择器 交叉组成, 并且开头末尾必须是 属性选择器</p><p>示例 <code>div &gt; img</code> 的结构是 <code>属性选择器 关系选择器 属性选择器</code>, 它表示选择父节点是 <code>div</code> 的 <code>img</code> 节点, 这与 <a href="https://developer.mozilla.org/zh-CN/docs/Web/CSS/Child_combinator" target="_blank" rel="noreferrer">相同 CSS 语法</a> 语义一致</p><p>另外 属性选择器 和 关系选择器 之间必须<strong>强制</strong>用 空白字符(空格/换行/回车/制表) 隔开</p><p>即 <code>div&gt;img</code> 非法, 必须写成 <code>div &gt; img</code></p><p>下面分别介绍 <a href="#attr">属性选择器</a> 和 <a href="#connect">关系选择器</a></p><h2 id="attr" tabindex="-1">属性选择 <a class="header-anchor" href="#attr" aria-label="Permalink to &quot;属性选择 {#attr}&quot;">​</a></h2><p>它和 CSS 语法的 属性选择器很相似, 但更强大, 如下是一个示例</p><p><code>@TextView[a=1][b^=&#39;2&#39;][c*=&#39;a&#39;||d.length&gt;7&amp;&amp;e=false][!(f=true)][g.plus(1)&gt;0]</code></p><p><code>@</code> 表示选择此节点, 一条规则最后属性选择器 <code>@</code> 生效, 如果没有 <code>@</code>, 取最后一个属性选择器</p><p><code>TextView</code> 代表节点的 name 属性, 而且与 CSS 相似, <code>*</code> 表示匹配任意属性</p><p>由于该选择器主要用于 Android 平台, 节点的 name 都是 java 类如 android.text.TextView 这种形式</p><p>为了方便书写规则, 上述 <code>TextView</code> 等价 <code>[name=&#39;TextView&#39;||name$=&#39;.TextView&#39;]</code></p><p><code>[]</code> 内部是一个 逻辑表达式/布尔表达式/取反表达式</p><ul><li>逻辑表达式 -&gt; <code>name=&#39;TextView&#39;||name$=&#39;.TextView&#39;</code></li><li>布尔表达式 -&gt; <code>name=&#39;TextView&#39;</code></li><li>取反表达式 -&gt; <code>!(name$=&#39;TextView&#39;)</code>, <code>!(name=&#39;TextView&#39;||name$=&#39;.TextView&#39;)</code></li></ul><p>注意 取反表达式中的 <code>!</code> 后面必须是 <code>(...)</code>, <code>!!(...)</code> 是非法的</p><p>逻辑表达式 有两个操作符 <code>||</code> 和 <code>&amp;&amp;</code>. <code>&amp;&amp;</code> 优先级更高</p><p>即 <code>[a&gt;1||b&gt;1&amp;&amp;c&gt;1||d&gt;1]</code> 等价于 <code>[a&gt;1||(b&gt;1&amp;&amp;c&gt;1)||d&gt;1]</code></p><p>并列的 <code>[]</code> 视为使用 <code>&amp;&amp;</code> 的逻辑表达式, 即 <code>[a=1][b=1]</code> 等价于 <code>[a=1&amp;&amp;b=1]</code></p><p>布尔表达式 由 <code>左值 操作符 右值</code> 构成, 左值/右值 是一个 值表达式</p><p>下面分别介绍 <a href="#value-exp">值表达式</a> 和 <a href="#attr-operator">操作符</a></p><h2 id="value-exp" tabindex="-1">值表达式 <a class="header-anchor" href="#value-exp" aria-label="Permalink to &quot;值表达式 {#value-exp}&quot;">​</a></h2><p>值表达式分两类: 变量 和 字面量, 下图是关系表格</p>',32)),e[17]||(e[17]=t("table",null,[t("thead",null,[t("tr",null,[t("th",{colspan:"2"},[t("div",{"text-center":""},"值表达式")]),t("th",null,"示例")])]),t("tbody",null,[t("tr",null,[t("td",{rowspan:"3"},"变量"),t("td",null,"标识符"),t("td",null,[t("code",null," a ")])]),t("tr",null,[t("td",null,"成员表达式"),t("td",null,[t("code",null,"a.b")])]),t("tr",null,[t("td",null,"调用表达式"),t("td",null,[t("code",null,"a(b,c)")])]),t("tr",null,[t("td",{rowspan:"4"},"字面量"),t("td",null,"null"),t("td",null,[t("code",null,"null")])]),t("tr",null,[t("td",null,"boolean"),t("td",null,[t("code",null,"false")])]),t("tr",null,[t("td",null,"int"),t("td",null,[t("code",null,"114514")])]),t("tr",null,[t("td",null,"string"),t("td",null,[t("code",null,"'ikun'")])])])],-1)),e[18]||(e[18]=l('<p>每个值表达式都有其类型, 可细分为两类</p><p>基础类型: <code>null</code> <code>boolean</code> <code>int</code> <code>string</code></p><p>对象类型: <code>object</code></p><p>对象类型可细分为 <code>context</code> 和 <code>node</code> 两种类型</p><p>比如选择器 <code>[parent=null]</code> 代表选择一个父节点是 null 的节点, 即根节点</p><p>上面的 <code>parent</code> 属于 值表达式/变量/标识符, 类型是 <code>node</code></p><p><code>context</code> 类型指代当前节点的上下文, 当想使用不属于 <code>node</code> 上的属性方法时就需要 <code>context</code></p><p>你可以在 <a href="./node">选择器-属性方法</a> 查看所有类型和对应属性方法</p><h3 id="var" tabindex="-1">变量 <a class="header-anchor" href="#var" aria-label="Permalink to &quot;变量 {#var}&quot;">​</a></h3><p>首先需要了解 <strong>标识符</strong>: 正则匹配 <code>^[_a-zA-Z][a-zA-Z0-9_]*$</code> 并且不是 <code>null</code>/<code>true</code>/<code>false</code> 的字符串</p><p>示例合法变量名: <code>a</code> <code>ikun</code> <code>manbaout</code>, 下面可输入字符测试否是合法</p>',11)),t("div",h,[a(i)]),e[19]||(e[19]=l('<hr><p>接下来了解 <strong>成员表达式</strong> <code>a.b</code>, 它被 <code>.</code> 分为两个部分, 前部分是另一个变量, 后部分是一个标识符作为属性</p><p>同理 <code>a.b.c</code> 也是一个 成员表达式, 其中 <code>a.b</code> 是它的变量部分, <code>c</code> 是一个合法的标识符作为属性</p><p>根据上面标识符的规则, <code>a.1</code>, <code>a.null</code>, <code>a.true</code> 都是非法成员表达式</p><hr><p>最后了解 <strong>调用表达式</strong> <code>a(b,c)</code>, 它由两个部分构成, <code>(</code> 的左侧 <code>a</code> 是一个变量作为 调用者</p><p><code>(b,c)</code> 作为调用参数(值类型), 调用参数数量可以是 0 或任意个, 即 <code>a()</code> 也是合法的</p><p>合法的其它例子: <code>a.b(c,d).e(f).g(1,2,true)</code></p><p>需要注意调用者不能是 调用表达式, 即 <code>a()()</code> 非法</p><h3 id="literal" tabindex="-1">字面量 <a class="header-anchor" href="#literal" aria-label="Permalink to &quot;字面量 {#literal}&quot;">​</a></h3><p>根据上面的表格, 字面量有 4 种: <code>null</code>, <code>boolean</code>, <code>int</code>, <code>string</code></p><ul><li>null</li><li>boolean 使用 <code>true</code>/<code>false</code></li><li>int 匹配 <code>^-?[0-9]$</code>, 即十进制整数, 示例 <code>-1</code>,<code>0</code>,<code>1</code>, 不支持 <code>+1</code> 这种写法</li><li>string 使用 &#39; ` &quot; 之一成对包裹, 内部字符转义使用 <code>\\</code><br> 所有的转义字符示例 <code>\\\\</code>, <code>\\&#39;</code>, <code>\\&quot;</code>, <code>\\`</code>, <code>\\n</code>, <code>\\r</code>, <code>\\t</code>, <code>\\b</code>, <code>\\xfF</code>, <code>\\uffFF</code><br> 不支持多行字符, 处于 <code>[0, 0x1F]</code> 的控制字符必须使用转义字符表示</li></ul><p>此外使用 string 时需要了解 <a href="#nest-escape">嵌套转义字符</a> 以避免出现错误</p><h2 id="attr-operator" tabindex="-1">操作符 <a class="header-anchor" href="#attr-operator" aria-label="Permalink to &quot;操作符 {#attr-operator}&quot;">​</a></h2><p>操作符 用于连接两个 值表达式</p><table tabindex="0"><thead><tr><th style="text-align:center;">操作符</th><th style="text-align:center;">名称</th><th style="text-align:center;">说明</th></tr></thead><tbody><tr><td style="text-align:center;">=</td><td style="text-align:center;">equal</td><td style="text-align:center;">等于</td></tr><tr><td style="text-align:center;">!=</td><td style="text-align:center;">notEqual</td><td style="text-align:center;">不等于</td></tr><tr><td style="text-align:center;">&gt;</td><td style="text-align:center;">more</td><td style="text-align:center;">大于</td></tr><tr><td style="text-align:center;">&gt;=</td><td style="text-align:center;">moreEqual</td><td style="text-align:center;">大于或等于</td></tr><tr><td style="text-align:center;">&lt;</td><td style="text-align:center;">less</td><td style="text-align:center;">小于</td></tr><tr><td style="text-align:center;">&lt;=</td><td style="text-align:center;">lessEqual</td><td style="text-align:center;">小于或等于</td></tr><tr><td style="text-align:center;">^=</td><td style="text-align:center;">startsWith</td><td style="text-align:center;">以...开头</td></tr><tr><td style="text-align:center;">!^=</td><td style="text-align:center;">notStartsWith</td><td style="text-align:center;">不以...开头</td></tr><tr><td style="text-align:center;">*=</td><td style="text-align:center;">contains</td><td style="text-align:center;">包含...</td></tr><tr><td style="text-align:center;">!*=</td><td style="text-align:center;">notContains</td><td style="text-align:center;">不包含...</td></tr><tr><td style="text-align:center;">$=</td><td style="text-align:center;">endsWith</td><td style="text-align:center;">以...结尾</td></tr><tr><td style="text-align:center;">!$=</td><td style="text-align:center;">notEndsWith</td><td style="text-align:center;">不以...结尾</td></tr><tr><td style="text-align:center;">~=</td><td style="text-align:center;">matches</td><td style="text-align:center;">正则匹配...</td></tr><tr><td style="text-align:center;">!~=</td><td style="text-align:center;">notMatches</td><td style="text-align:center;">正则不匹配...</td></tr></tbody></table><p>附加说明: <code>matches</code>/<code>notMatches</code> 要求 值 必须是合法的 <a href="https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html" target="_blank" rel="noreferrer">Java/Kotlin 正则表达式</a>, 否则提示语法错误</p><p>并且 <code>matches</code>/<code>notMatches</code> 右侧的值只能是 字面量/字符串</p><p>操作符只能使用在对应的类型的值, 比如 <code>a&gt;&#39;&#39;</code> 类型不匹配, 将提示 <code>非法类型</code></p><p>下面表格中 <code>-</code> 表示类型不匹配</p><table tabindex="0"><thead><tr><th style="text-align:center;"></th><th style="text-align:center;">null</th><th style="text-align:center;">boolean</th><th style="text-align:center;">int</th><th style="text-align:center;">string</th></tr></thead><tbody><tr><td style="text-align:center;">=</td><td style="text-align:center;">✔</td><td style="text-align:center;">✔</td><td style="text-align:center;">✔</td><td style="text-align:center;">✔</td></tr><tr><td style="text-align:center;">!=</td><td style="text-align:center;">✔</td><td style="text-align:center;">✔</td><td style="text-align:center;">✔</td><td style="text-align:center;">✔</td></tr><tr><td style="text-align:center;">&gt;</td><td style="text-align:center;">-</td><td style="text-align:center;">-</td><td style="text-align:center;">✔</td><td style="text-align:center;">-</td></tr><tr><td style="text-align:center;">&gt;=</td><td style="text-align:center;">-</td><td style="text-align:center;">-</td><td style="text-align:center;">✔</td><td style="text-align:center;">-</td></tr><tr><td style="text-align:center;">&lt;</td><td style="text-align:center;">-</td><td style="text-align:center;">-</td><td style="text-align:center;">✔</td><td style="text-align:center;">-</td></tr><tr><td style="text-align:center;">&lt;=</td><td style="text-align:center;">-</td><td style="text-align:center;">-</td><td style="text-align:center;">✔</td><td style="text-align:center;">-</td></tr><tr><td style="text-align:center;">^=</td><td style="text-align:center;">-</td><td style="text-align:center;">-</td><td style="text-align:center;">-</td><td style="text-align:center;">✔</td></tr><tr><td style="text-align:center;">!^=</td><td style="text-align:center;">-</td><td style="text-align:center;">-</td><td style="text-align:center;">-</td><td style="text-align:center;">✔</td></tr><tr><td style="text-align:center;">*=</td><td style="text-align:center;">-</td><td style="text-align:center;">-</td><td style="text-align:center;">-</td><td style="text-align:center;">✔</td></tr><tr><td style="text-align:center;">!*=</td><td style="text-align:center;">-</td><td style="text-align:center;">-</td><td style="text-align:center;">-</td><td style="text-align:center;">✔</td></tr><tr><td style="text-align:center;">$=</td><td style="text-align:center;">-</td><td style="text-align:center;">-</td><td style="text-align:center;">-</td><td style="text-align:center;">✔</td></tr><tr><td style="text-align:center;">!$=</td><td style="text-align:center;">-</td><td style="text-align:center;">-</td><td style="text-align:center;">-</td><td style="text-align:center;">✔</td></tr><tr><td style="text-align:center;">~=</td><td style="text-align:center;">-</td><td style="text-align:center;">-</td><td style="text-align:center;">-</td><td style="text-align:center;">✔</td></tr><tr><td style="text-align:center;">!~=</td><td style="text-align:center;">-</td><td style="text-align:center;">-</td><td style="text-align:center;">-</td><td style="text-align:center;">✔</td></tr></tbody></table><p>除 <code>=</code>/<code>!=</code> 以外的操作符, 当节点属性是 null 时表达式为 <code>false</code></p><ul><li>a &gt; 233</li><li>a &gt;= 233</li><li>a &lt; 233</li><li>a &lt;= 233</li><li>a ^= &#39;xxx&#39;</li><li>a !^= &#39;xxx&#39;</li><li>a *= &#39;xxx&#39;</li><li>a !* &#39;xxx&#39;</li><li>a $= &#39;xxx&#39;</li><li>a !$= &#39;xxx&#39;</li><li>a ~= &#39;xxx&#39;</li><li>a !~= &#39;xxx&#39;</li></ul><p>即当 a 是 <code>null</code> 时以上表达式为 <code>false</code></p><p>你可能会对 <code>a !$= &#39;xxx&#39;</code> 在 a 是 <code>null</code> 表达式为 <code>false</code> 感到奇怪</p><p>那你可以换一种写法使用 <code>a=null || a!$=&#39;xxx&#39;</code> 或 <code>!(a$=&#39;xxx&#39;)</code></p><h2 id="connect" tabindex="-1">关系选择 <a class="header-anchor" href="#connect" aria-label="Permalink to &quot;关系选择 {#connect}&quot;">​</a></h2><p>关系选择器 由 关系操作符 和 关系表达式 构成, 用于连接两个属性选择器</p><p>简单示例: <code>div &gt; a</code>, 它 表示/约束 两个节点之间的关系, 下面介绍 <a href="#connect-operator">关系操作符</a> 和 <a href="#connect-exp">关系表达式</a></p><h3 id="connect-exp" tabindex="-1">关系表达式 <a class="header-anchor" href="#connect-exp" aria-label="Permalink to &quot;关系表达式 {#connect-exp}&quot;">​</a></h3><p>关系表达式 表示查找节点的范围, 有两种</p><ul><li>元组表达式 <code>(a1,a2,a3,a_n)</code>, 其中 a1, a2, a3, a_n 是常量有序递增正整数, 示例 <code>(1)</code>, <code>(2,3,5)</code></li><li>多项式表达式 <code>(an+b)</code>, 其中 a 和 b 是常量整数, 它是元组表达式的另一种表示, 这个元组的数满足集合 <code>{an+b|an+b&gt;=1,n&gt;=1}</code> 如果集合为空集则表达式非法<br> 当 a&lt;=0 时, 它具有等价的元组表达式<br> 示例 <code>(-n+4)</code> 等价于 <code>(1,2,3)</code><br> 示例 <code>(-3n+10)</code> 等价于 <code>(1,4,7)</code><br> 当 a&gt;0 时, 它表示无限的元组表达式<br> 示例 <code>(n)</code>, 它表示 <code>(1,2,3,...)</code> 一个无限的元组<br> 示例 <code>(2n-1)</code>, 它表示 <code>(1,3,5,...)</code> 一个无限的元组</li></ul><h3 id="connect-operator" tabindex="-1">关系操作符 <a class="header-anchor" href="#connect-operator" aria-label="Permalink to &quot;关系操作符 {#connect-operator}&quot;">​</a></h3><p>关系操作符 表示查找节点的方向, 有 6 种关系操作符</p><ul><li>前 5 种为 <code>+</code>, <code>-</code>, <code>&gt;</code>, <code>&lt;</code>, <code>&lt;&lt;</code>, 表示节点的相对位置</li></ul>',35)),a(o,null,{default:p(()=>[t("table",x,[e[15]||(e[15]=t("thead",null,[t("tr",null,[t("th",{style:{"text-align":"center"}},"操作符"),t("th",{style:{"text-align":"center"}},"名称"),t("th",{style:{"text-align":"center"}},"图例"),t("th",{style:{"text-align":"center"}},"选择器")])],-1)),t("tbody",null,[t("tr",null,[e[0]||(e[0]=t("td",{style:{"text-align":"center"}},"+",-1)),e[1]||(e[1]=t("td",{style:{"text-align":"center"}},"前置兄弟节点",-1)),t("td",u,[a(d,{src:"0020.png",width:"250"})]),e[2]||(e[2]=t("td",{style:{"text-align":"center"}},[t("a",{href:"https://i.gkd.li/i/14045424?gkd=KiArIFtfaWQ9MzNd",target:"_blank",rel:"noreferrer"},"* + [_id=33]")],-1))]),t("tr",null,[e[3]||(e[3]=t("td",{style:{"text-align":"center"}},"-",-1)),e[4]||(e[4]=t("td",{style:{"text-align":"center"}},"后置兄弟节点",-1)),t("td",y,[a(d,{src:"0021.png",width:"250"})]),e[5]||(e[5]=t("td",{style:{"text-align":"center"}},[t("a",{href:"https://i.gkd.li/i/14045424?gkd=KiAtIFtfaWQ9MzJd",target:"_blank",rel:"noreferrer"},"* - [_id=32]")],-1))]),t("tr",null,[e[6]||(e[6]=t("td",{style:{"text-align":"center"}},">",-1)),e[7]||(e[7]=t("td",{style:{"text-align":"center"}},"祖先节点",-1)),t("td",k,[a(d,{src:"0022.png",width:"250"})]),e[8]||(e[8]=t("td",{style:{"text-align":"center"}},[t("a",{href:"https://i.gkd.li/i/14045424?gkd=KiA-IFtfaWQ9OTBd",target:"_blank",rel:"noreferrer"},"* > [_id=90]")],-1))]),t("tr",null,[e[9]||(e[9]=t("td",{style:{"text-align":"center"}},"<",-1)),e[10]||(e[10]=t("td",{style:{"text-align":"center"}},"直接子节点",-1)),t("td",b,[a(d,{src:"0023.png",width:"250"})]),e[11]||(e[11]=t("td",{style:{"text-align":"center"}},[t("a",{href:"https://i.gkd.li/i/14045424?gkd=KiA8IFtfaWQ9ODld",target:"_blank",rel:"noreferrer"},"* < [_id=89]")],-1))]),t("tr",null,[e[12]||(e[12]=t("td",{style:{"text-align":"center"}},"<<",-1)),e[13]||(e[13]=t("td",{style:{"text-align":"center"}},"子孙节点(深度先序)",-1)),t("td",m,[a(d,{src:"0024.png",width:"250"})]),e[14]||(e[14]=t("td",{style:{"text-align":"center"}},[t("a",{href:"https://i.gkd.li/i/14045424?gkd=KiA8PDIgW19pZD0yOV0",target:"_blank",rel:"noreferrer"},"* <<2 [_id=29]")],-1))])])])]),_:1}),e[20]||(e[20]=l('<ul><li>另外一种为 <code>-&gt;</code>, 用于查询过程中, 表示当前节点的之前节点</li></ul><p>比如 <code>C -&gt;1 B &gt; A</code>, 由 A 找到 B 后, 此时的 C 就是 B 的之前节点即 A, 这适合想快速去之前节点重新查询关系的场景</p><p>将 关系操作符 和 关系表达式 连接起来就得到了 关系选择器</p><p><code>A +(a1,a2,a3,a_n) B</code> : A 是 B 的前置兄弟节点, 并且 A.index 满足 B.index-(a_m), 其中 a_m 是元组的任意一个数字</p><p><code>A -(a1,a2,a3,a_n) B</code> : A 是 B 的后置兄弟节点, 并且 A.index 满足 B.index+(a_m)</p><p><code>A &gt;(a1,a2,a3,a_n) B</code> : A 是 B 的祖先节点, 并且 A.depth 满足 B.depth-(a_m), 根节点的 depth=0</p><p><code>A &lt;(a1,a2,a3,a_n) B</code> : A 是 B 的直接子节点, 并且 A.index 满足 a_m-1</p><p><code>A &lt;&lt;(a1,a2,a3,a_n) B</code> : A 是 B 的子孙节点, 并且 A.order 满足 a_m-1, A.order 是深度优先先序遍历的索引</p><p><code>N -&gt;(a1,a2,a3,a_n) B &gt; A</code>, N 是 B 的之前(右侧)的已查询节点, 并且 N.index 满足 B.index-(a_m), index 顺序为 (n,n-1,...,2,1,0)</p><h3 id="connect-shortcut" tabindex="-1">表达式简写 <a class="header-anchor" href="#connect-shortcut" aria-label="Permalink to &quot;表达式简写 {#connect-shortcut}&quot;">​</a></h3><p>一般情况下, 并不需要写严格完整的表达式, 使用简化写法更方便快捷</p><p>下面是一些特殊情况下的简写表示</p><p>当 a=0 或 b=0 时, 括号可以省略, 以 <code>+</code> 为例</p><ul><li><code>A +(3n+0) B</code> -&gt; <code>A +(3n) B</code> -&gt; <code>A +3n B</code></li><li><code>A +(0n+3) B</code> -&gt; <code>A +(+3) B</code> -&gt; <code>A +3 B</code></li></ul><p>当 a=0 且 b=1 时, an+b 可以省略, 以 <code>+</code> 为例</p><ul><li><code>A +(0n+1) B</code> -&gt; <code>A + B</code></li></ul><p>此外 <code>A + B</code>,<code>A &gt; B</code> 都与等价的 CSS 语法语义相同</p><p>当 a=1 且 b=0 且操作符是 <code>&gt;</code>, 可以进一步简写, 比如</p><ul><li><code>A &gt;(1n+0) B</code> -&gt; <code>A &gt;n B</code> -&gt; <code>A B</code></li></ul><p>这与等价的 CSS 语法语义相同</p><h2 id="advanced" tabindex="-1">进阶 <a class="header-anchor" href="#advanced" aria-label="Permalink to &quot;进阶 {#advanced}&quot;">​</a></h2><p>正如上述的 <a href="#attr">属性选择</a> 一样, 选择器也可以组合为 逻辑表达式/取反表达式</p><p>只有一个格式要求: 单个选择器必须强制使用 <code>()</code> 包裹</p><ul><li><p>一般选择器 -&gt; <code>A + B</code> 或 <code>(A + B)</code></p></li><li><p>逻辑或表达式 -&gt; <code>(A + B) || (M &gt; N)</code>, 表示匹配节点满足 <code>A + B</code> 或 <code>M &gt; N</code></p><p>匹配结果遵循 <code>||</code> 的短路原则, 优先判断左侧的 <code>A + B</code>, 如果满足 <code>A + B</code> 那就是 <code>B</code>. 不满足 <code>A + B</code> 时, 判断右侧表达式, 如果满足 <code>M &gt; N</code> 那就是 <code>N</code>, 当然也可以在内部使用 <code>@</code> 自定义目标节点</p></li><li><p>逻辑且表达式 -&gt; <code>(A + B) &amp;&amp; (M &gt; N)</code>, 表示匹配节点满足 <code>A + B</code> 和 <code>M &gt; N</code>, 结果取右侧表达式结果, 结果为 <code>N</code>, 当然也可以在内部使用 <code>@</code> 自定义目标节点</p></li><li><p>取反表达式 -&gt; <code>!(A + B)</code>, 表示匹配节点应不满足 <code>A + B</code>, 需要注意的是 <code>!(@A + B)</code> 中的 <code>@</code> 是无效的, 它的匹配结果只能是末尾节点 <code>B</code></p></li></ul><p>此外 逻辑且表达式 的优先级更高, 即 <code>(A + B) || (C + B) &amp;&amp; (M &gt; N)</code> 等价于 <code>(A + B) || ((C + B) &amp;&amp; (M &gt; N))</code></p><h2 id="example" tabindex="-1">示例 <a class="header-anchor" href="#example" aria-label="Permalink to &quot;示例 {#example}&quot;">​</a></h2><div class="language-txt vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">txt</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span>@LinearLayout &gt; TextView[id=`com.byted.pangle:id/tt_item_tv`][text=`不感兴趣`]</span></span></code></pre></div><p>选择器从末尾开始查找, 首先找到 id=`com.byted.pangle:id/tt_item_tv` 和 text=`不感兴趣` 的 TextView, 并且父节点是 LinearLayout 的节点</p><p>此时我们得到两个节点 [LinearLayout, TextView] 根据 <code>@</code> 知道目标节点是 LinearLayout</p><p>实际上它与</p><div class="language-txt vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">txt</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span>TextView[id=`com.byted.pangle:id/tt_item_tv`][text=`不感兴趣`] &lt;n LinearLayout</span></span></code></pre></div><p>的目标匹配节点是等价的, 但是在查询算法时间复杂度上, 后者更慢</p><p>如下是网页无障碍快照审查工具, 使用它的搜索框的选择器查询可以实时测试编写的选择器</p><ul><li><a href="https://i.gkd.li/i/14045424" target="_blank" rel="noreferrer">快照-14045424</a></li><li><a href="https://i.gkd.li/i/14034770" target="_blank" rel="noreferrer">快照-14034770</a></li><li><a href="https://i.gkd.li/i/14031920" target="_blank" rel="noreferrer">快照-14031920</a></li><li><a href="https://i.gkd.li/i/14018243" target="_blank" rel="noreferrer">快照-14018243</a></li><li><a href="https://i.gkd.li/i/14011298" target="_blank" rel="noreferrer">快照-14011298</a></li><li><a href="https://i.gkd.li/i/13999908" target="_blank" rel="noreferrer">快照-13999908</a></li></ul><h2 id="note" tabindex="-1">一些注意 <a class="header-anchor" href="#note" aria-label="Permalink to &quot;一些注意 {#note}&quot;">​</a></h2><h3 id="match-order" tabindex="-1">匹配顺序 <a class="header-anchor" href="#match-order" aria-label="Permalink to &quot;匹配顺序 {#match-order}&quot;">​</a></h3><p>选择器的匹配顺序是 <strong>从右往左匹配</strong></p><p>例如 <code>FrameLayout &gt; TextView</code>, 它是先从 根节点/事件节点 找到 <code>TextView</code>, 然后再判断 parent 是不是 <code>FrameLayout</code></p><p>并不是先找到 <code>FrameLayout</code> 然后再判断 child 是否有 <code>TextView</code></p><p>同时在 <a href="https://i.gkd.li/i/15747758?gkd=RnJhbWVMYXlvdXQgPiBUZXh0Vmlldw" target="_blank" rel="noreferrer">选择器路径视图</a> 也会标明匹配顺序和查找方向</p><h3 id="regex-optimization" tabindex="-1">正则表达式优化 <a class="header-anchor" href="#regex-optimization" aria-label="Permalink to &quot;正则表达式优化 {#regex-optimization}&quot;">​</a></h3><p>对 <code>matches</code>/<code>notMatches</code> 的优化: 如果正则表达式满足下面的条件, 选择器将使用内置的简单的函数匹配, 而不是真正地去运行一个正则表达式</p><ul><li><code>[text~=&quot;(?is)abc.*&quot;]</code> -&gt; <code>startsWith(&#39;abc&#39;, ignoreCase = true)</code></li><li><code>[text~=&quot;(?is).*abc.*&quot;]</code> -&gt; <code>contains(&#39;abc&#39;, ignoreCase = true)</code></li><li><code>[text~=&quot;(?is).*abc&quot;]</code> -&gt; <code>endsWith(&#39;abc&#39;, ignoreCase = true)</code></li><li><code>[text!~=&quot;(?is)abc.*&quot;]</code> -&gt; <code>notStartsWith(&#39;abc&#39;, ignoreCase = true)</code></li><li><code>[text!~=&quot;(?is).*abc.*&quot;]</code> -&gt; <code>notContains(&#39;abc&#39;, ignoreCase = true)</code></li><li><code>[text!~=&quot;(?is).*abc&quot;]</code> -&gt; <code>notEndsWith(&#39;abc&#39;, ignoreCase = true)</code></li></ul><p>上面的 <code>abc</code> 指代不包含 <code>\\^$.?*|+()[]{}</code> 这类特殊正则字符的任意字符串, 如 <code>ikun</code> 符合, <code>ikun?</code> 不符合, <code>ignoreCase = true</code> 表示忽略大小写</p><p>简单来说就是如果你只想忽略大小写去简单匹配或不匹配一些字符串, 那么直接使用上面的格式</p><h3 id="regex-multiplatform" tabindex="-1">正则表达式一致性 <a class="header-anchor" href="#regex-multiplatform" aria-label="Permalink to &quot;正则表达式一致性 {#regex-multiplatform}&quot;">​</a></h3><p>由于 选择器 需要同时满足 浏览器/Js(审查工具), Android/Java(GKD) 运行, 而这两个平台的正则表达式的底层实现和语法表示略有不同</p><p>因此为了在 Js 端实现和 Java 一致的正则表达式规范, 网页审查工具借助 <a href="https://kotlinlang.org/docs/wasm-overview.html" target="_blank" rel="noreferrer">Kotlin Wasm</a> 将正则表达式的 matches 函数接口编译为 wasm 提供给 Js 调用</p><p>Kotlin Wasm 需要你的浏览器支持 <a href="https://developer.chrome.com/blog/wasmgc?hl=zh-cn" target="_blank" rel="noreferrer">WasmGC</a>, 也就是版本需要满足下列条件</p>',49)),a(d,{src:"0025.png"}),e[21]||(e[21]=l(`<p>如果你在 nodejs 使用 gkd 选择器, 需要 node&gt;=22 版本</p><p>如果你的 浏览器/nodejs 版本不满足, 正则表达式将自动回退到 Js 端实现, 以下是在 Js 端使用正则表达式需要注意的地方</p><p>比如上面的正则表达式优化例子中开头的 <code>(?is)</code> 是 Java 正则表达式的 inline flags 语法, 但实际上 Js 并不支持这样写, 只是选择器内部做了一些兼容让它支持</p><p>并且选择器的 Js 端只兼容在开头的 flags, 在内部的 flags 不支持, 此外 Java 和 Js 支持的 flags 也有不同, 某些特殊的表达式表现也不一致</p><p>总之不要使用太过复杂(多复杂我也不知道)的正则表达式, 某些正则表达式有可能在审查工具上匹配, 但是在 GKD 上不匹配</p><p>如果你能确保正则表达式在 Js 和 Java/Kotlin 的匹配行为一致, 那就没问题</p><p>总之两种情况</p><ul><li>高版本 浏览器/nodejs 可以使用 <strong>完整一致</strong> 的正则表达式</li><li>低版本 浏览器/nodejs 使用 <strong>小范围不完整不一致</strong> 的正则表达式</li></ul><p>如果你的选择器没有使用正则表达式则不用关心此问题</p><h3 id="nest-escape" tabindex="-1">嵌套转义字符 <a class="header-anchor" href="#nest-escape" aria-label="Permalink to &quot;嵌套转义字符 {#nest-escape}&quot;">​</a></h3><p>转义字符一般用于表示不能直接显示具体特殊意义的字符</p><p>对于 GKD 的选择器而言, 转义字符使用示例如下</p><div class="language-txt vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">txt</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span>TextView[text=&quot;\\\\\\n&quot;]</span></span></code></pre></div><p>这表示选择一个 TextView, 它的 text 长度为 2, 开头是 <code>\\</code>, 末尾是换行</p><p>这和平常写的代码里面的 string 声明用法一模一样</p><p>但是选择器最终放在规则的字段里面, 也就是这个选择器字符串处于 JSON 的 string 内</p><p>如果你想当然地直接手动复制插入到编辑器里出现下面这种规则</p><div class="language-json5 vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">json5</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">{</span></span>
2
2
  <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> matches</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;TextView[text=&quot;</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">\\\\\\n</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;]&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
3
3
  <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span></code></pre></div><p>由于 JSON 也有自己的转义, 规则与 GKD 的转义基本一致, 因此你的实际选择器实际变成了</p><div class="language-txt vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">txt</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span>TextView[text=&quot;\\</span></span>
4
4
  <span class="line"><span>&quot;]</span></span></code></pre></div><p>这明显是一个非法的选择器</p><p>解决方式也很简单, 手动插入的时候将你的 <code>\\</code> 变成 <code>\\\\</code>, 正确的规则如下</p><div class="language-json5 vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">json5</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">{</span></span>
@@ -1 +1 @@
1
- import{_ as s,B as n,o as c,c as r,ao as l,j as t,G as a,w as p}from"./chunks/framework.DZAoeHqG.js";const A=JSON.parse('{"title":"选择器","description":"","frontmatter":{},"headers":[],"relativePath":"guide/selector.md","filePath":"guide/selector.md","lastUpdated":1744453064000}'),g={name:"guide/selector.md"},h={class:"vp-raw"},x={tabindex:"0"},u={style:{"text-align":"center"}},y={style:{"text-align":"center"}},k={style:{"text-align":"center"}},b={style:{"text-align":"center"}},m={style:{"text-align":"center"}};function E(v,e,f,_,C,q){const i=n("IdentifierField"),d=n("GImg"),o=n("NImageGroup");return c(),r("div",null,[e[16]||(e[16]=l("",32)),e[17]||(e[17]=t("table",null,[t("thead",null,[t("tr",null,[t("th",{colspan:"2"},[t("div",{"text-center":""},"值表达式")]),t("th",null,"示例")])]),t("tbody",null,[t("tr",null,[t("td",{rowspan:"3"},"变量"),t("td",null,"标识符"),t("td",null,[t("code",null," a ")])]),t("tr",null,[t("td",null,"成员表达式"),t("td",null,[t("code",null,"a.b")])]),t("tr",null,[t("td",null,"调用表达式"),t("td",null,[t("code",null,"a(b,c)")])]),t("tr",null,[t("td",{rowspan:"4"},"字面量"),t("td",null,"null"),t("td",null,[t("code",null,"null")])]),t("tr",null,[t("td",null,"boolean"),t("td",null,[t("code",null,"false")])]),t("tr",null,[t("td",null,"int"),t("td",null,[t("code",null,"114514")])]),t("tr",null,[t("td",null,"string"),t("td",null,[t("code",null,"'ikun'")])])])],-1)),e[18]||(e[18]=l("",11)),t("div",h,[a(i)]),e[19]||(e[19]=l("",35)),a(o,null,{default:p(()=>[t("table",x,[e[15]||(e[15]=t("thead",null,[t("tr",null,[t("th",{style:{"text-align":"center"}},"操作符"),t("th",{style:{"text-align":"center"}},"名称"),t("th",{style:{"text-align":"center"}},"图例"),t("th",{style:{"text-align":"center"}},"选择器")])],-1)),t("tbody",null,[t("tr",null,[e[0]||(e[0]=t("td",{style:{"text-align":"center"}},"+",-1)),e[1]||(e[1]=t("td",{style:{"text-align":"center"}},"前置兄弟节点",-1)),t("td",u,[a(d,{src:"0020.png",width:"250"})]),e[2]||(e[2]=t("td",{style:{"text-align":"center"}},[t("a",{href:"https://i.gkd.li/i/14045424?gkd=KiArIFtfaWQ9MzNd",target:"_blank",rel:"noreferrer"},"* + [_id=33]")],-1))]),t("tr",null,[e[3]||(e[3]=t("td",{style:{"text-align":"center"}},"-",-1)),e[4]||(e[4]=t("td",{style:{"text-align":"center"}},"后置兄弟节点",-1)),t("td",y,[a(d,{src:"0021.png",width:"250"})]),e[5]||(e[5]=t("td",{style:{"text-align":"center"}},[t("a",{href:"https://i.gkd.li/i/14045424?gkd=KiAtIFtfaWQ9MzJd",target:"_blank",rel:"noreferrer"},"* - [_id=32]")],-1))]),t("tr",null,[e[6]||(e[6]=t("td",{style:{"text-align":"center"}},">",-1)),e[7]||(e[7]=t("td",{style:{"text-align":"center"}},"祖先节点",-1)),t("td",k,[a(d,{src:"0022.png",width:"250"})]),e[8]||(e[8]=t("td",{style:{"text-align":"center"}},[t("a",{href:"https://i.gkd.li/i/14045424?gkd=KiA-IFtfaWQ9OTBd",target:"_blank",rel:"noreferrer"},"* > [_id=90]")],-1))]),t("tr",null,[e[9]||(e[9]=t("td",{style:{"text-align":"center"}},"<",-1)),e[10]||(e[10]=t("td",{style:{"text-align":"center"}},"直接子节点",-1)),t("td",b,[a(d,{src:"0023.png",width:"250"})]),e[11]||(e[11]=t("td",{style:{"text-align":"center"}},[t("a",{href:"https://i.gkd.li/i/14045424?gkd=KiA8IFtfaWQ9ODld",target:"_blank",rel:"noreferrer"},"* < [_id=89]")],-1))]),t("tr",null,[e[12]||(e[12]=t("td",{style:{"text-align":"center"}},"<<",-1)),e[13]||(e[13]=t("td",{style:{"text-align":"center"}},"子孙节点(深度先序)",-1)),t("td",m,[a(d,{src:"0024.png",width:"250"})]),e[14]||(e[14]=t("td",{style:{"text-align":"center"}},[t("a",{href:"https://i.gkd.li/i/14045424?gkd=KiA8PDIgW19pZD0yOV0",target:"_blank",rel:"noreferrer"},"* <<2 [_id=29]")],-1))])])])]),_:1}),e[20]||(e[20]=l("",49)),a(d,{src:"0025.png"}),e[21]||(e[21]=l("",37))])}const F=s(g,[["render",E]]);export{A as __pageData,F as default};
1
+ import{_ as s,B as n,o as c,c as r,ao as l,j as t,G as a,w as p}from"./chunks/framework.DZAoeHqG.js";const A=JSON.parse('{"title":"选择器","description":"","frontmatter":{},"headers":[],"relativePath":"guide/selector.md","filePath":"guide/selector.md","lastUpdated":1744454038000}'),g={name:"guide/selector.md"},h={class:"vp-raw"},x={tabindex:"0"},u={style:{"text-align":"center"}},y={style:{"text-align":"center"}},k={style:{"text-align":"center"}},b={style:{"text-align":"center"}},m={style:{"text-align":"center"}};function E(v,e,f,_,C,q){const i=n("IdentifierField"),d=n("GImg"),o=n("NImageGroup");return c(),r("div",null,[e[16]||(e[16]=l("",32)),e[17]||(e[17]=t("table",null,[t("thead",null,[t("tr",null,[t("th",{colspan:"2"},[t("div",{"text-center":""},"值表达式")]),t("th",null,"示例")])]),t("tbody",null,[t("tr",null,[t("td",{rowspan:"3"},"变量"),t("td",null,"标识符"),t("td",null,[t("code",null," a ")])]),t("tr",null,[t("td",null,"成员表达式"),t("td",null,[t("code",null,"a.b")])]),t("tr",null,[t("td",null,"调用表达式"),t("td",null,[t("code",null,"a(b,c)")])]),t("tr",null,[t("td",{rowspan:"4"},"字面量"),t("td",null,"null"),t("td",null,[t("code",null,"null")])]),t("tr",null,[t("td",null,"boolean"),t("td",null,[t("code",null,"false")])]),t("tr",null,[t("td",null,"int"),t("td",null,[t("code",null,"114514")])]),t("tr",null,[t("td",null,"string"),t("td",null,[t("code",null,"'ikun'")])])])],-1)),e[18]||(e[18]=l("",11)),t("div",h,[a(i)]),e[19]||(e[19]=l("",35)),a(o,null,{default:p(()=>[t("table",x,[e[15]||(e[15]=t("thead",null,[t("tr",null,[t("th",{style:{"text-align":"center"}},"操作符"),t("th",{style:{"text-align":"center"}},"名称"),t("th",{style:{"text-align":"center"}},"图例"),t("th",{style:{"text-align":"center"}},"选择器")])],-1)),t("tbody",null,[t("tr",null,[e[0]||(e[0]=t("td",{style:{"text-align":"center"}},"+",-1)),e[1]||(e[1]=t("td",{style:{"text-align":"center"}},"前置兄弟节点",-1)),t("td",u,[a(d,{src:"0020.png",width:"250"})]),e[2]||(e[2]=t("td",{style:{"text-align":"center"}},[t("a",{href:"https://i.gkd.li/i/14045424?gkd=KiArIFtfaWQ9MzNd",target:"_blank",rel:"noreferrer"},"* + [_id=33]")],-1))]),t("tr",null,[e[3]||(e[3]=t("td",{style:{"text-align":"center"}},"-",-1)),e[4]||(e[4]=t("td",{style:{"text-align":"center"}},"后置兄弟节点",-1)),t("td",y,[a(d,{src:"0021.png",width:"250"})]),e[5]||(e[5]=t("td",{style:{"text-align":"center"}},[t("a",{href:"https://i.gkd.li/i/14045424?gkd=KiAtIFtfaWQ9MzJd",target:"_blank",rel:"noreferrer"},"* - [_id=32]")],-1))]),t("tr",null,[e[6]||(e[6]=t("td",{style:{"text-align":"center"}},">",-1)),e[7]||(e[7]=t("td",{style:{"text-align":"center"}},"祖先节点",-1)),t("td",k,[a(d,{src:"0022.png",width:"250"})]),e[8]||(e[8]=t("td",{style:{"text-align":"center"}},[t("a",{href:"https://i.gkd.li/i/14045424?gkd=KiA-IFtfaWQ9OTBd",target:"_blank",rel:"noreferrer"},"* > [_id=90]")],-1))]),t("tr",null,[e[9]||(e[9]=t("td",{style:{"text-align":"center"}},"<",-1)),e[10]||(e[10]=t("td",{style:{"text-align":"center"}},"直接子节点",-1)),t("td",b,[a(d,{src:"0023.png",width:"250"})]),e[11]||(e[11]=t("td",{style:{"text-align":"center"}},[t("a",{href:"https://i.gkd.li/i/14045424?gkd=KiA8IFtfaWQ9ODld",target:"_blank",rel:"noreferrer"},"* < [_id=89]")],-1))]),t("tr",null,[e[12]||(e[12]=t("td",{style:{"text-align":"center"}},"<<",-1)),e[13]||(e[13]=t("td",{style:{"text-align":"center"}},"子孙节点(深度先序)",-1)),t("td",m,[a(d,{src:"0024.png",width:"250"})]),e[14]||(e[14]=t("td",{style:{"text-align":"center"}},[t("a",{href:"https://i.gkd.li/i/14045424?gkd=KiA8PDIgW19pZD0yOV0",target:"_blank",rel:"noreferrer"},"* <<2 [_id=29]")],-1))])])])]),_:1}),e[20]||(e[20]=l("",49)),a(d,{src:"0025.png"}),e[21]||(e[21]=l("",37))])}const F=s(g,[["render",E]]);export{A as __pageData,F as default};
@@ -1 +1 @@
1
- import{_ as r,B as l,o as p,c as d,ao as n,j as t,a,G as o,w as h}from"./chunks/framework.DZAoeHqG.js";const x=JSON.parse('{"title":"快照审查","description":"","frontmatter":{},"headers":[],"relativePath":"guide/snapshot.md","filePath":"guide/snapshot.md","lastUpdated":1744453064000}'),u={name:"guide/snapshot.md"};function b(c,e,g,k,m,f){const i=l("GImg"),s=l("NImageGroup");return p(),d("div",null,[e[12]||(e[12]=n('<h1 id="title" tabindex="-1">快照审查 <a class="header-anchor" href="#title" aria-label="Permalink to &quot;快照审查 {#title}&quot;">​</a></h1><p>关于快照的一些知识</p><h2 id="what-is-snapshot" tabindex="-1">什么是快照 <a class="header-anchor" href="#what-is-snapshot" aria-label="Permalink to &quot;什么是快照 {#what-is-snapshot}&quot;">​</a></h2><p>快照是一个 zip 文件, 保存了设备在某个时间点的状态, 包含以下内容</p><ul><li>截图</li><li>设备信息</li><li>界面信息</li><li>无障碍节点信息</li></ul><p>以前开发者和用户必须下载指定应用然后到指定界面使用类似 autojs 之类的工具才能审查界面</p><p>有了快照之后, 用户只需要在指定页面抓取快照分享给开发者即可</p><p>另外对于某些出现频率极低的界面, 快照也能直接保留这些界面的状态, 无需每次等待出现</p><h2 id="capture-snapshot" tabindex="-1">抓取/捕获快照 <a class="header-anchor" href="#capture-snapshot" aria-label="Permalink to &quot;抓取/捕获快照 {#capture-snapshot}&quot;">​</a></h2><p>在 GKD 内可以通过多种方式抓取快照, 需要一些前置条件</p><p>抓取快照时必须<strong>开启无障碍权限</strong>, 否则无法获取 界面信息 和 无障碍节点信息</p><p>如果你的设备满足 Android&gt;=11, GKD 可以通过 无障碍权限 获取截图, 否则需要额外开启截屏服务</p><p>到 <code>主页-设置-高级模式-截屏服务</code> 开启即可</p><p>当 Android&lt;=10 且不开启截屏服务时 GKD 会使用 同尺寸透明图片 作为截图保存到快照里</p><p>以下是抓取快照的方式</p><h3 id="by-button" tabindex="-1">通过悬浮窗按钮 <a class="header-anchor" href="#by-button" aria-label="Permalink to &quot;通过悬浮窗按钮 {#by-button}&quot;">​</a></h3><p>打开 <code>主页-设置-高级模式-悬浮窗服务</code> 后界面会出现一个可拖动的按钮, 在任意界面点击这个按钮即可抓取快照</p><h3 id="by-tile" tabindex="-1">通过快捷开关 <a class="header-anchor" href="#by-tile" aria-label="Permalink to &quot;通过快捷开关 {#by-tile}&quot;">​</a></h3><p>下拉打开状态栏, 左右滑动切换到有图标的界面, 点击添加或编辑, 在应用快捷开关找到 <code>捕获快照</code>, 图标和上面介绍的悬浮窗按钮一样</p><p>添加这个快捷开关到状态栏界面, 添加完毕后在任意界面下拉状态栏点击这个图标即可抓取快照</p><h3 id="by-screenshot" tabindex="-1">通过截屏快照 <a class="header-anchor" href="#by-screenshot" aria-label="Permalink to &quot;通过截屏快照 {#by-screenshot}&quot;">​</a></h3><p>打开 <code>主页-设置-高级模式-截屏快照</code>, 某些手机截屏时会发出特殊的事件, GKD 可以抓取这些事件后触发抓取快照</p><h3 id="by-inspect" tabindex="-1">通过审查工具 <a class="header-anchor" href="#by-inspect" aria-label="Permalink to &quot;通过审查工具 {#by-inspect}&quot;">​</a></h3><p>首先打开 <code>主页-设置-高级模式-HTTP服务</code>, 在 <a href="https://i.gkd.li/device" target="_blank" rel="noreferrer">连接设备</a> 输入刚刚出现的地址后连接</p><p>连接成功后点击右侧的 <code>快照</code> 按钮即可抓取快照</p><h3 id="by-invoke-service" tabindex="-1">通过调用服务 <a class="header-anchor" href="#by-invoke-service" aria-label="Permalink to &quot;通过调用服务 {#by-invoke-service}&quot;">​</a></h3><p>GKD 内部暴露了一个服务, 外部应用(某些快捷手势应用)可以启动这个服务, 启动后将抓取当前界面的快照, 它的路径是</p><div class="language-text vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">text</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span>li.songe.gkd.debug.SnapshotActionService</span></span></code></pre></div><p>通过这个服务, 你可以通过自定义手势来捕获快照, 可在 <a href="https://github.com/gkd-kit/gkd/issues/253" target="_blank" rel="noreferrer">gkd-kit/gkd#253</a> 了解更多</p><h2 id="how-to-upload" tabindex="-1">上传原理 <a class="header-anchor" href="#how-to-upload" aria-label="Permalink to &quot;上传原理 {#how-to-upload}&quot;">​</a></h2><p>当你使用审查工具生成分享链接时, 审查工具会自动帮你上传文件到 Github 后得到链接</p><p>相当于自动化以下行为</p><ul><li>从审查工具下载快照为 file.zip 文件到电脑上</li><li>登录 Github 并转到任意能上传文件或者评论的公开页面</li><li>将刚刚的文件拖动进输入框, 文件链接会自动生成</li></ul><p>当在 GKD 内生成链接时也是一样的原理, 不同之处在于</p><p>网页版的审查工具直接通过 <a href="https://github.com/gkd-kit/network-extension" target="_blank" rel="noreferrer">油猴脚本</a> 获取上传凭证</p><p>而 GKD 内需要手动输入上传凭证, 也就是需要手动输入 Github Cookie</p><p>可通过如下步骤获取 Github Cookie</p><h3 id="get-cookie-on-pc" tabindex="-1">在电脑上获取 <a class="header-anchor" href="#get-cookie-on-pc" aria-label="Permalink to &quot;在电脑上获取 {#get-cookie-on-pc}&quot;">​</a></h3>',38)),t("ul",null,[e[1]||(e[1]=t("li",null,[a("在浏览器打开 "),t("a",{href:"https://github.com",target:"_blank",rel:"noreferrer"},"Github"),a(" 登录账号")],-1)),e[2]||(e[2]=t("li",null,[a("在 "),t("a",{href:"https://github.com",target:"_blank",rel:"noreferrer"},"https://github.com"),a(" 使用 f12 打开调试面板, 选中 网络-文档 后按 f5 刷新页面, 会出来一条 "),t("code",null,"github.com"),a(" 的记录")],-1)),t("li",null,[e[0]||(e[0]=a("点击这条记录, 选中 标头-请求标头-Cookie 的右侧文字, 按下 Ctrl+C 复制即可")),o(i,{src:"0034.png"})]),e[3]||(e[3]=t("li",null,"复制成功后, 到 GKD 内 高级设置-快照-Github Cookie 编辑填入保存即可",-1))]),e[13]||(e[13]=t("h3",{id:"get-cookie-on-phone",tabindex:"-1"},[a("在手机上获取 "),t("a",{class:"header-anchor",href:"#get-cookie-on-phone","aria-label":'Permalink to "在手机上获取 {#get-cookie-on-phone}"'},"​")],-1)),t("ul",null,[e[9]||(e[9]=t("li",null,[a("下载安装 "),t("a",{href:"https://viayoo.com",target:"_blank",rel:"noreferrer"},"Via"),a(" 浏览器")],-1)),e[10]||(e[10]=t("li",null,[a("在浏览器打开 "),t("a",{href:"https://github.com",target:"_blank",rel:"noreferrer"},"Github"),a(" 登录账号")],-1)),t("li",null,[e[5]||(e[5]=a("点击顶部地址栏左侧的图标, 在出现的菜单中点击 ")),e[6]||(e[6]=t("code",null,"查看 Cookies",-1)),e[7]||(e[7]=a(", 出现的弹窗后点击 ")),e[8]||(e[8]=t("code",null,"复制文本",-1)),o(s,null,{default:h(()=>[o(i,{src:"0035.png",class:"w-[45%]"}),e[4]||(e[4]=a()),o(i,{src:"0036.png",class:"w-[45%]"})]),_:1})]),e[11]||(e[11]=t("li",null,"复制成功后, 到 GKD 内 高级设置-快照-Github Cookie 编辑填入保存即可",-1))]),e[14]||(e[14]=n('<h2 id="rm-privacy-info" tabindex="-1">去除隐私信息 <a class="header-anchor" href="#rm-privacy-info" aria-label="Permalink to &quot;去除隐私信息 {#rm-privacy-info}&quot;">​</a></h2><p>当抓取快照时, 图片中可能含有人的名字等敏感信息, 可能需要去除这些信息</p><p>在 GKD 的快照记录界面, 你可以先将快照图片保存到相册, 然后在相册做打马赛克之类的编辑, 之后回到记录界面替换截图即可</p><p>如果敏感信息是文字保留在快照里的节点信息上, 你需要将快照 zip 文件解压缩, 内部有一个 json 文件</p><p>节点信息就在 nodes 数组字段上, 可以直接使用文本替换为等价数量的 <code>*</code>, 修改文件后重新压缩为 zip 重新导入快照即可</p><h2 id="share-note" tabindex="-1">分享须知 <a class="header-anchor" href="#share-note" aria-label="Permalink to &quot;分享须知 {#share-note}&quot;">​</a></h2><p>快照文件是 zip 格式, 你可以直接分享发送文件给其他人</p><p>通过链接分享时, 某些用户喜欢直接复制地址栏链接</p><div class="language-text vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">text</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span>https://i.gkd.li/snapshot/1734099604908</span></span></code></pre></div><p>这是错误的分享方式, 地址栏复制的链接只能在当前用户浏览器上打开</p><p>其它设备打开这种本地链接只会显示 <code>快照缺失</code></p><p>正确的分享方式是快照页面右上角分享生成的链接</p><div class="language-text vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">text</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span>https://i.gkd.li/i/18135562</span></span></code></pre></div>',13))])}const y=r(u,[["render",b]]);export{x as __pageData,y as default};
1
+ import{_ as r,B as l,o as p,c as d,ao as n,j as t,a,G as o,w as h}from"./chunks/framework.DZAoeHqG.js";const x=JSON.parse('{"title":"快照审查","description":"","frontmatter":{},"headers":[],"relativePath":"guide/snapshot.md","filePath":"guide/snapshot.md","lastUpdated":1744454038000}'),u={name:"guide/snapshot.md"};function b(c,e,g,k,m,f){const i=l("GImg"),s=l("NImageGroup");return p(),d("div",null,[e[12]||(e[12]=n('<h1 id="title" tabindex="-1">快照审查 <a class="header-anchor" href="#title" aria-label="Permalink to &quot;快照审查 {#title}&quot;">​</a></h1><p>关于快照的一些知识</p><h2 id="what-is-snapshot" tabindex="-1">什么是快照 <a class="header-anchor" href="#what-is-snapshot" aria-label="Permalink to &quot;什么是快照 {#what-is-snapshot}&quot;">​</a></h2><p>快照是一个 zip 文件, 保存了设备在某个时间点的状态, 包含以下内容</p><ul><li>截图</li><li>设备信息</li><li>界面信息</li><li>无障碍节点信息</li></ul><p>以前开发者和用户必须下载指定应用然后到指定界面使用类似 autojs 之类的工具才能审查界面</p><p>有了快照之后, 用户只需要在指定页面抓取快照分享给开发者即可</p><p>另外对于某些出现频率极低的界面, 快照也能直接保留这些界面的状态, 无需每次等待出现</p><h2 id="capture-snapshot" tabindex="-1">抓取/捕获快照 <a class="header-anchor" href="#capture-snapshot" aria-label="Permalink to &quot;抓取/捕获快照 {#capture-snapshot}&quot;">​</a></h2><p>在 GKD 内可以通过多种方式抓取快照, 需要一些前置条件</p><p>抓取快照时必须<strong>开启无障碍权限</strong>, 否则无法获取 界面信息 和 无障碍节点信息</p><p>如果你的设备满足 Android&gt;=11, GKD 可以通过 无障碍权限 获取截图, 否则需要额外开启截屏服务</p><p>到 <code>主页-设置-高级模式-截屏服务</code> 开启即可</p><p>当 Android&lt;=10 且不开启截屏服务时 GKD 会使用 同尺寸透明图片 作为截图保存到快照里</p><p>以下是抓取快照的方式</p><h3 id="by-button" tabindex="-1">通过悬浮窗按钮 <a class="header-anchor" href="#by-button" aria-label="Permalink to &quot;通过悬浮窗按钮 {#by-button}&quot;">​</a></h3><p>打开 <code>主页-设置-高级模式-悬浮窗服务</code> 后界面会出现一个可拖动的按钮, 在任意界面点击这个按钮即可抓取快照</p><h3 id="by-tile" tabindex="-1">通过快捷开关 <a class="header-anchor" href="#by-tile" aria-label="Permalink to &quot;通过快捷开关 {#by-tile}&quot;">​</a></h3><p>下拉打开状态栏, 左右滑动切换到有图标的界面, 点击添加或编辑, 在应用快捷开关找到 <code>捕获快照</code>, 图标和上面介绍的悬浮窗按钮一样</p><p>添加这个快捷开关到状态栏界面, 添加完毕后在任意界面下拉状态栏点击这个图标即可抓取快照</p><h3 id="by-screenshot" tabindex="-1">通过截屏快照 <a class="header-anchor" href="#by-screenshot" aria-label="Permalink to &quot;通过截屏快照 {#by-screenshot}&quot;">​</a></h3><p>打开 <code>主页-设置-高级模式-截屏快照</code>, 某些手机截屏时会发出特殊的事件, GKD 可以抓取这些事件后触发抓取快照</p><h3 id="by-inspect" tabindex="-1">通过审查工具 <a class="header-anchor" href="#by-inspect" aria-label="Permalink to &quot;通过审查工具 {#by-inspect}&quot;">​</a></h3><p>首先打开 <code>主页-设置-高级模式-HTTP服务</code>, 在 <a href="https://i.gkd.li/device" target="_blank" rel="noreferrer">连接设备</a> 输入刚刚出现的地址后连接</p><p>连接成功后点击右侧的 <code>快照</code> 按钮即可抓取快照</p><h3 id="by-invoke-service" tabindex="-1">通过调用服务 <a class="header-anchor" href="#by-invoke-service" aria-label="Permalink to &quot;通过调用服务 {#by-invoke-service}&quot;">​</a></h3><p>GKD 内部暴露了一个服务, 外部应用(某些快捷手势应用)可以启动这个服务, 启动后将抓取当前界面的快照, 它的路径是</p><div class="language-text vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">text</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span>li.songe.gkd.debug.SnapshotActionService</span></span></code></pre></div><p>通过这个服务, 你可以通过自定义手势来捕获快照, 可在 <a href="https://github.com/gkd-kit/gkd/issues/253" target="_blank" rel="noreferrer">gkd-kit/gkd#253</a> 了解更多</p><h2 id="how-to-upload" tabindex="-1">上传原理 <a class="header-anchor" href="#how-to-upload" aria-label="Permalink to &quot;上传原理 {#how-to-upload}&quot;">​</a></h2><p>当你使用审查工具生成分享链接时, 审查工具会自动帮你上传文件到 Github 后得到链接</p><p>相当于自动化以下行为</p><ul><li>从审查工具下载快照为 file.zip 文件到电脑上</li><li>登录 Github 并转到任意能上传文件或者评论的公开页面</li><li>将刚刚的文件拖动进输入框, 文件链接会自动生成</li></ul><p>当在 GKD 内生成链接时也是一样的原理, 不同之处在于</p><p>网页版的审查工具直接通过 <a href="https://github.com/gkd-kit/network-extension" target="_blank" rel="noreferrer">油猴脚本</a> 获取上传凭证</p><p>而 GKD 内需要手动输入上传凭证, 也就是需要手动输入 Github Cookie</p><p>可通过如下步骤获取 Github Cookie</p><h3 id="get-cookie-on-pc" tabindex="-1">在电脑上获取 <a class="header-anchor" href="#get-cookie-on-pc" aria-label="Permalink to &quot;在电脑上获取 {#get-cookie-on-pc}&quot;">​</a></h3>',38)),t("ul",null,[e[1]||(e[1]=t("li",null,[a("在浏览器打开 "),t("a",{href:"https://github.com",target:"_blank",rel:"noreferrer"},"Github"),a(" 登录账号")],-1)),e[2]||(e[2]=t("li",null,[a("在 "),t("a",{href:"https://github.com",target:"_blank",rel:"noreferrer"},"https://github.com"),a(" 使用 f12 打开调试面板, 选中 网络-文档 后按 f5 刷新页面, 会出来一条 "),t("code",null,"github.com"),a(" 的记录")],-1)),t("li",null,[e[0]||(e[0]=a("点击这条记录, 选中 标头-请求标头-Cookie 的右侧文字, 按下 Ctrl+C 复制即可")),o(i,{src:"0034.png"})]),e[3]||(e[3]=t("li",null,"复制成功后, 到 GKD 内 高级设置-快照-Github Cookie 编辑填入保存即可",-1))]),e[13]||(e[13]=t("h3",{id:"get-cookie-on-phone",tabindex:"-1"},[a("在手机上获取 "),t("a",{class:"header-anchor",href:"#get-cookie-on-phone","aria-label":'Permalink to "在手机上获取 {#get-cookie-on-phone}"'},"​")],-1)),t("ul",null,[e[9]||(e[9]=t("li",null,[a("下载安装 "),t("a",{href:"https://viayoo.com",target:"_blank",rel:"noreferrer"},"Via"),a(" 浏览器")],-1)),e[10]||(e[10]=t("li",null,[a("在浏览器打开 "),t("a",{href:"https://github.com",target:"_blank",rel:"noreferrer"},"Github"),a(" 登录账号")],-1)),t("li",null,[e[5]||(e[5]=a("点击顶部地址栏左侧的图标, 在出现的菜单中点击 ")),e[6]||(e[6]=t("code",null,"查看 Cookies",-1)),e[7]||(e[7]=a(", 出现的弹窗后点击 ")),e[8]||(e[8]=t("code",null,"复制文本",-1)),o(s,null,{default:h(()=>[o(i,{src:"0035.png",class:"w-[45%]"}),e[4]||(e[4]=a()),o(i,{src:"0036.png",class:"w-[45%]"})]),_:1})]),e[11]||(e[11]=t("li",null,"复制成功后, 到 GKD 内 高级设置-快照-Github Cookie 编辑填入保存即可",-1))]),e[14]||(e[14]=n('<h2 id="rm-privacy-info" tabindex="-1">去除隐私信息 <a class="header-anchor" href="#rm-privacy-info" aria-label="Permalink to &quot;去除隐私信息 {#rm-privacy-info}&quot;">​</a></h2><p>当抓取快照时, 图片中可能含有人的名字等敏感信息, 可能需要去除这些信息</p><p>在 GKD 的快照记录界面, 你可以先将快照图片保存到相册, 然后在相册做打马赛克之类的编辑, 之后回到记录界面替换截图即可</p><p>如果敏感信息是文字保留在快照里的节点信息上, 你需要将快照 zip 文件解压缩, 内部有一个 json 文件</p><p>节点信息就在 nodes 数组字段上, 可以直接使用文本替换为等价数量的 <code>*</code>, 修改文件后重新压缩为 zip 重新导入快照即可</p><h2 id="share-note" tabindex="-1">分享须知 <a class="header-anchor" href="#share-note" aria-label="Permalink to &quot;分享须知 {#share-note}&quot;">​</a></h2><p>快照文件是 zip 格式, 你可以直接分享发送文件给其他人</p><p>通过链接分享时, 某些用户喜欢直接复制地址栏链接</p><div class="language-text vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">text</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span>https://i.gkd.li/snapshot/1734099604908</span></span></code></pre></div><p>这是错误的分享方式, 地址栏复制的链接只能在当前用户浏览器上打开</p><p>其它设备打开这种本地链接只会显示 <code>快照缺失</code></p><p>正确的分享方式是快照页面右上角分享生成的链接</p><div class="language-text vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">text</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span>https://i.gkd.li/i/18135562</span></span></code></pre></div>',13))])}const y=r(u,[["render",b]]);export{x as __pageData,y as default};
@@ -1 +1 @@
1
- import{_ as r,B as l,o as p,c as d,ao as n,j as t,a,G as o,w as h}from"./chunks/framework.DZAoeHqG.js";const x=JSON.parse('{"title":"快照审查","description":"","frontmatter":{},"headers":[],"relativePath":"guide/snapshot.md","filePath":"guide/snapshot.md","lastUpdated":1744453064000}'),u={name:"guide/snapshot.md"};function b(c,e,g,k,m,f){const i=l("GImg"),s=l("NImageGroup");return p(),d("div",null,[e[12]||(e[12]=n("",38)),t("ul",null,[e[1]||(e[1]=t("li",null,[a("在浏览器打开 "),t("a",{href:"https://github.com",target:"_blank",rel:"noreferrer"},"Github"),a(" 登录账号")],-1)),e[2]||(e[2]=t("li",null,[a("在 "),t("a",{href:"https://github.com",target:"_blank",rel:"noreferrer"},"https://github.com"),a(" 使用 f12 打开调试面板, 选中 网络-文档 后按 f5 刷新页面, 会出来一条 "),t("code",null,"github.com"),a(" 的记录")],-1)),t("li",null,[e[0]||(e[0]=a("点击这条记录, 选中 标头-请求标头-Cookie 的右侧文字, 按下 Ctrl+C 复制即可")),o(i,{src:"0034.png"})]),e[3]||(e[3]=t("li",null,"复制成功后, 到 GKD 内 高级设置-快照-Github Cookie 编辑填入保存即可",-1))]),e[13]||(e[13]=t("h3",{id:"get-cookie-on-phone",tabindex:"-1"},[a("在手机上获取 "),t("a",{class:"header-anchor",href:"#get-cookie-on-phone","aria-label":'Permalink to "在手机上获取 {#get-cookie-on-phone}"'},"​")],-1)),t("ul",null,[e[9]||(e[9]=t("li",null,[a("下载安装 "),t("a",{href:"https://viayoo.com",target:"_blank",rel:"noreferrer"},"Via"),a(" 浏览器")],-1)),e[10]||(e[10]=t("li",null,[a("在浏览器打开 "),t("a",{href:"https://github.com",target:"_blank",rel:"noreferrer"},"Github"),a(" 登录账号")],-1)),t("li",null,[e[5]||(e[5]=a("点击顶部地址栏左侧的图标, 在出现的菜单中点击 ")),e[6]||(e[6]=t("code",null,"查看 Cookies",-1)),e[7]||(e[7]=a(", 出现的弹窗后点击 ")),e[8]||(e[8]=t("code",null,"复制文本",-1)),o(s,null,{default:h(()=>[o(i,{src:"0035.png",class:"w-[45%]"}),e[4]||(e[4]=a()),o(i,{src:"0036.png",class:"w-[45%]"})]),_:1})]),e[11]||(e[11]=t("li",null,"复制成功后, 到 GKD 内 高级设置-快照-Github Cookie 编辑填入保存即可",-1))]),e[14]||(e[14]=n("",13))])}const y=r(u,[["render",b]]);export{x as __pageData,y as default};
1
+ import{_ as r,B as l,o as p,c as d,ao as n,j as t,a,G as o,w as h}from"./chunks/framework.DZAoeHqG.js";const x=JSON.parse('{"title":"快照审查","description":"","frontmatter":{},"headers":[],"relativePath":"guide/snapshot.md","filePath":"guide/snapshot.md","lastUpdated":1744454038000}'),u={name:"guide/snapshot.md"};function b(c,e,g,k,m,f){const i=l("GImg"),s=l("NImageGroup");return p(),d("div",null,[e[12]||(e[12]=n("",38)),t("ul",null,[e[1]||(e[1]=t("li",null,[a("在浏览器打开 "),t("a",{href:"https://github.com",target:"_blank",rel:"noreferrer"},"Github"),a(" 登录账号")],-1)),e[2]||(e[2]=t("li",null,[a("在 "),t("a",{href:"https://github.com",target:"_blank",rel:"noreferrer"},"https://github.com"),a(" 使用 f12 打开调试面板, 选中 网络-文档 后按 f5 刷新页面, 会出来一条 "),t("code",null,"github.com"),a(" 的记录")],-1)),t("li",null,[e[0]||(e[0]=a("点击这条记录, 选中 标头-请求标头-Cookie 的右侧文字, 按下 Ctrl+C 复制即可")),o(i,{src:"0034.png"})]),e[3]||(e[3]=t("li",null,"复制成功后, 到 GKD 内 高级设置-快照-Github Cookie 编辑填入保存即可",-1))]),e[13]||(e[13]=t("h3",{id:"get-cookie-on-phone",tabindex:"-1"},[a("在手机上获取 "),t("a",{class:"header-anchor",href:"#get-cookie-on-phone","aria-label":'Permalink to "在手机上获取 {#get-cookie-on-phone}"'},"​")],-1)),t("ul",null,[e[9]||(e[9]=t("li",null,[a("下载安装 "),t("a",{href:"https://viayoo.com",target:"_blank",rel:"noreferrer"},"Via"),a(" 浏览器")],-1)),e[10]||(e[10]=t("li",null,[a("在浏览器打开 "),t("a",{href:"https://github.com",target:"_blank",rel:"noreferrer"},"Github"),a(" 登录账号")],-1)),t("li",null,[e[5]||(e[5]=a("点击顶部地址栏左侧的图标, 在出现的菜单中点击 ")),e[6]||(e[6]=t("code",null,"查看 Cookies",-1)),e[7]||(e[7]=a(", 出现的弹窗后点击 ")),e[8]||(e[8]=t("code",null,"复制文本",-1)),o(s,null,{default:h(()=>[o(i,{src:"0035.png",class:"w-[45%]"}),e[4]||(e[4]=a()),o(i,{src:"0036.png",class:"w-[45%]"})]),_:1})]),e[11]||(e[11]=t("li",null,"复制成功后, 到 GKD 内 高级设置-快照-Github Cookie 编辑填入保存即可",-1))]),e[14]||(e[14]=n("",13))])}const y=r(u,[["render",b]]);export{x as __pageData,y as default};
@@ -1,4 +1,4 @@
1
- import{_ as t,B as h,o as e,c as k,ao as a,G as n,j as p,a as l}from"./chunks/framework.DZAoeHqG.js";const u=JSON.parse('{"title":"订阅规则","description":"","frontmatter":{},"headers":[],"relativePath":"guide/subscription.md","filePath":"guide/subscription.md","lastUpdated":1744453064000}'),E={name:"guide/subscription.md"};function r(d,s,g,o,F,y){const i=h("GImg");return e(),k("div",null,[s[0]||(s[0]=a(`<h1 id="title" tabindex="-1">订阅规则 <a class="header-anchor" href="#title" aria-label="Permalink to &quot;订阅规则 {#title}&quot;">​</a></h1><p>GKD 的本地规则和远程规则都是订阅, 它们的格式完全一致, 不同之处在于本地订阅用户可以自行更改, 远程规则需要自动更新</p><p>订阅使用 <a href="https://json5.org/" target="_blank" rel="noreferrer">JSON5</a> 语法, JSON5 是 JSON 的兼容扩展, 它无需键引号, 支持注释</p><p>如下便是一个最简单的空规则的订阅文件的内容</p><div class="language-json5 vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">json5</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;">// gkd.json5</span></span>
1
+ import{_ as t,B as h,o as e,c as k,ao as a,G as n,j as p,a as l}from"./chunks/framework.DZAoeHqG.js";const u=JSON.parse('{"title":"订阅规则","description":"","frontmatter":{},"headers":[],"relativePath":"guide/subscription.md","filePath":"guide/subscription.md","lastUpdated":1744454038000}'),E={name:"guide/subscription.md"};function r(d,s,g,o,F,y){const i=h("GImg");return e(),k("div",null,[s[0]||(s[0]=a(`<h1 id="title" tabindex="-1">订阅规则 <a class="header-anchor" href="#title" aria-label="Permalink to &quot;订阅规则 {#title}&quot;">​</a></h1><p>GKD 的本地规则和远程规则都是订阅, 它们的格式完全一致, 不同之处在于本地订阅用户可以自行更改, 远程规则需要自动更新</p><p>订阅使用 <a href="https://json5.org/" target="_blank" rel="noreferrer">JSON5</a> 语法, JSON5 是 JSON 的兼容扩展, 它无需键引号, 支持注释</p><p>如下便是一个最简单的空规则的订阅文件的内容</p><div class="language-json5 vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">json5</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;">// gkd.json5</span></span>
2
2
  <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">{</span></span>
3
3
  <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">233</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
4
4
  <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;Subscription&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
@@ -1 +1 @@
1
- import{_ as t,B as h,o as e,c as k,ao as a,G as n,j as p,a as l}from"./chunks/framework.DZAoeHqG.js";const u=JSON.parse('{"title":"订阅规则","description":"","frontmatter":{},"headers":[],"relativePath":"guide/subscription.md","filePath":"guide/subscription.md","lastUpdated":1744453064000}'),E={name:"guide/subscription.md"};function r(d,s,g,o,F,y){const i=h("GImg");return e(),k("div",null,[s[0]||(s[0]=a("",12)),n(i,{src:"https://e.gkd.li/76d0bc58-de43-416d-9454-f980e8075660","max-w":"[min(375px,100%)]"}),s[1]||(s[1]=a("",4)),n(i,{src:"https://e.gkd.li/6497fe09-b019-46b9-8157-d4b21809b4fc","max-w":"[min(375px,100%)]"}),s[2]||(s[2]=a("",4)),n(i,{src:"https://e.gkd.li/8af0bc31-8a23-4342-b2f3-7a9378f9e094","max-w":"[min(375px,100%)]"}),s[3]||(s[3]=a("",4)),n(i,{src:"https://e.gkd.li/f51545c0-d3aa-4ec7-87eb-adf927023640","max-w":"[min(375px,100%)]"}),s[4]||(s[4]=a("",4)),n(i,{src:"https://e.gkd.li/6784867c-4fa9-4a06-abf7-b332ba2945de","max-w":"[min(375px,100%)]"}),s[5]||(s[5]=p("p",null,[l("此处可添加 "),p("a",{href:"/api/interfaces/RawCategory"},"规则类别"),l(", 在输入框中输入类别名称点击确认即可")],-1)),s[6]||(s[6]=p("p",null,"TODO",-1))])}const C=t(E,[["render",r]]);export{u as __pageData,C as default};
1
+ import{_ as t,B as h,o as e,c as k,ao as a,G as n,j as p,a as l}from"./chunks/framework.DZAoeHqG.js";const u=JSON.parse('{"title":"订阅规则","description":"","frontmatter":{},"headers":[],"relativePath":"guide/subscription.md","filePath":"guide/subscription.md","lastUpdated":1744454038000}'),E={name:"guide/subscription.md"};function r(d,s,g,o,F,y){const i=h("GImg");return e(),k("div",null,[s[0]||(s[0]=a("",12)),n(i,{src:"https://e.gkd.li/76d0bc58-de43-416d-9454-f980e8075660","max-w":"[min(375px,100%)]"}),s[1]||(s[1]=a("",4)),n(i,{src:"https://e.gkd.li/6497fe09-b019-46b9-8157-d4b21809b4fc","max-w":"[min(375px,100%)]"}),s[2]||(s[2]=a("",4)),n(i,{src:"https://e.gkd.li/8af0bc31-8a23-4342-b2f3-7a9378f9e094","max-w":"[min(375px,100%)]"}),s[3]||(s[3]=a("",4)),n(i,{src:"https://e.gkd.li/f51545c0-d3aa-4ec7-87eb-adf927023640","max-w":"[min(375px,100%)]"}),s[4]||(s[4]=a("",4)),n(i,{src:"https://e.gkd.li/6784867c-4fa9-4a06-abf7-b332ba2945de","max-w":"[min(375px,100%)]"}),s[5]||(s[5]=p("p",null,[l("此处可添加 "),p("a",{href:"/api/interfaces/RawCategory"},"规则类别"),l(", 在输入框中输入类别名称点击确认即可")],-1)),s[6]||(s[6]=p("p",null,"TODO",-1))])}const C=t(E,[["render",r]]);export{u as __pageData,C as default};