欢迎加入开源鸿蒙跨平台社区https://openharmonycrossplatform.csdn.net

📋 前言

滑块(Slider)是移动应用中常用的交互组件,用于在给定范围内选择一个数值。无论是调节音量、设置亮度、筛选价格区间,还是调整播放进度,滑块都能提供直观的用户体验。@react-native-community/slider 是 React Native 官方社区维护的滑块组件库,提供了跨平台一致的滑块体验,支持丰富的自定义选项和事件回调。

🎯 库简介

基本信息

  • 库名称: @react-native-community/slider
  • 版本信息:
    • 4.4.4 + @react-native-ohos/slider: 支持 RN 0.72 版本
    • 5.0.1 + @react-native-ohos/slider: 支持 RN 0.77 版本
    • 5.1.2 + @react-native-ohos/slider: 支持 RN 0.82 版本
  • 官方仓库: https://github.com/callstack/react-native-slider
  • 鸿蒙仓库: https://atomgit.com/openharmony-sig/rntpc_react-native-slider
  • 主要功能:
    • 🎚️ 范围数值选择
    • 🎨 丰富的样式定制
    • 📱 跨平台一致体验
    • 🔄 步进值支持
    • 🖼️ 自定义滑块图标
    • 📊 步骤标记显示

为什么需要滑块组件?

特性 手动实现 @react-native-community/slider
跨平台一致性 ⚠️ 需分别适配 ✅ 统一体验
步进值支持 ⚠️ 需自行计算 ✅ 内置 step 属性
自定义样式 ❌ 工作量大 ✅ 丰富的 props
事件回调 ⚠️ 需自行处理 ✅ 完整事件支持
滑块图标 ❌ 需额外实现 ✅ 支持自定义图片
HarmonyOS 支持 ❌ 无 ✅ 完善适配

核心功能

功能 说明 HarmonyOS 支持
基础滑块 范围数值选择
步进值 按步长滑动
最小/最大值 设置范围边界
滑动限制 设置上下限
自定义颜色 轨道和滑块颜色
自定义图标 滑块图片
步骤标记 显示刻度
禁用状态 禁止交互
反向滑块 从右到左

兼容性验证

在以下环境验证通过:

  • RNOH: 0.72.90; SDK: HarmonyOS 6.0.0 Release SDK; IDE: DevEco Studio 6.0.2; ROM: 6.0.0

📦 安装步骤

1. 安装依赖

请到三方库的 Releases 发布地址查看配套的版本信息:

三方库版本 发布信息 支持 RN 版本
4.4.4 @react-native-ohos/slider Releases 0.72
5.0.1 @react-native-ohos/slider Releases 0.77
5.1.2 @react-native-ohos/slider Releases 0.82

在这里插入图片描述

# RN 0.72 版本
npm install @react-native-ohos/slider@4.4.4-rc.1

# RN 0.77 版本
npm install @react-native-ohos/slider@5.0.1-rc.1

# RN 0.82 版本
npm install @react-native-ohos/slider@5.1.2-rc.1

# 或者使用 yarn
yarn add @react-native-ohos/slider

2. 验证安装

安装完成后,检查 package.json 文件:

{
  "dependencies": {
    "@react-native-ohos/slider": "^4.4.4-rc.1"
  }
}

3. TypeScript 类型定义配置

由于鸿蒙适配版本的 @react-native-community/slider 库类型定义可能不完整,建议在项目中添加自定义类型定义文件。

src/types 目录下创建 react-native-slider.d.ts 文件:

declare module '@react-native-community/slider' {
  import { Component } from 'react';
  import { ViewStyle, StyleProp } from 'react-native';

  export interface StepMarkerProps {
    stepMarked: boolean;
    currentValue: number;
    index: number;
    min: number;
    max: number;
  }

  export interface SliderProps {
    style?: StyleProp<ViewStyle>;
    value?: number;
    minimumValue?: number;
    maximumValue?: number;
    step?: number;
    minimumTrackTintColor?: string;
    maximumTrackTintColor?: string;
    thumbTintColor?: string;
    upperLimit?: number;
    lowerLimit?: number;
    onValueChange?: (value: number) => void;
    onSlidingStart?: (value: number) => void;
    onSlidingComplete?: (value: number) => void;
    onLowLimit?: () => void;
    onUpperLimit?: () => void;
    disabled?: boolean;
    vertical?: boolean;
    inverted?: boolean;
    trackStyle?: StyleProp<ViewStyle>;
    thumbStyle?: StyleProp<ViewStyle>;
    debugTouchArea?: boolean;
    StepMarker?: React.ComponentType<StepMarkerProps>;
  }

