Skip to content

一,RN 初识

是什么?

react 是封装了一套 js 来写前端代码,有自己的规则和语法,编写更简单,使用的还是 html 的标签如 div,image,input 这些, 开发者可以自己写一下自己常用的基础组件,比如按钮,海报,表格,这些然后在需要的时候引入这些组件就可以使用;当然社区里面网络上也有很多别人写的组件,可以直接引入使用; 那 ReactNative 呢,它的特点是,官方用 react 已经写好了基础组件,如 View 区域,Text 文本等这些,让我们根据语法规则在需要的地方使用即可; 所以主要有三点:第一:react 的语法规则,第二:官方已有的组件,熟悉其属性;第三:软终端中的自定义的组件;

RN 官网:https://reactnative.cn/docs/components-and-apis

从 html->react->RN 的编写变化?

以点击增加数量为例, html 实现

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<script>
 let count=0
 function handleClick() {
  count++
  console.log('test',count)
	//将值设置到dom里去,需要直接操作dom
	let textObj=document.getElementById("countId");
	console.log('textObj',textObj)
	textObj.innerHTML=count
	<!-- textObj.style.color="red"; -->
  }
</script>
<body>
  <h1>点击增加数量</h1>
   <button onclick="handleClick()">
        Clicked <text id='countId'>count</text> times
   </button>
</body>
</html>

react 实现 app.js

import { useState } from 'react';

function default clickUpdate() {
  const [count, setCount] = useState(0);

  function handleClick() {
    setCount(count + 1);
  }

  return (
    <div>
     <h1>点击增加数量</h1>
      <button onClick={handleClick}>
        Clicked {count} times
      </button>
    </div>
  );
}

rn 实现

app.js

import React,{ useState } from 'react';
import {Text, View,Button} from 'react-native';

function default clickUpdate() {
  const [count, setCount] = useState(0);

  function handleClick() {
    setCount(count + 1);
  }

  return (
    <View>
     <Text>点击增加数量<//Text>
     <Button
      onPress={handleClick}
      title={ `Clicked ${count} times`}
      color="#841584"
      ></Button>
    </View>
  );
}

RN 可以做什么?

rn 是特点是一次编辑多处使用的,多平台适用的语音;可用户安桌,苹果和 web 等 你编写的代码可以打包成 js,这是咱们电视端的方式 可以编译成一个独立的 apk,这是手机端模式 也可以在 android 项目里直接编写对应的代码; 在安卓端,实质就是,rn 将我们写的 view 用安卓的 view 画出来,都是一一对应的,它的作用就像个翻译; 特点是:一般情况下无需升级 apk 既可以升级 rn 服务;特殊情况是指,需要增加新的依赖包,比如视频 sdk; 然后就是需要模拟器;

那么我们怎么在编辑里面使用官方组件?

咱们的项目结构是这样的,在线环境

https://code.ipanel.cn/user/view/workbench/appview/projectApp
点击指定的项目进入在线编辑器, 或者创建应用->电视 js-app->简版应用向导->然后按需求填写,应用来源可以是个人也可以是项目号,都行;

项目结构

简单示例

首先在 app/page/service 里面定义一个文件名比如:portal.js

javascript
import React from "react";
import { Text, View } from "react-native";

const WelcomeScreen = () => {
  return (
    <View>
      <Text>welcom to react nataive</Text>
      <Text>Edit App.js to change this screen and turn it into your app.</Text>
      <Text>See Your Changes</Text>
    </View>
  );
};
export default WelcomeScreen;

怎么样让这个页面组件显示出来?在 app.js 中引入这个文件 我们的 app 就是系统会去加载的东西,引入到这里来编译的时候就会把引入的文件一起编译;

javascript
import React, { useEffect } from "react";
import WelcomeScreen from "./service/portal";
console.log("[App.js]---isHermes=" + isHermes());
const App = (props) => {
  useEffect(() => {
    //在初始化render之后只执行一次,在这个方法内,可以访问任何组件,、、
    console.log("[App.js]---componentDidMount");
    return () => {
      console.log("[App.js]---componentWillUnmount---");
    };
  }, []);

  return <WelcomeScreen></WelcomeScreen>;
};

export default App;

在 index 中注册 app,index.js 是项目注册的文件,默认就是 index.js 文件

import {AppRegistry} from 'react-native';
import App from './app/pages/App'; // 应用入口文件(可根据目录结构修改)

//这个就会去注册这个应用,然后rn解析器就知道,有这么一个应用
AppRegistry.registerComponent('rn_post', () => App);

