使用UI框架


React Native暴露了你可以使用的普通组件,但是为了使他们看起来更加专业,通常还有很多工作要做。 使用Shoutem UI框架, 一组用于在React Native中构建专业UI/UX的3个软件包: - @shoutem/ui - @shoutem/theme - @shoutem/animation

我们将使用@shoutem/ui,包含可以在任何React Native应用程序中使用的样式化UI组件。 它基本上将任何普通的应用变成一个惊人的应用。 有很多组件,你可以使用开箱即用。 所有组件的文档可以在这里中找到。

添加静态数据

让我们添加静态restaurants,并在列表中显示。 首先从工具包导入UI组件。

#file: app/screens/RestaurantsList.js
import React, {
  Component
} from 'react';
import {
  Image,
  ListView,
  Tile,
  Title,
  Subtitle,
  Overlay,
  Screen
} from '@shoutem/ui';
import { NavigationBar } from '@shoutem/ui/navigation';

我们为您准备了一些数据。 创建app/assets文件夹,保存扩展的应用程序部分的资源,并提取这个JSON文件,其中包含restaurants数据。

RestaurantsList类中定义一个返回restaurants数组的方法。

#file: app/screens/RestaurantsList.js
export default class RestaurantsList extends Component {

  getRestaurants() {
    return require('../assets/restaurants.json');
  }

实现使用ListViewrender方法。 ListView接受以数组形式显示在列表中的数据和renderRow方法,它定义了列表行的外观。

添加renderRow方法并替换render方法的实现:

#file: app/screens/RestaurantsList.js
  getRestaurants() {...}

  renderRow(restaurant) {
    return (
      <Image styleName="large-banner" source={{ uri: restaurant.image &&
        restaurant.image.url ? restaurant.image.url : undefined  }}>
        <Tile>
          <Title>{restaurant.name}</Title>
          <Subtitle>{restaurant.address}</Subtitle>
        </Tile>
      </Image>
    );
  }

  render() {
    return (
      <Screen>
        <NavigationBar title="RESTAURANTS" />
        <ListView
          data={this.getRestaurants()}
          renderRow={restaurant => this.renderRow(restaurant)}
        />
      </Screen>
    );
  }

上传扩展:

$ shoutem push
Uploading `Restaurants` extension to Shoutem...
Success!

RestaurantsList现在已经显示了restaurants的列表。

这看起来正是我们想要的。

尝试点击一行。 什么都没发生! 我们希望在单击列表行项时打开详细信息屏幕。

创建详细信息屏幕

首先,创建详细信息屏幕:

$ shoutem screen add RestaurantDetails
File `app/screens/RestaurantDetails.js` is created.

Screen在extension.json文件中定义。 不要忘记在index.js中导出它。

#file: app/index.js
import RestaurantsList from './screens/RestaurantsList';
import RestaurantDetails from './screens/RestaurantDetails';

export const screens = {
  RestaurantsList,
  RestaurantDetails
};

export const reducer = {};

当触摸列表项时,我们要打开详细信息屏幕。 为此,我们需要来自React Native的TouchableOpacity和Shoutem的navigateTo - Redux动作创建器。 它接受Shoutem的route对象(具有screenprops属性)作为唯一的参数。

为了引用app/index.js中导出的RestaurantDetails屏幕,我们使用app/const.js文件中创建的ext helper函数。 此函数返回绝对名称,例如 developer.restaurants.RestaurantsList,对于作为其第一个参数传递的扩展部分,如果没有传递参数,则为扩展名name

让我们导入这些东西:

#file: app/screens/RestaurantsList.js
import {
  TouchableOpacity
} from 'react-native';
import { navigateTo } from '@shoutem/core/navigation';
import { ext } from '../const';

要打开一个触摸屏幕,我们需要触发navigateTo。 我们可以通过dispatch直接在屏幕上使用它,但Redux标准的方法是将dispatch和action创建器绑定在mapDispatchToProps函数中,connect 函数的第二个参数。 绑定动作通过props访问,这就是为什么我们需要绑定renderRow动作到正确的this上下文。

#file: app/screens/RestaurantsList.js
import { connect } from 'react-redux';

class RestaurantsList extends Component {
  constructor(props) {
    super(props);

    this.renderRow = this.renderRow.bind(this);
  }
  ...
}

export default connect(
  undefined,
  { navigateTo }
)(RestaurantsList);

实现renderRow函数。

#file: app/screens/RestaurantsList.js
  renderRow(restaurant) {
    const { navigateTo } = this.props;

    return (
      <TouchableOpacity onPress={() => navigateTo({
        screen: ext('RestaurantDetails'),
        props: { restaurant }
      })}>
        <Image styleName="large-banner" source={{ uri: restaurant.image &&
        restaurant.image.url ? restaurant.image.url : undefined }}>
          <Tile>
            <Title>{restaurant.name}</Title>
            <Subtitle>{restaurant.address}</Subtitle>
          </Tile>
        </Image>
      </TouchableOpacity>
    );
  }

这就是你最终在app/screens/RestaurantsList.js该有的东西:

#file: app/screens/RestaurantsList.js
import React, {
  Component
} from 'react';
import {
  TouchableOpacity,
} from 'react-native';
import {
  Image,
  ListView,
  Tile,
  Title,
  Subtitle,
  Overlay,
  Screen
} from '@shoutem/ui';
import { NavigationBar } from '@shoutem/ui/navigation';
import { navigateTo } from '@shoutem/core/navigation';
import { ext } from '../const';
import { connect } from 'react-redux';

class RestaurantsList extends Component {
  constructor(props) {
    super(props);

    this.renderRow = this.renderRow.bind(this);
  }