  export default class Slider extends Component<SliderProps> {}
}

然后在 tsconfig.json 中确保包含该类型文件:

{
  "compilerOptions": {
    "typeRoots": ["./node_modules/@types", "./src/types"]
  },
  "include": ["src/**/*", "src/types/**/*"]
}

🔧 HarmonyOS 平台配置 ⭐

Link 配置

版本 是否支持 autolink RN 框架版本
~5.1.2 No 0.82
~5.0.1 No 0.77
~4.4.4 Yes 0.72

如您使用的版本支持 Autolink,并且工程已接入 Autolink,可跳过 ManualLink 配置。

ManualLink: 此步骤为手动配置原生依赖项的指导

1. 在工程根目录的 oh-package.json5 添加 overrides 字段(看自己package.json中的版本)

打开 harmony/oh-package.json5,添加以下配置:

{
  // ... 其他配置
  "overrides": {
    "@rnoh/react-native-openharmony": "0.72.90"
  }
}

2. 引入原生端代码

方式一:通过 HAR 包引入(推荐)

💡 提示:HAR 包位于三方库安装路径的 harmony 文件夹下。

打开 harmony/entry/oh-package.json5,添加以下依赖:

"dependencies": {
  "@react-native-ohos/slider": "file:../../node_modules/@react-native-ohos/slider/harmony/slider.har"
}

点击右上角的 sync 按钮,或者在终端执行:

cd harmony/entry
ohpm install

3. 配置 CMakeLists 和引入 SliderPackage

打开 harmony/entry/src/main/cpp/CMakeLists.txt,添加:

