使用React Native图表套件来实现数据的可视化

当我们在应用中的力量在于数据时,重要的是我们要把它以一种漂亮的方式呈现给用户。这就是图表库发挥作用的地方。它们允许你以一种吸引人的方式向用户展示数据,从而使他们更多地使用你的应用程序。

在本教程中,我们将看看React Native Chart Kit,一个用于React Native的图表库。我们将建立一个使用它的应用程序,这样你就可以有一个实际的例子来工作。

应用程序概述。构建一个财务跟踪应用程序

我们将讨论的应用程序是一个财务跟踪应用程序。

该应用程序有四个屏幕。其中三个用于用户输入,而最后一个用于图表。我们将主要关注有图表的屏幕,但我们也将简单地浏览其他屏幕。

这些是三个输入屏幕。

  • 创建类别--每笔交易的总体分类是一个类别。一个类别可以是收入或支出。例如,用户可以创建 "互联网账单 "作为支出类别,或 "主要工作工资 "作为收入类别。

Create Category Page RN Finance Tracker

  • 创建基金 - 允许用户输入他们不同的基金商店。例子包括:实物钱包、电子钱包、储蓄银行账户、股票和加密货币

Create Fund Page RN Finance Tracker

  • 创建交易 - 这是用户输入他们所有交易的地方,以便他们可以跟踪他们的财务状况。例如,当他们在月底收到工资时,他们可以为其创建一个交易。
    Create Transaction Page RN Finance Tracker

最后一个屏幕是报告屏幕,在这里向用户展示图表。

Report Screen RN Finance Tracker

设置项目

设置项目的最简单方法是启动一个新的React Native项目。

npx react-native init RNChartKitFinanceApp

复制代码

一旦完成,从GitHub repo中复制文件。我在这个应用中使用的React Native版本是0.65.1 。如果当你读到这篇文章时,已经有了更多的新版本,请务必使用该版本。你可以通过替换你的package.json 文件中的相应版本来做到这一点。

该应用程序使用以下软件包。如果你想跟着建立一个财务追踪应用,玩玩React Native Chart Kit,一定要按照他们网站上相应的安装步骤。

上面只提到了有原生依赖关系的包。请查看 repo 中的package.json 文件,以获得完整的依赖性列表。

构建应用程序

这不会是你常见的从头开始构建的教程,这样我们就可以把重点放在主要话题上。React Native Chart Kit。

我们不会再去看创建类别、资金和交易的屏幕。我们也不会再讨论React Navigation和React Native Paper的设置。你可以简单地检查一下这个版本。

相反,我们将只关注报告屏幕和获取数据的查询方式。

SQLite数据库

让我们先来看看我们是如何使用数据库的。如前所述,我们使用React Native SQLite存储库来本地管理我们自己的SQLite数据库。

我们只需要该库的三个方法:enablePromise()openDatabase()

// data/db-service.js
import {
  enablePromise,
  openDatabase,
} from 'react-native-sqlite-storage';

复制代码

enablePromise() 是为了在执行不同方法时能够处理承诺。

enablePromise(true);

复制代码

openDatabase() 是为了在操作系统分配给应用程序的本地存储上创建和打开数据库文件。如果该文件已经存在,那么它将简单地打开该文件。

export const getDBConnection = async () => {
  return openDatabase({name: 'finance-data.db', location: 'default'});
};

复制代码

创建一个表只是知道要执行的查询,然后在db 对象上调用executeSql() 方法,提供你的查询作为一个参数。下面是创建transactions 表的代码。

我们使用一个整数数据类型来存储transactionDate ,因为它将被存储为一个Unix时间戳。

category_id 是来自categories 表的rowId 。我们还可以给它添加一个外键,但为了简单起见,我们不会这样做。

export const createTransactionsTable = async db => {
  const query = `CREATE TABLE IF NOT EXISTS transactions (transactionDate INTEGER, summary TEXT, category_id INTEGER NOT NULL, transactionType TEXT, amount FLOAT)`;
  await db.executeSql(query);
};

复制代码