  getRestaurants() {
    return require('../assets/restaurants.json');
  }

  renderRow(restaurant) {
    const { navigateTo } = this.props;

    return (
      <TouchableOpacity onPress={() => navigateTo({
        screen: ext('RestaurantDetails'),
        props: { restaurant }
      })}>
        <Image styleName="large-banner" source={{ uri: restaurant.image &&
        restaurant.image.url ? restaurant.image.url : undefined }}>
          <Tile>
            <Title>{restaurant.name}</Title>
            <Subtitle>{restaurant.address}</Subtitle>
          </Tile>
        </Image>
      </TouchableOpacity>
    );
  }

  render() {
    return (
      <Screen>
        <NavigationBar title="RESTAURANTS" />
        <ListView
          data={this.getRestaurants()}
          renderRow={restaurant => this.renderRow(restaurant)}
        />
      </Screen>
    );
  }
}

export default connect(
  undefined,
  { navigateTo }
)(RestaurantsList)

RestaurantDetails屏幕,只需复制以下代码。 我们不会引入任何新的,只是使用一些新的组件。

#file: app/screens/RestaurantDetails.js
import React, {
  Component
} from 'react';
import {
  ScrollView,
} from 'react-native';
import {
  Icon,
  Row,
  Subtitle,
  Text,
  Title,
  View,
  Image,
  Divider,
  Overlay,
  Tile,
} from '@shoutem/ui';

export default class RestaurantDetails extends Component {
  render() {
    const { restaurant } = this.props;

    return (
      <ScrollView style = {{marginTop:-70}}>
        <Image styleName="large-portrait" source={{ uri: restaurant.image &&
        restaurant.image.url ? restaurant.image.url : undefined }}>
          <Overlay styleName="fill-parent">
            <Title>{restaurant.name}</Title>
            <Subtitle>{restaurant.address}</Subtitle>
          </Overlay>
        </Image>

        <Row>
          <Text>{restaurant.description}</Text>
        </Row>

        <Divider styleName="line" />

        <Row>
          <Icon name="laptop" />
          <View styleName="vertical">
            <Subtitle>Visit webpage</Subtitle>
            <Text>{restaurant.url}</Text>
          </View>
          <Icon name="right-arrow" />
        </Row>

        <Divider styleName="line" />

        <Row>
          <Icon name="pin" />
          <View styleName="vertical">
            <Subtitle>Address</Subtitle>
            <Text>{restaurant.address}</Text>
          </View>
          <Icon name="right-arrow" />
        </Row>

        <Divider styleName="line" />

        <Row>
          <Icon name="email" />
          <View styleName="vertical">
            <Subtitle>Email</Subtitle>
            <Text>{restaurant.mail}</Text>
          </View>
        </Row>

        <Divider styleName="line" />
      </ScrollView>
    );
  }
}

我们将跳过实现Web和电子邮件属性的处理,并只渲染它们。

上传扩展:

$ shoutem push
Uploading `Restaurants` extension to Shoutem...
Success!

当您点击列表中的某一行时,会得到:

这正是我们想要的! 但是,我们的应用程序正在使用静态数据。 让我们将它连接到Shoutem Cloud Storage