在本教程中,我将向您展示如何仅使用 ES6 和框架本身提供的一些基本组件来创建自定义的 react Native Calendar 组件。
1.创建一个新项目
为了避免在您的计算机上安装 React Native CLI 及其所有依赖项,目前,我建议您使用 Expo 的Snack,这是一个免费的、基于浏览器的 IDE,用于 React Native 应用程序开发。如果您还没有 Expo 帐户,请立即创建一个。
登录 Expo 后,通过切换到Snacks 选项卡并单击Create a Snack 链接来创建一个新的 Snack 项目。
IDE 只需几秒钟即可创建您的项目并为其准备预览设备。准备好后,它应该如下所示:
在继续之前,请确保删除App.js中存在的所有示例代码。
2.创建一个新组件
为了能够在您的项目中使用 React 框架和 React Native 组件,请在App.js 文件import
的开头添加以下语句:
import * as React from 'react'; import * as RN from 'react-native';
您可以通过创建扩展Component
类的类来创建自定义 React 组件。在类中,您必须添加一个名为 的方法render()
,该方法返回jsx代码。以下代码创建了一个名为 的组件MyCalendar
:
class MyCalendar extends React.Component { render() { return ( <RN.View> </RN.View> ); } }
在该render()
方法中,我们当前返回的是一个空View
组件。它将作为我们日历中所有其他组件的容器。
3.创建常量
日历组件需要两个字符串数组:一个存储月份的名称,一个存储星期几的名称。
months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]; weekDays = [ "Sun","Mon","Tue","Wed","Thu","Fri","Sat" ];
接下来,我们需要一个数组来存储每个月的天数。对于二月,设数字为 28。我们将编写代码来处理闰年之后。
nDays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
4.初始化一个状态
为了使我们的日历组件具有交互性,我们必须将状态与其关联。现在,我们只Date
在状态中存储一个对象,初始化为今天的日期。
state = { activeDate: new Date() }
当然,状态是可变的。当用户点击日历中的不同日期时,我们将更改状态以使用新日期。
5.生成矩阵
一个七行七列的矩阵足够大,可以代表一年中的任何一个月。我们将仅将第一行用作标题,在其中存储星期几的名称。要创建和初始化此矩阵,请创建一个名为 的新方法generateMatrix()
。
generateMatrix() { var matrix = []; // Create header matrix[0] = this.weekDays; // More code here }
在开始向矩阵添加天数之前,我们需要确定当前月份的开始日期。为此,首先获取该Date
状态中存储的对象的年份和月份。然后使用这些值创建一个新Date
对象 和1
,该月的第一天。通过调用getDay()
这个新对象的方法,您可以得到该月的第一天。
var year = this.state.activeDate.getFullYear(); var month = this.state.activeDate.getMonth(); var firstDay = new Date(year, month, 1).getDay();
我们不能直接使用nDays
数组来确定当前月份的天数。如果月份是二月,我们需要在处理闰年时手动添加额外的一天。就是这样:
var maxDays = this.nDays[month]; if (month == 1) { // February if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) { maxDays += 1; } }
至此,我们拥有了填充矩阵其余部分所需的所有数据。以下代码向您展示了如何使用计数器、两个for
循环和两个简单if
条件来执行此操作:
var counter = 1; for (var row = 1; row < 7; row++) { matrix[row] = []; for (var col = 0; col < 7; col++) { matrix[row][col] = -1; if (row == 1 && col >= firstDay) { // Fill in rows only after the first day of the month matrix[row][col] = counter++; } else if (row > 1 && counter <= maxDays) { // Fill in rows only if the counter's not greater than // the number of days in the month matrix[row][col] = counter++; } } } return matrix;
请注意,您需要显式初始化 7 x 7 矩阵的每个元素。如果您忘记这样做,则第一行或最后一行的元素可能少于七个。map()
在使用该方法循环矩阵时,这可能会导致问题。
6.渲染一个月
回到render()
方法内部,我们现在必须渲染我们创建的矩阵。所以调用 generateMatrix()
里面的方法。
var matrix = this.generateMatrix();
Text
接下来,让我们通过在当前为空的组件中添加一个组件来显示年份和当前月份的名称View
。或者,您可以使用该 style
道具为文本添加样式。
<RN.Text style={{ fontWeight: 'bold', fontSize: 18, textAlign: 'center' }}> {this.months[this.state.activeDate.getMonth()]} {this.state.activeDate.getFullYear()} </RN.Text>
我们将使用flexbox来渲染矩阵每一行的内容。更准确地说,对于每一行,我们将使用一个View
组件,其flex
和 flexDirection
参数分别设置为1
和row
。此外,为了确保该行的所有项目具有相同的宽度,我们将 flexbox 的justifyContent
参数设置为space-around
.
此外,为了显示矩阵的各个元素,我们将Text
再次使用组件。通过修改负责第一行元素backgroundColor
的组件的属性 Text
,我们可以使表头突出。同样,如果要突出显示星期日,请使用 负责第一列元素color
的组件的属性 。Text
我们的日历应该能够突出显示今天的日期,或者用户选择的日期。fontWeight
因此,让我们为每个Text
组件关联一个属性。bold
只要其内容与我们状态 activeDate
变量中的日期匹配,我们就会将其设置为。
以下代码向您展示了如何使用该map()
方法作为 for
循环的替代方法,同时为矩阵的内容生成组件层次结构:
var rows = []; rows = matrix.map((row, rowIndex) => { var rowItems = row.map((item, colIndex) => { return ( <RN.Text style={{ flex: 1, height: 18, textAlign: 'center', // Highlight header backgroundColor: rowIndex == 0 ? '#ddd' : '#fff', // Highlight Sundays color: colIndex == 0 ? '#a00' : '#000', // Highlight current date fontWeight: item == this.state.activeDate.getDate() ? 'bold': '' }} onPress={() => this._onPress(item)}> {item != -1 ? item : ''} </RN.Text> ); }); return ( <RN.View style={{ flex: 1, flexDirection: 'row', padding: 15, justifyContent: 'space-around', alignItems: 'center', }}> {rowItems} </RN.View> ); });
要实际渲染矩阵,您现在必须包含rows
在该 render()
方法返回的 JSX 中。Text
所以在负责显示年份和月份名称的组件下面添加如下代码:
{ rows }
您可能已经注意到,我们为每个 显示日期的组件关联了一个onPress
事件处理程序 。 每当用户单击日期时Text
,我们将使用它来更新 变量。activeDate
当然,请记住忽略 Text
空的或负责星期几名称的组件。
因此,将以下方法添加到您的类中:
_onPress = (item) => { this.setState(() => { if (!item.match && item != -1) { this.state.activeDate.setDate(item); return this.state; } }); };
7.更改月份
我们的日历组件将有两个标记为Next 和Previous的按钮。这些按钮在按下时应该允许用户从一个月移动到另一个月。正如您可能已经猜到的那样,在他们的事件处理程序中,我们需要做的就是获取activeDate
对象并将其月份递增或递减1
.
因此,在方法返回的 JSX 末尾添加以下代码 render()
:
<RN.Button title="Previous" onPress={() => this.changeMonth(-1)}/> <RN.Button title="Next" onPress={() => this.changeMonth(+1)}/>
接下来,创建changeMonth()
方法。在它里面,你必须先调用 setState()
方法,然后再调用setMonth()
方法来更新 activeDate
对象。
changeMonth = (n) => { this.setState(() => { this.state.activeDate.setMonth( this.state.activeDate.getMonth() + n ) return this.state; }); }
8.使用组件
我们的 React Native 日历组件已准备就绪。要使用它,只需将它添加到 您的 类的render()
方法中。App
export default class App extends React.Component { render() { return <MyCalendar/>; } }
如果您现在运行您的项目,您应该会看到如下所示的日历:
结论
您现在知道如何创建和使用自定义的 React Native 日历组件,而不依赖于任何第三方包。我们今天创建的组件是交互式的、可扩展的,并且可以在任何应用程序中使用,只需很少的更改。随意添加更多样式和功能。
要了解更多关于 React Native 组件的信息,请参阅官方文档。并查看我们关于 React Native 应用程序开发的其他一些帖子!