下面是创建一个新交易记录的代码。除了transactionDate ,所有的交易字段都可以直接保存。

日期选择器库返回一个Date 对象,所以我们必须通过调用getTime() ,然后除以1000 ,得到Unix的时间戳,从而获得时间戳。这一点非常重要,因为如果不是以正确的格式保存,我们就不能调用SQLite中的日期操作方法。

export const createTransaction = async (db, transaction) => {
  const {transactionDate, summary, category, transactionType, amount} =
          transaction;
  const timestamp = parseInt((transactionDate.getTime() / 1000).toFixed(0));
  const insertQuery = `INSERT INTO transactions(transactionDate, summary, category_id, transactionType, amount) VALUES("${timestamp}", "${summary}", "${category}", "${transactionType}", "${amount}")`;
  return db.executeSql(insertQuery);
};

复制代码

请务必查看repo中处理类别和资金的代码,因为在本教程中我们将不再讨论这些。

使用React Native Chart Kit创建财务图表

现在是时候进行本教程的主要话题了:用React Native Chart Kit创建图表。

首先,导入我们需要的模块。

// src/screens/ReportsScreen.js
import React, {useState, useEffect, useCallback} from 'react';
import {ScrollView, StyleSheet} from 'react-native';

import {withTheme} from 'react-native-paper';
import {SafeAreaView} from 'react-native-safe-area-context';

复制代码

同时为每种类型的图表导入自定义组件。我们将在后面创建这些组件。

import FinancePieChart from '../components/FinancePieChart';
import FinanceLineChart from '../components/FinanceLineChart';
import FinanceBarChart from '../components/FinanceBarChart';

复制代码

接下来,提取用于从数据库中获取我们需要的报告数据的方法。这些方法中的每一个(除了前两个)都对应于我们将显示给用户的报告类型。

如果你看过前面的截图,这些方法中的每一个都对应于每个图表(除了最后一个图表取决于下面的最后三个方法)。我们也将在后面添加这些。

import {
  getDBConnection,
  createTransactionsTable,

  // pie charts
  getTransactionsGroupedByTransactionType,
  getExpenseTransactionsGroupedByCategory,
  getIncomeTransactionsGroupedByCategory,
  getSavingsTransactionsGroupedByCategory,
  getInvestmentTransactionsGroupedByCategory,

  // bar charts
  getExpenseGroupedByMonth,

  // for bar chart and line chart
  getSavingsGroupedByMonth,
  getIncomeGroupedByMonth, 

  getInvestmentsGroupedByMonth, // for line chart
} from '../../data/db-service';

复制代码

接下来,创建组件并初始化我们将用于存储来自数据库的数据的状态值。

const ReportsScreen = ({theme}) => {
  const {colors, fonts} = theme;

  const [byExpenseCategories, setByExpenseCategories] = useState([]);
  const [byIncomeCategories, setByIncomeCategories] = useState([]);
  const [bySavingsCategories, setBySavingsCategories] = useState([]);
  const [byInvestmentCategories, setByInvestmentCategories] = useState([]);
  const [byTransactionTypes, setByTransactionTypes] = useState([]);

  const [monthlyIncome, setMonthlyIncome] = useState([]);
  const [monthlyExpense, setMonthlyExpense] = useState([]);

  const [monthlySavings, setMonthlySavings] = useState([]);
  const [monthlyInvestments, setMonthlyInvestments] = useState([]);
}

复制代码

接下来,我们现在调用这些方法,用它们返回的值来更新状态。

