React Native中<Text/>/<HtmlView/>自定义文本选择菜单及点击事件
嘿,这个需求我刚好折腾过,给你分享两种组件的实现思路,亲测可行!
在React Native中自定义文本选择菜单的实现方案
不管是用<Text>还是<HtmlView>(或者更推荐的react-native-render-html),都能实现自定义文本选择菜单并触发保存到数据库的逻辑,下面分情况给你拆解:
一、针对<Text>组件的实现
如果是原生<Text>组件,我们可以利用它的onSelectionChange事件监听文本选中状态,再结合自定义弹窗或者原生上下文菜单来实现需求。
方案1:自定义弹窗菜单(灵活可控)
这种方式可以完全自定义菜单的样式和位置,适合需要定制UI的场景:
import React, { useState, useRef } from 'react'; import { View, Text, TouchableOpacity, StyleSheet } from 'react-native'; const CustomTextSelection = () => { const [selectedText, setSelectedText] = useState(''); const [menuVisible, setMenuVisible] = useState(false); const [menuPosition, setMenuPosition] = useState({ x: 0, y: 0 }); const textRef = useRef(null); // 监听文本选择变化 const handleSelectionChange = (event) => { const { selection, text } = event.nativeEvent; // 只有选中范围不为空时才触发 if (selection.start !== selection.end) { const pickedText = text.substring(selection.start, selection.end); setSelectedText(pickedText); // 计算菜单弹出位置(这里只是简单示例,实际可以根据选中位置更精准计算) textRef.current.measure((x, y, width, height, pageX, pageY) => { setMenuPosition({ x: pageX + 20, y: pageY - 50 }); setMenuVisible(true); }); } else { // 取消选中就隐藏菜单 setMenuVisible(false); } }; // 保存到数据库的逻辑 const saveToDatabase = () => { // 这里替换成你的数据库操作,比如用Realm、AsyncStorage或者后端接口 console.log('已选中文本并保存:', selectedText); setMenuVisible(false); }; return ( <View style={styles.container}> <Text ref={textRef} style={styles.text} selectable onSelectionChange={handleSelectionChange} > 试试选中这段文本,看看自定义菜单会不会弹出来~选中后点击保存就能把文本存到数据库啦! </Text> {/* 自定义菜单弹窗 */} {menuVisible && ( <View style={[styles.menuBox, { left: menuPosition.x, top: menuPosition.y }]}> <TouchableOpacity style={styles.menuItem} onPress={saveToDatabase}> <Text style={styles.menuText}>保存到数据库</Text> </TouchableOpacity> </View> )} </View> ); }; const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', paddingHorizontal: 20, }, text: { fontSize: 18, lineHeight: 26, color: '#333', }, menuBox: { position: 'absolute', backgroundColor: '#fff', borderRadius: 8, shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.2, shadowRadius: 4, elevation: 5, // Android阴影 }, menuItem: { paddingHorizontal: 18, paddingVertical: 10, }, menuText: { fontSize: 16, color: '#1890ff', fontWeight: '500', }, }); export default CustomTextSelection;
方案2:原生上下文菜单(贴合系统风格)
如果想让菜单和系统自带的选择菜单风格统一,可以用第三方库react-native-context-menu-view来实现:
首先安装依赖:
npm install react-native-context-menu-view # 或者 yarn add react-native-context-menu-view
然后实现代码:
import React, { useState } from 'react'; import { View, Text, StyleSheet } from 'react-native'; import ContextMenu from 'react-native-context-menu-view'; const NativeStyleSelection = () => { const [selectedText, setSelectedText] = useState(''); const handleSelectionChange = (event) => { const { selection, text } = event.nativeEvent; if (selection.start !== selection.end) { setSelectedText(text.substring(selection.start, selection.end)); } }; const saveToDatabase = () => { console.log('通过原生菜单保存文本:', selectedText); // 这里写你的数据库逻辑 }; return ( <View style={styles.container}> <ContextMenu actions={[{ title: '保存到数据库', id: 'save' }]} onPress={(event) => { if (event.nativeEvent.action.id === 'save') { saveToDatabase(); } }} > <Text style={styles.text} selectable onSelectionChange={handleSelectionChange} > 长按选中这段文本,会弹出和系统风格一致的菜单,点击保存即可存到数据库~ </Text> </ContextMenu> </View> ); }; const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', paddingHorizontal: 20, }, text: { fontSize: 18, lineHeight: 26, color: '#333', }, }); export default NativeStyleSelection;
二、针对<HtmlView>/HTML渲染内容的实现
如果是渲染HTML内容,原生<HtmlView>的事件监听比较有限,更推荐用react-native-render-html或者WebView来实现:
方案1:用WebView渲染HTML并监听选择事件
通过WebView注入JS代码来监听网页内的文本选择,再通过postMessage和RN通信:
import React from 'react'; import { View, WebView, StyleSheet } from 'react-native'; const HtmlTextSelection = () => { // 保存到数据库的逻辑 const saveToDatabase = (selectedText) => { console.log('保存HTML中的文本:', selectedText); // 替换成你的数据库操作 }; // 注入到WebView的JS代码,监听文本选择 const injectedJS = ` document.addEventListener('selectionchange', function() { const selection = window.getSelection(); const pickedText = selection.toString().trim(); if (pickedText) { // 把选中的文本发送给RN window.ReactNativeWebView.postMessage(pickedText); } }); true; `; // 你要渲染的HTML内容 const htmlContent = ` <div style="font-size: 18px; line-height: 26px; padding: 20px;"> <p>这是HTML渲染的内容,选中这段文本试试~</p> <p>选中后会自动触发保存逻辑,或者你也可以在RN端弹出自定义菜单再保存~</p> </div> `; return ( <View style={styles.container}> <WebView style={styles.webView} source={{ html: htmlContent }} injectedJavaScript={injectedJS} onMessage={(event) => { const selectedText = event.nativeEvent.data; saveToDatabase(selectedText); }} javaScriptEnabled={true} /> </View> ); }; const styles = StyleSheet.create({ container: { flex: 1, }, webView: { flex: 1, }, }); export default HtmlTextSelection;
方案2:用react-native-render-html自定义Text渲染
如果不想用WebView,可以用react-native-render-html,自定义Text组件的渲染逻辑,把前面<Text>组件的选择逻辑套进去:
首先安装依赖:
npm install react-native-render-html
然后实现代码:
import React from 'react'; import { View, StyleSheet } from 'react-native'; import RenderHtml from 'react-native-render-html'; import CustomSelectableText from './CustomTextSelection'; // 就是前面写的自定义Text组件 const HtmlWithCustomSelection = () => { const htmlContent = ` <div style="font-size: 18px; line-height: 26px;"> <p>这是用react-native-render-html渲染的内容,支持自定义文本选择菜单~</p> <p>选中任意文本,就能看到我们自定义的保存菜单啦!</p> </div> `; // 自定义Text渲染器,替换默认的Text组件 const customRenderers = { text: (props) => <CustomSelectableText {...props} />, }; return ( <View style={styles.container}> <RenderHtml contentWidth={350} source={{ html: htmlContent }} renderers={customRenderers} /> </View> ); }; const styles = StyleSheet.create({ container: { flex: 1, paddingHorizontal: 20, paddingTop: 20, }, }); export default HtmlWithCustomSelection;
内容的提问来源于stack exchange,提问作者Aashutosh Kumar
相关产品推荐
相关产品推荐