project(rnapp)
cmake_minimum_required(VERSION 3.4.1)
set(RNOH_APP_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
+ set(OH_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules")
set(RNOH_CPP_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../react-native-harmony/harmony/cpp")

add_subdirectory("${RNOH_CPP_DIR}" ./rn)

# 添加 Slider 模块
+ add_subdirectory("${OH_MODULES}/@react-native-ohos/slider/src/main/cpp" ./slider)

add_library(rnoh_app SHARED
    "./PackageProvider.cpp"
    "${RNOH_CPP_DIR}/RNOHAppNapiBridge.cpp"
)

target_link_libraries(rnoh_app PUBLIC rnoh)

# 链接 Slider 库
+ target_link_libraries(rnoh_app PUBLIC rnoh_slider)

打开 harmony/entry/src/main/cpp/PackageProvider.cpp,添加:

#include "RNOH/PackageProvider.h"
+ #include "SliderPackage.h"

using namespace rnoh;

std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(Package::Context ctx) {
    return {
        + std::make_shared<SliderPackage>(ctx),
    };
}

4. 在 ArkTs 侧引入 Slider 组件

找到 function buildCustomRNComponent(),一般位于 entry/src/main/ets/pages/index.etsentry/src/main/ets/rn/LoadBundle.ets,添加:

import { RNCSlider, SLIDER_TYPE } from "@react-native-ohos/slider"

@Builder
export function buildCustomRNComponent(ctx: ComponentBuilderContext) {
  // ... 其他组件
  if (ctx.componentName === SLIDER_TYPE) {
    RNCSlider({
      ctx: ctx.rnComponentContext,
      tag: ctx.tag,
    })
  }
}

💡 提示:本库使用了混合方案,需要添加组件名。

entry/src/main/ets/pages/index.etsentry/src/main/ets/rn/LoadBundle.ets 找到常量 arkTsComponentNames 在其数组里添加组件名:

const arkTsComponentNames: Array<string> = [
  // ... 其他组件名
  SLIDER_TYPE
];

🚀 同步并运行

点击 DevEco Studio 右上角的 sync 按钮,或者在终端执行:

cd harmony/entry
ohpm install

然后编译、运行即可。

📖 API 详解

基础属性

value - 当前值

表示滑块的当前值,可用于以编程方式控制滑块位置。这是滑块组件最核心的属性,决定了滑块手柄的位置。

类型number

默认值0

使用场景

  • 初始化滑块位置
  • 响应用户操作更新显示
  • 程序化控制滑块位置
import Slider from "@react-native-community/slider";

const [value, setValue] = useState(50);

<Slider
  value={value}
  onValueChange={setValue}
/>

// 实际应用:双向绑定
const [sliderValue, setSliderValue] = useState(30);

<View>
  <Text>当前值: {Math.round(sliderValue)}</Text>
  <Slider
    value={sliderValue}
    onValueChange={setSliderValue}
    minimumValue={0}
    maximumValue={100}
  />
</View>

⚠️ 注意:这不是受控组件,您不需要在拖动过程中更新值。滑块内部会自行管理拖动过程中的值,onValueChange 回调会在值变化时通知您。


minimumValue - 最小值

设置滑块的最小值,定义滑动范围的起点。

类型number

默认值0

使用场景

  • 定义数值范围起点
  • 温度滑块(如最低温度)
  • 年龄选择器(如 18 岁)
// 基础用法
<Slider
  minimumValue={0}
  maximumValue={100}
  value={50}
/>

// 温度范围(摄氏度)
<Slider
  minimumValue={-20}
  maximumValue={50}
  value={25}
/>

// 年龄选择
<Slider
  minimumValue={18}
  maximumValue={100}
  value={25}
  step={1}
/>

// 价格范围(元)
<Slider
  minimumValue={0}
  maximumValue={10000}
  value={5000}
  step={100}
/>

maximumValue - 最大值

设置滑块的最大值,定义滑动范围的终点。

类型number

默认值1

使用场景

  • 定义数值范围终点
  • 音量控制(0-100)
  • 亮度调节(0-100)
// 百分比滑块
<Slider
  minimumValue={0}
  maximumValue={100}
  value={50}
/>

// 音量控制
<Slider
  minimumValue={0}
  maximumValue={100}
  value={volume}
  onValueChange={setVolume}
/>

// 评分滑块(1-5星)
<Slider
  minimumValue={1}
  maximumValue={5}
  value={3}
  step={1}
/>

// 时间选择(分钟)
<Slider
  minimumValue={0}
  maximumValue={60}
  value={30}
  step={5}
/>

step - 步进值

设置滑块的步长值,使滑块只能取特定间隔的值。该值应在 0 到 (maximumValue - minimumValue) 之间。

类型number

默认值0(连续滑动)

使用场景

  • 整数选择(step=1)
  • 十分位选择(step=10)
  • 固定选项(如 1-5 星,step=1)
// 步进 10
<Slider
  minimumValue={0}
  maximumValue={100}
  step={10}  // 可选值:0, 10, 20, 30, ..., 100
  value={50}
/>

// 星级评分
<Slider
  minimumValue={1}
  maximumValue={5}
  step={1}  // 可选值:1, 2, 3, 4, 5
  value={3}
/>

// 价格筛选
<Slider
  minimumValue={0}
  maximumValue={10000}
  step={500}  // 每 500 元一档
  value={3000}
/>

// 时间间隔
<Slider
  minimumValue={0}
  maximumValue={60}
  step={15}  // 每 15 分钟一档:0, 15, 30, 45, 60
  value={30}
/>

disabled - 禁用状态

如果为 true,用户将无法移动滑块,滑块会显示为灰色不可交互状态。

类型boolean

默认值false

使用场景

  • 条件未满足时禁用
  • 只读展示
  • 权限控制
// 禁用状态
<Slider
  disabled={true}
  value={50}
/>

// 条件性禁用
const [isPremium, setIsPremium] = useState(false);

<Slider
  disabled={!isPremium}
  value={volume}
  onValueChange={isPremium ? setVolume : undefined}
/>

// 根据网络状态禁用
const [isOnline, setIsOnline] = useState(true);

<Slider
  disabled={!isOnline}
  value={quality}
  onValueChange={setQuality}
/>

inverted - 反向

反转滑块的方向,使最小值在右侧,最大值在左侧。

类型boolean

默认值false

使用场景

  • RTL 语言支持
  • 特殊交互设计
  • 反向数值表示
// 正常方向(默认)
<Slider
  minimumValue={0}
  maximumValue={100}
  value={50}
/>

// 反向滑块
<Slider
  inverted={true}
  minimumValue={0}
  maximumValue={100}
  value={50}
/>

// RTL 语言适配
const isRTL = I18nManager.isRTL;

<Slider
  inverted={isRTL}
  minimumValue={0}
  maximumValue={100}
  value={50}
/>

样式属性

style - 容器样式

用于设置 Slider 组件的样式和布局,可以控制宽度、高度、边距等。

类型ViewStyle

使用场景

  • 设置滑块宽度
  • 控制布局间距
  • 自定义外观
// 固定宽度
<Slider
  style={{ width: 300, height: 40 }}
  value={50}
/>

// 响应式宽度
<Slider
  style={{ width: '100%', height: 40 }}
  value={50}
/>

// 带边距
<Slider
  style={{ width: '100%', marginHorizontal: 16 }}
  value={50}
/>

// 使用 StyleSheet
const styles = StyleSheet.create({
  slider: {
    width: '100%',
    height: 40,
    marginVertical: 10,
  },
});

<Slider style={styles.slider} value={50} />

minimumTrackTintColor - 已滑轨道颜色

设置滑块手柄左侧(已滑动部分)轨道的颜色。

类型string

默认值:系统默认蓝色

使用场景

  • 品牌色适配
  • 状态颜色区分
  • 暗色/亮色主题
// 蓝色已滑轨道
<Slider
  minimumTrackTintColor="#007AFF"
  maximumTrackTintColor="#E5E5EA"
  value={50}
/>

// 根据值动态变色
const getTrackColor = (value: number) => {
  if (value < 30) return '#FF3B30'; // 红色
  if (value < 70) return '#FF9500'; // 橙色
  return '#34C759'; // 绿色
};

<Slider
  minimumTrackTintColor={getTrackColor(value)}
  value={value}
  onValueChange={setValue}
/>

// 主题适配
const isDarkMode = useColorScheme() === 'dark';

<Slider
  minimumTrackTintColor={isDarkMode ? '#0A84FF' : '#007AFF'}
  maximumTrackTintColor={isDarkMode ? '#3A3A3C' : '#E5E5EA'}
  value={50}
/>

maximumTrackTintColor - 未滑轨道颜色

设置滑块手柄右侧(未滑动部分)轨道的颜色。

类型string

默认值:浅灰色

使用场景

  • 背景色适配
  • 视觉层次
  • 主题适配
// 浅色背景
<Slider
  minimumTrackTintColor="#007AFF"
  maximumTrackTintColor="#E5E5EA"
  value={50}
/>

// 深色背景
<Slider
  minimumTrackTintColor="#0A84FF"
  maximumTrackTintColor="#3A3A3C"
  value={50}
/>

// 自定义对比色
<Slider
  minimumTrackTintColor="#34C759"
  maximumTrackTintColor="#F2F2F7"
  value={70}
/>

thumbTintColor - 滑块手柄颜色

设置前景滑块手柄的颜色。手柄是用户拖动的圆形控件。

类型string

默认值:白色(带阴影)

使用场景

  • 品牌色手柄
  • 状态指示
  • 视觉突出
// 蓝色手柄
<Slider
  thumbTintColor="#007AFF"
  value={50}
/>

// 与轨道同色
<Slider
  minimumTrackTintColor="#34C759"
  thumbTintColor="#34C759"
  value={50}
/>

// 状态颜色
<Slider
  thumbTintColor={isActive ? '#007AFF' : '#8E8E93'}
  value={50}
/>

⚠️ 注意:此属性将覆盖设置的 thumbImage 属性。如果同时设置了 thumbTintColorthumbImage,图片会以指定颜色着色。


thumbImage - 滑块手柄图片

为滑块手柄设置自定义图片,实现个性化的滑块外观。

类型ImageSource

使用场景

  • 自定义手柄形状
  • 品牌图标
  • 特殊视觉效果
// 使用本地图片
<Slider
  thumbImage={require('./custom-thumb.png')}
  value={50}
/>

// 使用不同状态的图片
const [isDragging, setIsDragging] = useState(false);

<Slider
  thumbImage={isDragging 
    ? require('./thumb-active.png') 
    : require('./thumb-normal.png')
  }
  value={50}
  onSlidingStart={() => setIsDragging(true)}
  onSlidingComplete={() => setIsDragging(false)}
/>

💡 提示:使用 thumbImage 属性时请确保引入的图片路径地址正确,可检查 harmony/entry/src/main/resources/rawfile/assets 目录下是否被打包至静态资源目录。建议图片尺寸适中,过大可能影响性能。


trackStyle - 轨道样式

自定义轨道的样式(仅限 Android)。

类型ViewStyle

<Slider
  trackStyle={{
    height: 8,
    borderRadius: 4,
  }}
  value={50}
/>

thumbStyle - 手柄样式

自定义手柄的样式(仅限 Android)。

类型ViewStyle

<Slider
  thumbStyle={{
    width: 24,
    height: 24,
    borderRadius: 12,
    backgroundColor: '#007AFF',
  }}
  value={50}
/>

限制属性

lowerLimit - 滑动下限

设置滑动下限,用户将无法滑到该限制以下。适用于需要设置最小阈值的场景。

类型number

默认值minimumValue

使用场景

  • 最小音量限制
  • 最低价格筛选
  • 最小值约束
// 最小只能滑到 20
<Slider
  minimumValue={0}
  maximumValue={100}
  lowerLimit={20}
  value={50}
/>

// 价格筛选 - 最低价格
<Slider
  minimumValue={0}
  maximumValue={10000}
  lowerLimit={100}  // 最低 100 元
  value={500}
  step={100}
/>

// 音量控制 - 保持最小音量
<Slider
  minimumValue={0}
  maximumValue={100}
  lowerLimit={10}  // 最低 10% 音量
  value={volume}
  onValueChange={setVolume}
/>

upperLimit - 滑动上限

设置滑动上限,用户将无法滑到该限制以上。适用于需要设置最大阈值的场景。

类型number

默认值maximumValue

使用场景

  • 最大音量限制
  • 最高价格筛选
  • 最大值约束
// 最大只能滑到 80
<Slider
  minimumValue={0}
  maximumValue={100}
  upperLimit={80}
  value={50}
/>

// 价格筛选 - 最高价格
<Slider
  minimumValue={0}
  maximumValue={10000}
  upperLimit={8000}  // 最高 8000 元
  value={5000}
  step={100}
/>

// 亮度控制 - 保护眼睛
<Slider
  minimumValue={0}
  maximumValue={100}
  upperLimit={80}  // 最高 80% 亮度
  value={brightness}
  onValueChange={setBrightness}
/>

事件属性

onValueChange - 值变化回调

用户拖动滑块时持续调用的回调函数,用于实时获取滑块当前值。

类型(value: number) => void

参数

  • value: 当前滑块值

使用场景

  • 实时更新显示值
  • 联动其他组件
  • 即时反馈
const [value, setValue] = useState(50);

<Slider
  value={value}
  onValueChange={(newValue) => {
    console.log('当前值:', newValue);
    setValue(newValue);
  }}
/>

// 实时显示
<View>
  <Text>当前值: {Math.round(value)}</Text>
  <Slider
    value={value}
    onValueChange={setValue}
    minimumValue={0}
    maximumValue={100}
  />
</View>

// 联动其他组件
const [opacity, setOpacity] = useState(1);

<View>
  <View style={{ opacity, width: 100, height: 100, backgroundColor: 'blue' }} />
  <Slider
    value={opacity}
    onValueChange={setOpacity}
    minimumValue={0}
    maximumValue={1}
  />
</View>

⚠️ 注意:此回调在拖动过程中会频繁调用,避免在其中执行耗时操作。


onSlidingStart - 开始滑动回调

用户开始拖动滑块时调用的回调函数,初始值作为参数传递。

类型(value: number) => void

参数

  • value: 开始滑动时的初始值

使用场景

  • 记录滑动起点
  • 暂停其他操作
  • 显示滑动提示
const [isSliding, setIsSliding] = useState(false);
const [startValue, setStartValue] = useState(0);

<Slider
  value={value}
  onSlidingStart={(startValue) => {
    console.log('开始滑动,初始值:', startValue);
    setIsSliding(true);
    setStartValue(startValue);
  }}
  onSlidingComplete={() => setIsSliding(false)}
/>

// 暂停播放
const [isPlaying, setIsPlaying] = useState(true);

<Slider
  value={currentTime}
  onSlidingStart={() => setIsPlaying(false)}
  onSlidingComplete={(time) => {
    seekTo(time);
    setIsPlaying(true);
  }}
/>

onSlidingComplete - 滑动完成回调

用户释放滑块时调用的回调函数,当前值作为参数传递。这是执行最终操作的最佳时机。

类型(value: number) => void

参数

  • value: 滑动完成时的最终值

使用场景

  • 提交数据到服务器
  • 保存设置
  • 执行搜索
<Slider
  value={value}
  onSlidingComplete={(finalValue) => {
    console.log('滑动完成,最终值:', finalValue);
    // 提交数据到服务器
    submitValue(finalValue);
  }}
/>

// 价格筛选 - 触发搜索
<Slider
  value={maxPrice}
  onSlidingComplete={(price) => {
    // 只在滑动完成时触发搜索
    searchProducts({ maxPrice: price });
  }}
/>

// 保存设置
<Slider
  value={volume}
  onSlidingComplete={(vol) => {
    // 保存音量设置
    AsyncStorage.setItem('volume', vol.toString());
  }}
/>

步骤标记属性(5.0.1+)

StepMarker - 步骤标记组件

用于在滑轨上渲染每个刻度的自定义组件,可根据滑块位置动态改变该刻度的样式。这是实现可视化刻度的强大功能。

类型React.ComponentType<StepMarkerProps>

StepMarkerProps 接口

属性 类型 说明
stepMarked boolean 当前刻度是否为滑块所在位置或之前
currentValue number Slider 当前滑块位置的数值
index number 当前刻度标记实例的渲染索引号
min number Slider 的最小值
max number Slider 的最大值

使用场景

  • 显示刻度值
  • 高亮当前位置
  • 自定义刻度样式
interface MarkerProps {
  stepMarked: boolean;
  currentValue: number;
  index: number;
  min: number;
  max: number;
}

const StepMarker: React.FC<MarkerProps> = ({ stepMarked, index }) => {
  const stepValue = index * 25;
  return (
    <View style={[
      styles.marker,
      stepMarked && styles.markerActive,
    ]}>
      <Text style={[
        styles.markerText,
        stepMarked && styles.markerTextActive,
      ]}>
        {stepValue}
      </Text>
    </View>
  );
};

<Slider
  minimumValue={0}
  maximumValue={100}
  step={25}
  StepMarker={StepMarker}
  value={value}
  onValueChange={setValue}
/>

const styles = StyleSheet.create({
  marker: {
    width: 28,
    height: 28,
    borderRadius: 14,
    backgroundColor: '#E5E5EA',
    justifyContent: 'center',
    alignItems: 'center',
  },
  markerActive: {
    backgroundColor: '#5856D6',
  },
  markerText: {
    fontSize: 10,
    fontWeight: '600',
    color: '#999',
  },
  markerTextActive: {
    color: '#FFF',
  },
});

renderStepNumber - 显示步骤编号

启用步骤编号显示功能,步骤编号将显示在滑轨下方。这是快速实现刻度显示的简便方法。

类型boolean

默认值false

使用场景

  • 快速显示刻度
  • 简单刻度需求
  • 无需自定义样式
// 启用步骤编号
<Slider
  minimumValue={0}
  maximumValue={100}
  step={25}
  renderStepNumber={true}
  value={50}
/>

// 配合 step 使用
<Slider
  minimumValue={0}
  maximumValue={10}
  step={2}
  renderStepNumber={true}
  value={6}
/>
// 显示:0, 2, 4, 6, 8, 10

其他属性

tapToSeek - 点击定位(iOS)

允许点击滑块轨道来设置手柄位置,提供更便捷的交互方式。

类型boolean

默认值false(iOS)

使用场景

  • 快速定位
  • 提升用户体验
<Slider
  tapToSeek={true}
  value={50}
/>

⚠️ 注意:此属性在 HarmonyOS 上暂不支持。


vertical - 垂直方向

如果设置为 true,则将滑块方向更改为垂直。适用于特殊布局需求。

类型boolean

默认值false

使用场景

  • 垂直布局
  • 特殊 UI 设计
  • 音量/亮度控制条
// 垂直滑块
<Slider
  vertical={true}
  style={{ height: 200, width: 40 }}
  value={50}
/>

// 垂直音量控制
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
  <Text>🔈</Text>
  <Slider
    vertical={true}
    style={{ height: 150, width: 40 }}
    value={volume}
    onValueChange={setVolume}
  />
  <Text>🔊</Text>
</View>

maximumTrackImage - 最大轨道图片(iOS)

为未滑动部分的轨道分配图像,仅支持静态图像。

类型ImageSource

<Slider
  maximumTrackImage={require('./track.png')}
  value={50}
/>

⚠️ 注意:此属性在 HarmonyOS 上暂不支持。


minimumTrackImage - 最小轨道图片(iOS)

为已滑动部分的轨道分配图像,仅支持静态图像。

类型ImageSource

<Slider
  minimumTrackImage={require('./track-active.png')}
  value={50}
/>

⚠️ 注意:此属性在 HarmonyOS 上暂不支持。


trackImage - 轨道图片(iOS)

为整个轨道分配单个图像,仅支持静态图像。

类型ImageSource

<Slider
  trackImage={require('./track.png')}
  value={50}
/>

⚠️ 注意:此属性在 HarmonyOS 上暂不支持。


📋 完整示例

在这里插入图片描述

import React, { useState, useEffect } from "react";
import {
  SafeAreaView,
  ScrollView,
  StyleSheet,
  Text,
  View,
  TouchableOpacity,
} from "react-native";
import Slider from "@react-native-community/slider";

const App: React.FC = () => {
  const [basicValue, setBasicValue] = useState(50);
  const [volume, setVolume] = useState(50);
  const [brightness, setBrightness] = useState(70);
  const [stepValue, setStepValue] = useState(50);
  const [isPlaying, setIsPlaying] = useState(false);
  const [currentTime, setCurrentTime] = useState(0);
  const duration = 180;

  useEffect(() => {
    let interval: ReturnType<typeof setInterval>;
    if (isPlaying && currentTime < duration) {
      interval = setInterval(() => {
        setCurrentTime((prev) => Math.min(duration, prev + 1));
      }, 1000);
    }
    return () => clearInterval(interval);
  }, [isPlaying, currentTime]);

  const formatTime = (seconds: number) => {
    const mins = Math.floor(seconds / 60);
    const secs = seconds % 60;
    return `${mins}:${secs.toString().padStart(2, "0")}`;
  };

  const getVolumeIcon = () => {
    if (volume === 0) return "🔇";
    if (volume < 30) return "🔈";
    if (volume < 70) return "🔉";
    return "🔊";
  };

  const StepMarker: React.FC<{
    stepMarked: boolean;
    currentValue: number;
    index: number;
  }> = ({ stepMarked, index }) => (
    <View style={[styles.marker, stepMarked && styles.markerActive]}>
      <Text style={[styles.markerText, stepMarked && styles.markerTextActive]}>
        {index * 25}
      </Text>
    </View>
  );

  return (
    <SafeAreaView style={styles.container}>
      <ScrollView contentContainerStyle={styles.content}>
        <Text style={styles.title}>滑块组件示例</Text>

        {/* 基础滑块 */}
        <View style={styles.card}>
          <Text style={styles.cardTitle}>基础滑块</Text>
          <Text style={styles.valueText}>当前值: {Math.round(basicValue)}</Text>
          <Slider
            style={styles.slider}
            minimumValue={0}
            maximumValue={100}
            value={basicValue}
            onValueChange={setBasicValue}
            minimumTrackTintColor="#007AFF"
            maximumTrackTintColor="#E5E5EA"
            thumbTintColor="#007AFF"
          />
          <View style={styles.rangeLabels}>
            <Text style={styles.rangeLabel}>0</Text>
            <Text style={styles.rangeLabel}>100</Text>
          </View>
        </View>

        {/* 系统控制 */}
        <View style={styles.card}>
          <Text style={styles.cardTitle}>系统控制</Text>
          <View style={styles.controlRow}>
            <Text style={styles.icon}>{getVolumeIcon()}</Text>
            <Text style={styles.controlLabel}>音量</Text>
            <Text style={styles.controlValue}>{Math.round(volume)}%</Text>
          </View>
          <Slider
            style={styles.slider}
            minimumValue={0}
            maximumValue={100}
            value={volume}
            onValueChange={setVolume}
            minimumTrackTintColor="#007AFF"
            maximumTrackTintColor="#E5E5EA"
            thumbTintColor="#007AFF"
          />
          <View style={styles.controlRow}>
            <Text style={styles.icon}>{brightness < 50 ? "🌙" : "☀️"}</Text>
            <Text style={styles.controlLabel}>亮度</Text>
            <Text style={styles.controlValue}>{Math.round(brightness)}%</Text>
          </View>
          <Slider
            style={styles.slider}
            minimumValue={0}
            maximumValue={100}
            value={brightness}
            onValueChange={setBrightness}
            minimumTrackTintColor="#FF9500"
            maximumTrackTintColor="#E5E5EA"
            thumbTintColor="#FF9500"
          />
        </View>

        {/* 带步骤标记的滑块 */}
        <View style={styles.card}>
          <Text style={styles.cardTitle}>带步骤标记的滑块</Text>
          <Text style={styles.valueText}>当前值: {Math.round(stepValue)}</Text>
          <Slider
            style={styles.slider}
            minimumValue={0}
            maximumValue={100}
            step={25}
            value={stepValue}
            onValueChange={setStepValue}
            minimumTrackTintColor="#5856D6"
            maximumTrackTintColor="#E5E5EA"
            thumbTintColor="#5856D6"
            StepMarker={StepMarker}
          />
        </View>

        {/* 播放进度 */}
        <View style={styles.card}>
          <Text style={styles.cardTitle}>播放进度</Text>
          <View style={styles.songInfo}>
            <View style={styles.albumArt}>
              <Text style={styles.albumArtText}>🎵</Text>
            </View>
            <View style={styles.songDetails}>
              <Text style={styles.songTitle}>示例歌曲</Text>
              <Text style={styles.artistName}>未知艺术家</Text>
            </View>
          </View>
          <Slider
            style={styles.slider}
            minimumValue={0}
            maximumValue={duration}
            value={currentTime}
            onValueChange={setCurrentTime}
            onSlidingStart={() => setIsPlaying(false)}
            minimumTrackTintColor="#007AFF"
            maximumTrackTintColor="#E5E5EA"
            thumbTintColor="#007AFF"
          />
          <View style={styles.timeLabels}>
            <Text style={styles.timeLabel}>{formatTime(currentTime)}</Text>
            <Text style={styles.timeLabel}>{formatTime(duration)}</Text>
          </View>
          <View style={styles.controls}>
            <TouchableOpacity
              style={styles.controlButton}
              onPress={() => setCurrentTime(0)}
            >
              <Text style={styles.controlIcon}>⏮️</Text>
            </TouchableOpacity>
            <TouchableOpacity
              style={styles.playButton}
              onPress={() => setIsPlaying(!isPlaying)}
            >
              <Text style={styles.playIcon}>{isPlaying ? "⏸️" : "▶️"}</Text>
            </TouchableOpacity>
            <TouchableOpacity
              style={styles.controlButton}
              onPress={() => {
                setCurrentTime(0);
                setIsPlaying(false);
              }}
            >
              <Text style={styles.controlIcon}>⏭️</Text>
            </TouchableOpacity>
          </View>
        </View>

        {/* 禁用状态 */}
        <View style={styles.card}>
          <Text style={styles.cardTitle}>禁用状态</Text>
          <Slider
            style={styles.slider}
            minimumValue={0}
            maximumValue={100}
            value={30}
            disabled={true}
            minimumTrackTintColor="#007AFF"
            maximumTrackTintColor="#E5E5EA"
            thumbTintColor="#007AFF"
          />
        </View>
      </ScrollView>
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: { flex: 1, backgroundColor: "#F5F5F5" },
  content: { padding: 16 },
  title: { fontSize: 24, fontWeight: "bold", marginBottom: 20, color: "#333", textAlign: "center" },
  card: { backgroundColor: "#FFF", borderRadius: 12, padding: 16, marginBottom: 16 },
  cardTitle: { fontSize: 16, fontWeight: "600", marginBottom: 12, color: "#333" },
  valueText: { fontSize: 14, color: "#007AFF", fontWeight: "500", marginBottom: 8, textAlign: "center" },
  slider: { width: "100%", height: 40 },
  rangeLabels: { flexDirection: "row", justifyContent: "space-between" },
  rangeLabel: { fontSize: 12, color: "#999" },
  controlRow: { flexDirection: "row", alignItems: "center", marginBottom: 8 },
  icon: { fontSize: 20, marginRight: 8 },
  controlLabel: { fontSize: 14, color: "#666", flex: 1 },
  controlValue: { fontSize: 14, fontWeight: "600", color: "#333" },
  marker: { width: 24, height: 24, borderRadius: 12, backgroundColor: "#E5E5EA", justifyContent: "center", alignItems: "center" },
  markerActive: { backgroundColor: "#5856D6" },
  markerText: { fontSize: 10, fontWeight: "600", color: "#999" },
  markerTextActive: { color: "#FFF" },
  songInfo: { flexDirection: "row", alignItems: "center", marginBottom: 16 },
  albumArt: { width: 60, height: 60, borderRadius: 8, backgroundColor: "#F0F0F0", justifyContent: "center", alignItems: "center" },
  albumArtText: { fontSize: 24 },
  songDetails: { marginLeft: 12, flex: 1 },
  songTitle: { fontSize: 16, fontWeight: "600", color: "#333", marginBottom: 2 },
  artistName: { fontSize: 12, color: "#999" },
  timeLabels: { flexDirection: "row", justifyContent: "space-between", marginTop: 4 },
  timeLabel: { fontSize: 12, color: "#999" },
  controls: { flexDirection: "row", justifyContent: "center", alignItems: "center", marginTop: 16 },
  controlButton: { padding: 8 },
  controlIcon: { fontSize: 24 },
  playButton: { backgroundColor: "#007AFF", width: 56, height: 56, borderRadius: 28, justifyContent: "center", alignItems: "center", marginHorizontal: 16 },
  playIcon: { fontSize: 24 },
});

export default App;

⚠️ 遗留问题

  • upperLimit 和 lowerLimit 只对数值生效,滑动限制不生效: issue#2
  • 不支持滑轨设置图片: issue#3
  • 未适配无障碍: issue#9
  • 不支持点击滑块轨道来设置拇指位置,不支持指定最大轨迹图像,不支持分配最小轨道图像: issue#10
Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