const loadDataCallback = useCallback(async () => {
  try {
    const db = await getDBConnection();
    await createTransactionsTable(db);

    const groupedByTransactionTypes =
      await getTransactionsGroupedByTransactionType(db);
    if (groupedByTransactionTypes.length) {
      setByTransactionTypes(groupedByTransactionTypes);
    }

    const groupedByExpenseCategories =
      await getExpenseTransactionsGroupedByCategory(db);
    if (groupedByExpenseCategories.length) {
      setByExpenseCategories(groupedByExpenseCategories);
    }

    const groupedByIncomeCategories =
      await getIncomeTransactionsGroupedByCategory(db);
    if (groupedByIncomeCategories.length) {
      setByIncomeCategories(groupedByIncomeCategories);
    }

    const groupedBySavingsCategories =
      await getSavingsTransactionsGroupedByCategory(db);
    if (groupedBySavingsCategories.length) {
      setBySavingsCategories(groupedBySavingsCategories);
    }

    const groupedByInvestmentCategories =
      await getInvestmentTransactionsGroupedByCategory(db);
    if (groupedByInvestmentCategories.length) {
      setByInvestmentCategories(groupedByInvestmentCategories);
    }

    const incomeMonth = await getIncomeGroupedByMonth(db);
    if (incomeMonth) {
      setMonthlyIncome(incomeMonth);
    }

    const expenseMonth = await getExpenseGroupedByMonth(db);
    if (expenseMonth) {
      setMonthlyExpense(expenseMonth);
    }

    const savingsMonth = await getSavingsGroupedByMonth(db);
    if (savingsMonth) {
      setMonthlySavings(savingsMonth);
    }

    const investmentMonth = await getInvestmentsGroupedByMonth(db);
    if (investmentMonth) {
      setMonthlyInvestments(investmentMonth);
    }
  } catch (error) {
    console.error('transaction list err: ', error);
  }
}, []);

useEffect(() => {
  loadDataCallback();
}, [loadDataCallback]);

复制代码

为了让我们对每个方法的作用有一致的认识,这里有一个快速的分类。

  • getTransactionsGroupedByTransactionType() - 按交易类型(如费用、收入、储蓄、投资)对当前月份的交易进行分组,并得到每个交易的总和。
  • getExpenseTransactionsGroupedByCategory() - 按支出下的所有类别(如水费、电费、外出就餐)对当月的交易进行分组,并返回每项的总和。
  • getIncomeTransactionsGroupedByCategory() - 与上一个类似,但对收入项下的类别(例如,主要收入、副业收入)进行分组。
  • getSavingsTransactionsGroupedByCategory() - 与上一个类似,但交易类型仅限于储蓄(如A银行储蓄、B银行储蓄)。
  • getInvestmentTransactionsGroupedByCategory() - 与上一条类似,但交易类型仅限于投资(如股票、加密货币、房地产)。
  • getIncomeGroupedByMonth() - 返回每个月的总收入
  • getExpenseGroupedByMonth() - 返回每个月的总支出
  • getSavingsGroupedByMonth() - 返回每个月的储蓄总额
  • getInvestmentsGroupedByMonth() - 返回每个月的投资总额

回到代码中,我们现在添加显示每个图表的条件。

const hasByTransactionTypes = byTransactionTypes.length > 0;
const hasByExpenseCategories = byExpenseCategories.length > 0;
const hasByIncomeCategories = byIncomeCategories.length > 0;
const hasBySavingsCategories = bySavingsCategories.length > 0;
const hasByInvestmentCategories = byInvestmentCategories.length > 0;
const hasMonthlyIncome = monthlyIncome.length > 0;
const hasMonthlyExpense = monthlyExpense.length > 0;

const hasIncomeSavingsInvestment =
        monthlyIncome.length > 0 ||
        monthlySavings.length > 0 ||
        monthlyInvestments.length > 0;

复制代码

接下来,我们需要声明最后一个图表的设置(即在一个图表中显示收入、储蓄和投资的折线图的那个)。

我们不能在组件本身中真正地构建数据;这就是为什么我们要在这里构建它。这些主要是由React Native Chart Kit库使用的。

const lineChartLegends = [
  {
    name: 'Income',
    color: '#003049',
  },
  {
    name: 'Savings',
    color: '#d62828',
  },
  {
    name: 'Investment',
    color: '#f77f00',
  },
];

// dataset for the line chart
const datasets = [];
if (monthlyIncome.length > 0) {
  datasets.push({
    data: monthlyIncome.map(item => item.value),
    color: (opacity = 1) => '#003049',
    strokeWidth: 2,
  });
}

