RN for OpenHarmony英雄联盟助手App实战:符文预设实现
本文介绍了英雄联盟符文预设功能的实现方案。该功能通过提供常用符文搭配预设,帮助新手玩家快速上手,降低学习成本。文章详细阐述了预设数据的结构化设计,包括主副系选择和适用场景描述。页面采用卡片式布局展示预设信息,包含醒目的名称、主副系路径和描述文字,形成清晰的视觉层次。样式设计上采用圆角卡片、金色标题和合理的间距,确保良好的用户体验。预设数据基于游戏经验总结,如"电刑法师"适合爆发

案例开源地址:https://atomgit.com/nutpi/rn_openharmony_lol
符文配置器让用户可以自由搭配符文,但对于新手来说,可能不知道什么搭配是好的。符文预设功能提供一些常用的、经过验证的符文搭配,让用户可以快速参考或直接使用。
这篇文章我们来实现符文预设页面,重点是静态数据的组织方式、卡片列表的展示、以及如何设计易于维护的预设数据结构。
为什么需要符文预设
在英雄联盟中,符文系统有 5 个系别,每个系别有 4 层符文,加上副系的选择,组合数量非常庞大。对于新手玩家来说,面对这么多选择往往不知所措。
符文预设的价值在于:
- 降低学习成本:新手不需要理解每个符文的效果,直接使用推荐配置
- 提供参考基准:即使是老玩家,也可以参考预设来优化自己的配置
- 快速开始游戏:不需要每次都从头配置,选择预设即可
从产品设计的角度,预设功能是"专家系统"的一种体现——把专家的知识固化下来,让普通用户也能享受到专业级的配置。
预设数据的设计
符文预设是相对静态的数据,不需要从 API 获取。我们直接在代码中定义:
const presets = [
{name: '电刑法师', primary: '主宰', secondary: '巫术', desc: '适合爆发型法师'},
{name: '征服者战士', primary: '精密', secondary: '坚决', desc: '适合持续作战的战士'},
{name: '彗星消耗', primary: '巫术', secondary: '启迪', desc: '适合消耗型法师'},
{name: '不灭之握', primary: '坚决', secondary: '精密', desc: '适合坦克英雄'},
{name: '致命节奏', primary: '精密', secondary: '主宰', desc: '适合攻速型射手'},
];
数据结构说明:
name:预设的名称,通常以基石符文命名primary:主系名称secondary:副系名称desc:简短的描述,说明适合什么类型的英雄
为什么用中文而不是 key?
这里直接用中文名称而不是英文 key(如 “Domination”),是因为这个数据只用于展示,不需要做映射转换。如果后续要和符文配置器联动(比如点击预设自动填充配置),可以改成用 key。
预设数据的来源与游戏理解
这些预设是根据游戏经验总结的常用搭配,每个预设都有其适用场景:
电刑法师:主宰系的电刑是爆发型法师的首选基石,配合巫术系的法力流系带和焦灼,能最大化爆发伤害。典型英雄包括阿狸、辛德拉、维克托等。电刑的触发条件是 3 秒内用 3 个独立技能或攻击命中敌人,对于技能连招流畅的法师来说非常容易触发。
征服者战士:精密系的征服者适合需要持续作战的战士,叠满后提供大量攻击力和治疗。配合坚决系的骨甲和复苏之风,增加生存能力。典型英雄包括剑姬、青钢影、锐雯等。征服者需要叠层才能发挥最大效果,所以适合能持续输出的英雄。
彗星消耗:巫术系的奥术彗星适合远程消耗型法师,配合启迪系的饼干和时间扭曲补剂,增强对线能力。典型英雄包括泽拉斯、拉克丝、维迦等。彗星的优势是冷却时间短,配合减速技能几乎必中。
不灭之握:坚决系的不灭之握是坦克的标配,提供持续的治疗和生命值加成。配合精密系的凯旋和韧性,增强团战能力。典型英雄包括奥恩、塞恩、慎等。不灭之握每 4 秒可以触发一次,对于需要频繁换血的坦克来说收益很高。
致命节奏:精密系的致命节奏适合攻速型射手,战斗中不断提升攻速。配合主宰系的血之滋味和贪欲猎手,增加续航。典型英雄包括金克丝、薇恩、卡莎等。致命节奏在长时间战斗中收益递增,适合后期团战。
页面组件实现
import React from 'react';
import {View, Text, ScrollView, StyleSheet} from 'react-native';
import {colors} from '../../styles/colors';
export function RunePresetPage() {
return (
<ScrollView style={styles.container} showsVerticalScrollIndicator={false}>
<Text style={styles.title}>常用符文预设</Text>
<Text style={styles.subtitle}>快速选择适合的符文搭配</Text>
{presets.map((preset, index) => (
<View key={index} style={styles.presetCard}>
<Text style={styles.presetName}>{preset.name}</Text>
<View style={styles.pathRow}>
<Text style={styles.pathText}>主系: {preset.primary}</Text>
<Text style={styles.pathText}>副系: {preset.secondary}</Text>
</View>
<Text style={styles.presetDesc}>{preset.desc}</Text>
</View>
))}
<View style={styles.bottomSpace} />
</ScrollView>
);
}
页面结构分析:
整个页面由三部分组成:标题区域、预设卡片列表、底部留白。这种结构简洁明了,用户一眼就能理解页面的用途。
为什么用 index 作为 key?
预设数据是静态的,不会动态增删或重排序,用 index 作为 key 是安全的。React 的 key 主要用于优化列表渲染,当列表项的顺序或数量变化时,key 帮助 React 识别哪些项需要更新。对于静态列表,index 完全够用。
如果预设数据会变化(比如用户可以添加自定义预设),应该给每个预设加一个唯一 ID:
const presets = [
{id: 'electrocute-mage', name: '电刑法师', ...},
{id: 'conqueror-fighter', name: '征服者战士', ...},
];
卡片内容的组织
每张预设卡片包含三部分信息,形成清晰的视觉层次:
<View key={index} style={styles.presetCard}>
<Text style={styles.presetName}>{preset.name}</Text>
<View style={styles.pathRow}>
<Text style={styles.pathText}>主系: {preset.primary}</Text>
<Text style={styles.pathText}>副系: {preset.secondary}</Text>
</View>
<Text style={styles.presetDesc}>{preset.desc}</Text>
</View>
预设名称:用金色大字体,是卡片最醒目的部分。名称通常是基石符文的名字,玩家一看就知道是什么流派。金色在游戏中代表稀有和重要,用在这里能吸引用户注意。
主副系信息:横向排列,显示主系和副系的名称。这是符文搭配的核心信息,用户通过这两个信息就能大致了解这个预设的风格。
描述文字:用较小的灰色字体,说明这个预设适合什么类型的英雄。帮助用户判断是否适合自己当前要玩的英雄。
样式设计详解
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.background,
padding: 16
},
title: {
fontSize: 20,
fontWeight: 'bold',
color: colors.textPrimary,
marginBottom: 4
},
subtitle: {
fontSize: 14,
color: colors.textSecondary,
marginBottom: 20
},
presetCard: {
backgroundColor: colors.backgroundCard,
borderRadius: 12,
padding: 16,
marginBottom: 12,
borderWidth: 1,
borderColor: colors.border
},
presetName: {
fontSize: 18,
fontWeight: '600',
color: colors.textGold,
marginBottom: 8
},
pathRow: {
flexDirection: 'row',
marginBottom: 8
},
pathText: {
fontSize: 14,
color: colors.textPrimary,
marginRight: 16
},
presetDesc: {
fontSize: 13,
color: colors.textSecondary
},
bottomSpace: {
height: 20
},
});
容器样式:
flex: 1 让容器占满可用空间,padding: 16 给内容留出呼吸空间。这个 padding 值在整个 App 中保持一致,形成统一的视觉节奏。
标题样式:
标题和副标题形成主次关系。标题 20px 加粗,副标题 14px 普通,颜色也有深浅之分。marginBottom: 20 让标题区域和卡片列表之间有足够的间距。
卡片样式:
borderRadius: 12:圆角让卡片更柔和,和整个 App 的设计语言一致padding: 16:内边距让内容不会贴着边缘,阅读更舒适marginBottom: 12:卡片之间的间距,不会太挤也不会太散borderWidth: 1+borderColor: colors.border:细边框增加卡片的层次感
预设名称的颜色:
用 colors.textGold 金色,和游戏中的风格一致。金色在深色背景上非常醒目,能有效吸引用户注意力。
主副系信息的布局:
flexDirection: 'row' 让主系和副系横向排列,marginRight: 16 在它们之间留出间距。这种布局紧凑但不拥挤,信息一目了然。
静态数据 vs 动态数据
这个页面使用静态数据,这是一个有意的设计选择。我们来分析一下静态数据和动态数据的取舍:
静态数据的优势:
- 无需网络请求:页面打开即可显示,没有加载等待
- 离线可用:即使没有网络也能正常使用
- 代码简单:不需要处理加载状态、错误状态
- 性能好:数据直接在内存中,访问速度快
静态数据的劣势:
- 更新需要发版:如果要修改预设,需要发布新版本
- 无法个性化:所有用户看到的都是一样的预设
- 数据量有限:不适合大量数据
对于符文预设这个场景,静态数据是合适的选择。预设数量不多,更新频率低,而且用户期望的是"专家推荐",不需要个性化。
如果后续要支持更多预设或者个性化推荐,可以改成从服务器获取:
const [presets, setPresets] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function loadPresets() {
const data = await api.getRunePresets();
setPresets(data);
setLoading(false);
}
loadPresets();
}, []);
交互增强:添加点击反馈
当前实现是纯展示,用户只能看不能操作。我们可以添加点击功能,让用户点击预设后跳转到符文配置器:
import {TouchableOpacity} from 'react-native';
import {useNavigation} from '../../context/NavigationContext';
export function RunePresetPage() {
const {navigate} = useNavigation();
const handlePresetPress = (preset) => {
// 跳转到符文配置器,并传递预设数据
navigate('RuneBuilder', {preset});
};
return (
<ScrollView style={styles.container}>
{presets.map((preset, index) => (
<TouchableOpacity
key={index}
style={styles.presetCard}
onPress={() => handlePresetPress(preset)}
activeOpacity={0.7}
>
{/* 卡片内容 */}
</TouchableOpacity>
))}
</ScrollView>
);
}
activeOpacity={0.7} 让用户点击时有视觉反馈,透明度变化表示"我点到了"。
扩展数据结构
如果要支持点击应用预设的功能,需要扩展数据结构,包含具体的符文选择:
const presets = [
{
id: 'electrocute-mage',
name: '电刑法师',
primary: 'Domination',
secondary: 'Sorcery',
primaryRunes: {
keystone: 'Electrocute',
slot1: 'TasteOfBlood',
slot2: 'EyeballCollection',
slot3: 'RavenousHunter',
},
secondaryRunes: {
slot1: 'ManaflowBand',
slot2: 'Scorch',
},
statMods: ['adaptive', 'adaptive', 'armor'],
desc: '适合爆发型法师',
champions: ['阿狸', '辛德拉', '维克托'],
},
// ...
];
这个扩展结构包含:
id:唯一标识,用于 key 和数据关联primaryRunes/secondaryRunes:具体的符文选择statMods:属性碎片的选择champions:推荐使用这个预设的英雄
按英雄筛选预设
有了 champions 字段,就可以实现按英雄筛选预设的功能:
const [selectedChampion, setSelectedChampion] = useState(null);
const filteredPresets = selectedChampion
? presets.filter(p => p.champions.includes(selectedChampion))
: presets;
return (
<ScrollView>
{/* 英雄选择器 */}
<ChampionPicker
value={selectedChampion}
onChange={setSelectedChampion}
/>
{/* 筛选后的预设列表 */}
{filteredPresets.map(preset => (
<PresetCard key={preset.id} preset={preset} />
))}
</ScrollView>
);
这样用户可以先选择要玩的英雄,然后看到适合这个英雄的符文预设。
用户自定义预设
更进一步,可以让用户保存自己的符文配置为预设:
const [customPresets, setCustomPresets] = useState([]);
// 从 AsyncStorage 加载用户预设
useEffect(() => {
async function loadCustomPresets() {
const saved = await AsyncStorage.getItem('customRunePresets');
if (saved) {
setCustomPresets(JSON.parse(saved));
}
}
loadCustomPresets();
}, []);
// 保存新预设
const savePreset = async (config, name) => {
const newPreset = {
id: Date.now().toString(),
name,
...config,
isCustom: true,
};
const updated = [...customPresets, newPreset];
setCustomPresets(updated);
await AsyncStorage.setItem('customRunePresets', JSON.stringify(updated));
};
// 合并系统预设和用户预设
const allPresets = [...presets, ...customPresets];
用户预设用 isCustom: true 标记,在 UI 上可以显示不同的样式或者提供删除功能。
小结
符文预设页面虽然代码量不大,但涉及到几个重要的设计决策:
- 静态数据的选择:对于更新频率低、数量少的数据,静态定义比动态获取更合适
- 数据结构的设计:当前结构简单够用,但预留了扩展空间
- 视觉层次的处理:通过字体大小、颜色深浅、间距等手段建立信息层级
- 交互的渐进增强:先实现基础展示,再逐步添加点击、筛选等交互功能
这种"先简单后复杂"的开发方式,可以让我们快速验证功能的价值,再根据用户反馈决定是否继续投入。
下一篇我们来实现召唤师技能列表,展示游戏中所有的召唤师技能。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐



所有评论(0)