注意线上自动创建的项目,需要在根目录下先执行 npm install,把依赖先下载下来;

然后运行,在项目根目录下执行:npm start --reset-cache 服务运行起来

找到项目里的 debug.py 文件,将 DEVICE_ID 的值设置为,中国广电 apk 里 我的-电视信息-appId 的值

在项目根目录下,执行:npm run debug,即可在软终端里看到页面效果。

补充:如果没有电视机或者机顶盒,那么在电脑上安装雷电等 android 模拟器,然后在模拟器上安装中国广电 apk。

二, 核心组件

基础组件

View

比如在 android 平台上,对应就是 android.view.View 视图,web 上就是 div 最基础的组件,支持布局,样式和一些交互响应,可以有任意多个任意类型的子视图; 可以理解成矩形区域或者模块; 写法和写一个 div 是类似的,包括属性也是类似可以,只是写法不一样; 注意这里的 style 写法和 css 里的略有区别,用的是驼峰式的名称,没有单位,单位表示的是与设备像素密度无关的逻辑像素点,会根据当前屏幕去适配; 而且必须是一个对象,或者对象数组;

javascript
<View
  style={{
    width: 100,
    height: 100,
    backgroundColor: "#ff0000",
    borderColor: "#0000ff",
    borderWidth: 2,
  }}
></View>

常用属性 onLayout 在组件挂载或者布局变化的时候会调用, 注意区分组件属性和样式属性

javascript
<View
  onLayout={(e) => {
    LOG("scrollHeight:" + JSON.stringify(e.nativeEvent.layout.height));
    this.contentHeight = e.nativeEvent.layout.height;
  }}
  style={{
    width: 100,
    height: 100,
    backgroundColor: "#ff0000",
    borderColor: "#0000ff",
    borderWidth: 1,
  }}
></View>

view

这是一个区域,但是没有内容,我们需要加上内容,很简单文本内容; 在 view 里增加子元素,默认的布局方式是从上到下,模块布局,像乐高积木叠加

Text

文本必须放在 Text 组件中;text 可放在 view 元素里,这个时候它也是一个盒子,可以设置 view 类似的属性,还可以设置文本的属性;

javascript
// 错误的做法:会导致一个错误。<View>下不能直接放一段文本。
<View>
  一些文本
</View>

// 正确的做法
<View>
  <Text style={{width:100,height:100,backgroundColor:'#ff0000',color:'#ff0000',fontWeight:500,fontSize:50}}>
    标题
  </Text>
    <Text>
    内容
  </Text>
</View>

可以单独使用, 还可以在里面嵌套 text 子元素,这个时候的文本属性就可以实现继承,但是这个时候内部 text 不是块熟性,而是行属性; 比如有的时候显示一段话,其中某些字符需要加粗 title First part and second part

javascript
<Text style={{ color: "#ff0000", fontSize: 30 }}>
  title
  <Text style={{ fontWeight: "bold" }}>First part and </Text>
  <Text>second part</Text>
</Text>

text 常用属性 onLayout numberOfLines 用来当文本过长的时候裁剪文本。包括折叠产生的换行在内,总的行数不会超过这个属性的限制。此属性一般和 ellipsizeMode 搭配使用 ellipsizeMode 表示当 Text 组件无法全部显示需要显示的字符串时如何用省略号进行修饰

head - 从文本内容头部截取显示省略号。例如: "...efg"
middle - 在文本内容中间截取显示省略号。例如: "ab...yz"
tail - 从文本内容尾部截取显示省略号。例如: "abcd..."
clip - 不显示省略号,直接从尾部截断。

样式属性, 字体的大小,颜色,是否加粗等

javascript
<Text
  styles={{
    backgroundColor: "#DCDCDC",
    marginTop: utils.px(10),
    fontSize: utils.px(28),
    paddingLeft: utils.px(12),
    borderRadius: utils.px(12),
    paddingHorizontal: utils.px(10),
    paddingVertical: utils.px(10),
    alignSelf: "flex-start",
    color: "#1C1C1C",
    textAlign: "left",
    textAlignVertical: "center",
    includeFontPadding: false,
    textDecorationLine: "underline",
  }}
></Text>

Image 和 BackgroundImage

需要显示图片的时候使用,一般使用方式,网络图片(如接口返回的)或者本地图片 如果是网络图片必须设置宽高,

javascript
 <Image
	  style={{width: utils.px(960),
		height: utils.px(1080)}}
	  source={require('../../assets/img/reserve.png')}>
</Image>
<Image
	  style={{width: utils.px(960),
		height: utils.px(1080)}}
	  resizeMode='contain'
	  source={uri: 'https://reactnative.dev/img/tiny_logo.png'}>