if (monthlySavings.length > 0) {
  datasets.push({
    data: monthlySavings.map(item => item.value),
    color: (opacity = 1) => '#d62828',
    strokeWidth: 2,
  });
}

if (monthlyInvestments.length > 0) {
  datasets.push({
    data: monthlyInvestments.map(item => item.value),
    color: (opacity = 1) => '#f77f00',
    strokeWidth: 2,
  });
}

const chartData = {
  labels: monthlyIncome.map(item => item.label),
  datasets,
};

复制代码

最后,返回UI。

return (
  <SafeAreaView
    style={[styles.container, {backgroundColor: colors.background}]}
  >
    <ScrollView
      style={{
        flex: 1,
      }}
    >
      {hasByTransactionTypes && (
        <FinancePieChart
          title="Transaction Types"
          data={byTransactionTypes}
        />
      )}

      {hasByExpenseCategories && (
        <FinancePieChart title="Expenses" data={byExpenseCategories} />
      )}

      {hasByIncomeCategories && (
        <FinancePieChart title="Income" data={byIncomeCategories} />
      )}

      {hasBySavingsCategories && (
        <FinancePieChart title="Savings" data={bySavingsCategories} />
      )}

      {hasByInvestmentCategories && (
        <FinancePieChart title="Investment" data={byInvestmentCategories} />
      )}

      {hasMonthlyIncome && (
        <FinanceBarChart
          title="Monthly Income"
          data={monthlyIncome}
          fillShadowGradient="#DF5353"
          color="#d62828"
        />
      )}

      {hasMonthlyExpense && (
        <FinanceBarChart
          title="Monthly Expense"
          data={monthlyExpense}
          fillShadowGradient="#00b4d8"
          color="#0077b6"
        />
      )}

      {hasIncomeSavingsInvestment && (
        <FinanceLineChart
          title="Income to savings to investment"
          chartData={chartData}
          fillShadowGradient="#ccc"
          legend={lineChartLegends}
        />
      )}
    </ScrollView>
  </SafeAreaView>
);

const styles = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: 'column',
  },
});

export default withTheme(ReportsScreen);

复制代码

饼图

Pie Chart React Native Chart Kit

现在让我们继续讨论图表组件的代码。首先,让我们看一下FinancePieChart 组件。这使用了React Native Chart Kit中的PieChart 组件。我们用它来生成交易类型、支出类型、收入类型、储蓄类型和投资类型的饼图。

我们首先导入我们需要的模块。我们还将导入一个Legend 组件,用于显示每个数据点的图例。

请注意,PieChart 组件已经有自己的图例,但我们不会使用它。这就是为什么我们要把hasLegend={false} 作为一个道具。这是因为它的可定制性不强。要修改它的位置真的很困难。使用一个自定义的Legend 组件可以有更多的灵活性。

我们正在使用Dimensions 模块来获取当前设备的屏幕宽度。然后我们把这个宽度提供给组件,使其适应屏幕的宽度。注意,它实际上不会消耗设备的全部可用宽度。相反,它将只使用其中的一半。

// src/components/FinancePieChart.js

import React from 'react';
import {View, Text, Dimensions, StyleSheet} from 'react-native';

import {PieChart} from 'react-native-chart-kit';

import Legend from './Legend';

const screenWidth = Dimensions.get('window').width;

复制代码

接下来,初始化图表配置。这是该图表的配置对象。它主要用于配置一般的图表设置,如strokeWidth (图表的基本笔画宽度)和color (用于计算图表中的标签和扇区的基本颜色的函数)。请注意,并非所有属性都适用于所有的图表类型。

会有一些只适用于特定类型。但好在所有这些属性都有分配给它们的默认值,所以你可能可以不使用不适用于你所使用的特定图表的属性。

const chartConfig = {
  backgroundGradientFrom: '#1E2923',
  backgroundGradientFromOpacity: 0,
  backgroundGradientTo: '#08130D',
  backgroundGradientToOpacity: 0.5,
  color: (opacity = 1) => `rgba(26, 255, 146, ${opacity})`,
  strokeWidth: 2,
  useShadowColorFromDataset: false,
};

复制代码

接下来,渲染PieChart 组件。这个组件接受以下道具。

  • data - 你想要呈现的数据。我们稍后会看到这一点,一旦我们通过src/data/db-service.js 文件中返回的查询和数据。但一般的经验法则是,你需要一个有标签和值的对象数组。标签是图例,而值是数据点。
  • widthheight - 图表的尺寸
  • chartConfig - 图表的一般配置对象
  • accessor - 来自data 对象的属性,数字值取自该对象。
  • backgroundColor - 应用于图表的背景颜色。如果你不希望应用任何背景颜色,这可以是transparentnone
  • center - 定位图表的x和y坐标偏移量。[0, 0] 表示我们将其定位在分配的位置的最开始。请注意,这不是一个固定的位置,而是一个相对的位置,所以它将始终坚持在它之前的元素。
  • hasLegend - 是否显示图例

你可以查看repo来了解更多关于你可以指定的道具

在图表的正下方,我们渲染图例。

function FinancePieChart({title, data}) {
  return (
    <View style={styles.container}>
      <View style={styles.titleContainer}>
        <Text>{title}</Text>
      </View>

      <View style={styles.bodyContainer}>
        <View style={styles.chartContainer}>
          <PieChart
            data={data}
            width={screenWidth}
            height={200}
            chartConfig={chartConfig}
            accessor={'total'}
            backgroundColor={'transparent'}
            center={[0, 0]}
            hasLegend={false}
          />
        </View>

        <View style={styles.legendContainer}>
          {data.map(({name, color}) => {
            return <Legend key={name} name={name} color={color} />;
          })}
        </View>
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    marginTop: 10,
  },
  titleContainer: {
    flex: 1,
    alignItems: 'center',
  },
  bodyContainer: {
    flexDirection: 'row',
  },
  chartContainer: {
    flex: 1,
  },
  legendContainer: {
    flex: 1,
    marginTop: 20,
  },
});

export default FinancePieChart;

复制代码

在我们进行下一个图表之前,让我们先看看我们如何获得提供给FinancePieChart 组件的数据。

下面是为第一个饼图获取数据的代码。我们使用聚合函数SUM() ,将每一行中的amount ,然后按transactionType 进行分组。交易类型可以在config/app.js 文件中找到。

为了得到我们需要的数据,我们在结果中循环几次:一个forEach 和一个for 循环。请注意,外循环只执行一次,这只是为了让我们能够得到包含实际结果的对象。

行数据并不是以普通对象的形式出现的,所以我们必须调用result.rows 上的item() 方法,并提供当前的index 来获得我们需要的数据。从那里,我们提取nametotal 列。

我们还增加了一个属性color ,用于指定数据点的颜色。这些颜色都是在src/helpers/palette.js 文件中硬编码的。这要归功于coolors提供的漂亮调色板。

export const getTransactionsGroupedByTransactionType = async db => {
  try {
    const transactions = [];
    const results = await db.executeSql(
      `SELECT SUM(amount) AS total, transactionType AS name FROM transactions 
      WHERE strftime("%m/%Y", datetime(transactionDate, 'unixepoch', 'localtime')) = ?
      GROUP BY transactionType`,
      [monthYear],
    );
    results.forEach(result => {
      for (let index = 0; index < result.rows.length; index++) {
        const {name, total} = result.rows.item(index);
        transactions.push({
          name,
          total,
          color: paletteOne[index],
        });
      }
    });
    return transactions;
  } catch (error) {
    console.error(error);
    throw Error('Failed to getTransactionsGroupedByTransactionType...');
  }
};

复制代码

请注意,monthYear 是在顶部附近声明的。

enablePromise(true);

const monthYear = new Date().toLocaleDateString(undefined, {
  year: 'numeric',
  month: '2-digit',
});

复制代码

其他饼图使用了几乎相同的代码。只有查询发生了变化,所以我在这里只包括查询。这一次,我们使用一个INNER JOIN 来连接categories 表。这是因为我们需要通过categoryType 来过滤。