</Image>

需要注意的属性, resizeMode,有的时候咱们设定的宽高和实际图片不一样,这个时候图片是拉升怎么拉升还是留有空白,就是通过这个设置 如果想要图片按照本来的尺寸显示,那就需要在图片下载下来之后去设置它的宽高 onLoad={({nativeEvent: {source: {width, height}}}) => setImageRealSize({width, height})}

cover: 在保持图片宽高比的前提下缩放图片,图片完全覆盖甚至超出容器,容器中不留任何空白。 contain: 在保持图片宽高比的前提下缩放图片,图片完全被包裹在容器中,容器中可能留有空白。

stretch: 拉伸图片且不维持宽高比,直到宽高都刚好填满容器。

repeat: 重复平铺图片直到填满容器。

center: 居中不拉伸。

javascript
<Image
  onLoad={({
    nativeEvent: {
      source: { width, height },
    },
  }) => {
    console.log("[PosterImg]", width, height);
    this.setState({ imgHeight: height });
  }}
  style={{
    width: utils.px(1080),
    height: utils.px(this.state.imgHeight),
    resizeMode: "stretch",
  }}
  source={{
    uri: imgSource,
  }}
></Image>

BackgroundImage 背景图片,上面可以有其他内容,即此元素可以在里面有子元素,这是和 image 最大的区别

javascript
<BackgroundImage
  style={{
    width: utils.px(1080),
    height: utils.px(this.state.imgHeight),
    resizeMode: "stretch",
  }}
  source={{
    uri: imgSource,
  }}
>
  <View>
    <Text>{"标题"}</Text>
  </View>
  <View>
    <Text>{"内容"}</Text>
  </View>
</BackgroundImage>

ScrollView

滚动,顾名思义哈,什么时候需要滚动,是有限区域需要显示超过这个区域内容的时候需要滚动, 所以必须有一个确定的高度才能正常工作,因为它实际上所做的就是将一系列不确定高度的子组件装进一个确定高度的容器(通过滚动操作) 怎么设置它的高度? 直接给它设置高度(不建议),

javascript
<ScrollView
  style={{
    height: utils.px(630),
    width: utils.px(1250),
    paddingTop: utils.px(10),
    paddingLeft: utils.px(20),
  }}
  showsVerticalScrollIndicator={false}
>
  <View
    style={{
      height: utils.px(1000),
    }}
  ></View>
</ScrollView>

要么确定所在的父容器都有确定的高度。然后 ScrollView 设置 flex: 1 以使其自动填充父容器的空余空间;

javascript
<View style={{ height: utils.px(69) }}>
  <ScrollView
    ref={(ref) => {
      this.scrollRef = ref;
    }}
    showsVerticalScrollIndicator={false}
    style={{ flex: 1 }}
  >
    <View
      style={{
        height: utils.px(1000),
      }}
    ></View>
  </ScrollView>
</View>

常用的方法 scrollTo({x: 0, y: 0, duration: 500}) 用来控制滚动的距离和时间。 使用场景:观看记录(有限记录,比如最新历史记录 10 条)

flatList

是可以滚动的,和 scrollView 的最大区别是,scrollView 是一次性显示所有的子元素; flatList 更灵活,只显示当前需要展示的内容,动态的去获取下一页需要展示的内容,并且可以设置初始展示几页内容,还可以设置一行展示几列; 使用场景:观看记录(所有历史记录)

javascript
<FlatList
  focusable={false}
  style={styles.content}
  focus_finder={true}
  // focusable={this.props.curTypeFlag}
  blocked_edges={["right", "up", "down"]}
  focus_selector={{
    theme_focus: "main",
    on_top: true,
    force_front: true,
    zoom: 0.05,
  }}
  data={liveHistoryList}
  renderItem={this.renderItem}
  keyExtractor={(item, index) => index.toString()}
  numColumns={5}
  onEndReachedThreshold={0.6}
  onEndReached={this.fetchLiveWatchList}
/>

交互组件

按钮

交互的一般是按钮这些,但是官方的这些按钮,就像手机上应用那样是无法区分当前焦点在那个按钮上的,是没有焦点状态显示; 电视端因为焦点等因素不使用官方的按钮, 而是使用咱们软终端里面自定义的组件,后续会讲; 这里主要熟悉按钮的一些通用熟悉和方法

javascript
import { Alert, Button } from "react-native";
<Button
  onPress={() => {
    Alert.alert("点击按钮");
  }}
  title="点我"
  color="#841584"