const results = await db.executeSql(
  `SELECT SUM(amount) AS total, categories.name AS name FROM transactions 
  INNER JOIN categories ON categories.rowId = transactions.category_id 
  WHERE categoryType = 'expense' AND strftime("%m/%Y", datetime(transactionDate, 'unixepoch', 'localtime')) = ?
  GROUP BY transactions.category_id`,
  [monthYear],
);

复制代码

柱状图

Bar Chart React Native Chart Kit

现在让我们来看看FinanceBarChart 这个组件。我们要用它来生成一个用户月收入和月支出的图表。代码应该是非常相似的。对于chartConfig ,我们添加了以下属性。

    • barPercentage - 定义了图表中每个条形图所占的可用宽度的百分比。这是一个介于0和1之间的值。在这种情况下,我们指定0.5 ,表示它将只使用可用宽度的一半。
    • fillShadowGradient - 定义了每个条形图的颜色。注意,背景渐变将以这个值为基础计算,所以它实际上不是一个纯色。
    • fillShadowGradientOpacity - 纹理的不透明度fillShadowGradient
  • labelColor - 每个条形图下面的文本标签的颜色

  • decimalPlaces - 显示多少个小数位。这个默认值是2 ,所以如果你不想显示任何小数,你必须指定0

我们提供给它的道具与饼图基本相同。唯一的区别是showBarTops 。这是一个布尔值,用于指定是否在每个条形图的顶部显示线条。我们指定了false ,因为我们并不真的想显示它。

至于你提供的数据,它允许你在datasets 属性下添加多个对象,但它实际上不会呈现多个条形。所以你不能真正使用这种类型的图表来进行数据比较。

// src/components/FinanceBarChart.js
import React from 'react';
import {View, Text, Dimensions, StyleSheet} from 'react-native';
import {BarChart} from 'react-native-chart-kit';

const screenWidth = Dimensions.get('window').width;

function FinanceBarChart({title, fillShadowGradient, data}) {
  const chartConfig = {
    backgroundGradientFrom: '#fff',
    backgroundGradientFromOpacity: 0,
    backgroundGradientTo: '#fff',
    backgroundGradientToOpacity: 0.5,

    fillShadowGradient,
    fillShadowGradientOpacity: 1,
    color: (opacity = 1) => `#023047`,
    labelColor: (opacity = 1) => `#333`,
    strokeWidth: 2,

    barPercentage: 0.5,
    useShadowColorFromDataset: false,
    decimalPlaces: 0,
  };

  const labels = data.map(item => {
    return item.label;
  });

  const values = data.map(item => {
    return item.value;
  });

  const chartData = {
    labels,
    datasets: [
      {
        data: values,
      },
    ],
  };

  return (
    <View style={styles.container}>
      <View style={styles.titleContainer}>
        <Text>{title}</Text>
      </View>
      <BarChart
        data={chartData}
        width={screenWidth}
        height={220}
        chartConfig={chartConfig}
        showBarTops={false}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    marginBottom: 20,
  },
  titleContainer: {
    flex: 1,
    alignItems: 'center',
  },
});

export default FinanceBarChart;

复制代码

请查看文档,了解更多关于你可以使用的配置和道具的信息。

现在让我们看一下为该图表提供数据的查询。同样,我们使用SQLite的strftime 函数,按月对交易进行分组,得到总和,并只过滤income 交易。

export const getIncomeGroupedByMonth = async (db) => {
  try {
    const transactions = [];
    const results = await db.executeSql(
      `SELECT strftime("%m-%Y", datetime(transactionDate, 'unixepoch', 'localtime')) AS monthYear,
      SUM(amount) AS total 
      FROM transactions 
      WHERE transactionType = 'income'
      GROUP BY monthYear`
    );

    results.forEach((result) => {
      for (let index = 0; index < result.rows.length; index++) {
        const item = result.rows.item(index);

        transactions.push({
          value: item.total,
          label: item.monthYear,
        });
      }
    });

    return transactions;
  } catch (error) {
    console.error(error);
    throw Error("Failed to getIncomeGroupedByMonth...");
  }
};

复制代码

获取月度支出的代码几乎是一样的。你只需要把transactionType 替换成expense ,所以我们在此不再赘述。

线形图

Line Chart React Native Chart Kit

我们要讨论的最后一种图表类型是折线图。我们用它来显示收入、储蓄和投资在各月之间的比较。

正如你在前面报告屏幕的代码中所看到的,这可以接受多个对象的datasets 属性,所以它最好用于比较目的。这使用了React Native Chart Kit库中的LineChart 组件。

chartConfig 而言,我们提供的这个真的没有什么不同。

LineChart 组件并不完全带有自己的图例。这就是为什么我们要再次引入我们自制的Legend 组件。这将作为我们所比较的每个数据点的图例,包括收入、储蓄和投资。

// src/components/FinanceLineChart.js
import React from 'react';
import {View, Text, Dimensions, StyleSheet} from 'react-native';
import {LineChart} from 'react-native-chart-kit';

import Legend from './Legend';

const screenWidth = Dimensions.get('window').width;

function FinanceLineChart({title, chartData, legend, fillShadowGradient}) {
  const chartConfig = {
    backgroundGradientFrom: '#fff',
    backgroundGradientFromOpacity: 0,
    backgroundGradientTo: '#fff',
    backgroundGradientToOpacity: 0.5,

    fillShadowGradient,
    fillShadowGradientOpacity: 0,
    color: (opacity = 1) => `#023047`,
    labelColor: (opacity = 1) => `#333`,
    strokeWidth: 2,

    useShadowColorFromDataset: false,
    decimalPlaces: 0,
  };

  return (
    <View style={styles.container}>
      <View style={styles.titleContainer}>
        <Text>{title}</Text>
      </View>
      <LineChart
        data={chartData}
        width={screenWidth}
        height={220}
        chartConfig={chartConfig}
      />

      <View style={styles.legendContainer}>
        {legend.map(({name, color}) => {
          return <Legend key={name} name={name} color={color} />;
        })}
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    marginBottom: 20,
  },
  titleContainer: {
    flex: 1,
    alignItems: 'center',
  },
  legendContainer: {
    flex: 1,
    marginTop: 20,
    alignItems: 'center',
  },
});

export default FinanceLineChart;

复制代码

在React Native Chart Kit提供的图表组件中,LineChart 是拥有最强大功能的一个组件。我们在这里没有使用它,但是如果你看一下文档,你会发现它比其他的有更多的道具可用。例如,它有一个onDataPointClick 道具,允许你在图表上点击数据点时执行一个动作。

至于数据,我们要从三个独立的查询中提取数据。其中一个已经被前面的FinanceBarChart 使用了。这就是monthlyIncome 。所以,现在我们需要的是储蓄和投资的数据。这是为储蓄准备的。

export const getSavingsGroupedByMonth = async (db) => {
  try {
    const transactions = [];
    const results = await db.executeSql(
      `SELECT strftime("%m-%Y", datetime(transactionDate, 'unixepoch', 'localtime')) AS monthYear,
      SUM(amount) AS total 
      FROM transactions 
      WHERE transactionType = 'savings'
      GROUP BY monthYear`
    );

    results.forEach((result) => {
      for (let index = 0; index < result.rows.length; index++) {
        const item = result.rows.item(index);

        transactions.push({
          value: item.total,
          label: item.monthYear,
        });
      }
    });

    return transactions;
  } catch (error) {
    console.error(error);
    throw Error("Failed to getSavingsGroupedByMonth...");
  }
};

复制代码

我将把另一个留给你,让你在repo中查看。

总结

就这样吧!在本教程中,你学会了如何使用React Native Chart Kit库在React Native中创建不同种类的图表。具体来说,我们在本教程中使用了饼状图、柱状图和线状图。你可以在其GitHub repo中找到该应用的完整源代码。

如果你想了解更多可以在React Native中使用的其他图表库,请查看这篇关于React Native图表库的文章。

The postUsing React Native Chart Kit to visualize dataappeared first onLogRocket Blog.

Guess you like

Origin juejin.im/post/7068108422645825566