></Button>;

可获得焦点组件

1,TouchableWithoutFeedback

一般不用这个焦点组件,因为没有响应触屏后的视觉反馈;如可用在空白区域的点击; 仅且只支持有一个子节点; 常用属性

onPress,onPressIn,onPressOut;
onLongPress;
onFocus,onBlur;
onLayout:加载完了之后获取组件的位置宽度等(xy,h,w)

delayLongPress
delayPress;

hitSlop,按钮外延范围,就是按钮外的部分区域点击也有效,但不会超过父元素的边界;
pressrentoutOffect,手指移开多远距离之后,会不再激活按

disable;不可点击
javascript
import { Alert, Button } from "react-native";
const TouchableExample = () => {
  const [count, setCount] = useState(0);
  const onPress = () => {
    setCount(count + 1);
  };
  <TouchableWithoutFeedback onPress={onPress}>
    <View>
      <Text>Touch Here</Text>
    </View>
  </TouchableWithoutFeedback>;
};
2,TouchableHightlight

继承 TouchableWithoutFeedback,所以有其所有属性;

当按下的时候,封装的视图的不透明度会降低,同时会有一个底层的颜色透过而被用户看到,使得视图变暗或变亮。

特有属性


underlayColor,有触摸操作时显示出来的底层的颜色。
activeOpacity:获得焦点时底色背景的透明度,0 到 1 之间,默认值为 0.85

属性值为函数
onShowUnderlay,
onHideUnderlay,底层的颜色被隐藏的时候调用

hasTVPreferredFocus  默认获得焦点
javascript
import React, { useState } from "react";
import { Text, View, TouchableHighlight } from "react-native";

const TouchableExample = () => {
  const [count, setCount] = useState(0);
  const onPress = () => {
    setCount(count + 1);
  };
  return (
    <View>
      <TouchableHighlight
        onPress={onPress}
        onFocus={() => {
          console.log("button onfocus");
        }}
        onBlur={() => {
          console.log("button onBlue");
        }}
        hasTVPreferredFocus={true}
        activeOpacity={0.3}
        underlayColor="#CDAD00"
      >
        <View style={{ width: 100, height: 50, backgroundColor: "#DDDDDD" }}>
          <Text style={{ color: "#FF4500", fontSize: 15 }}>Touch Here</Text>
          <Text style={{ color: "#00FA9A", fontSize: 30 }}>{count}</Text>
        </View>
      </TouchableHighlight>
    </View>
  );
};
export default TouchableExample;
3,TouchableOpacity

继承 TouchableWithoutFeedback,所以有其所有属性;

当按下的时候,封装的视图的不透明度会降低。不透明度的变化是通过把子元素封装在一个 Animated.View 中来实现的

与 TouchableHightlight 有什么区别? 并没有额外的底色颜色变化

这些焦点组件在手机端用的较多,电视端由于焦点的特殊性基本不用。

其他

Modal 弹框 弹框里可以是任意内容,是个独立的页面,且在最上层, visible:决定 modal 是否显示 onRequestClose: 用户按下 Android 设备上的后退按键时回调 onShow:会在 modal 显示时调用;比如定时关闭,当弹框显示的时候,回调 2s 后关闭 transparent:遮罩层,true 的时候弹出一个透明背景层的 modal onDismiss: modal 被关闭时调用 应用场景:最简单提示’保存成功‘

javascript
<Modal
  visible={modalVisible}
  onRequestClose={() => {
    setModalVisible(false);
  }}
  transparent={true}
>
  <View>
    <View
      style={{
        width: 600,
        height: 250,
        backgroundColor: "rgba(255,255,255,0.3)",
      }}
    >
      <Text style={{ fontSize: 30, color: "#ff0000" }}>{"测试内容"}</Text>
    </View>
  </View>
</Modal>

可以把这种弹框提示写成一个通用的组件,在需要的地方引入即可;

有了这些组件之后,在一个页面怎么使用?

简单示例

作业:按照此图完成页面,

屏幕大小:1920*1080 背景色 '#3181A5'

有一张图片在右下角

标题:字号 54 字号 48 颜色 #FFFFFF,

内容:字号 48 颜色 #FFFFFF,居中显示

按钮: width: 350,height:120, 字号 48

按钮常态:背景色 rgba(255,255,255,0.3),字体颜色#FFFFFF

按钮获焦:背景色 #FFFFFF,字体颜色#333333

交互效果: 点击重新连接弹出弹框,弹框内容显示‘即将重新连接,请稍后’

右下角图片 右下角图片大小: width: 960, height: 1080,