Добро пожаловать в эту серию, в которой вы узнаете, как использовать React Native для создания макетов страниц, обычно используемых в мобильных приложениях. Макеты, которые вы создаете, не будут функциональными — вместо этого, основной целью этой серии является то, что вы можете запачкать руки при размещении контента в ваших приложениях React Native.
Если вы новичок в разработке приложений React Native или стилей в целом, посмотрите мой предыдущий учебник:
Чтобы следовать этой серии, я призываю вас сначала попытаться воссоздать каждый экран самостоятельно, прежде чем вы прочитаете мои пошаговые инструкции в руководстве. Вы действительно не получите много пользы от этого урока, просто прочитав его! Попробуйте сначала, прежде чем искать ответы здесь. Если вам удалось сделать его похожим на исходный экран, сравните вашу реализацию с моей. Тогда решите для себя, какой из них лучше!
В этом последнем руководстве серии вы создадите следующую страницу новостной ленты:

Макеты новостной ленты используются для представления информации таким образом, чтобы ее можно было легко сканировать. Большую часть времени он представлен в виде списка с заголовком, выдержкой и, необязательно, предварительным изображением, представляющим каждый элемент новостей.
Вот пара примеров этого типа макета, используемого в дикой природе:


Настройка проекта
Первым шагом, конечно же, является создание нового проекта React Native:
|
1
|
react-native init react-native-common-screens
|
После настройки проекта откройте файл index.android.js и замените код по умолчанию следующим index.android.js :
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
import React, { Component } from ‘react’;
import {
AppRegistry
} from ‘react-native’;
import News from ‘./src/pages/News’;
export default class ReactNativeCommonScreens extends Component {
render() {
return (
<News />
);
}
}
AppRegistry.registerComponent(‘ReactNativeCommonScreens’, () => ReactNativeCommonScreens);
|
Создайте папку src/pages и создайте в News.js файл News.js
Вам также понадобится пакет react-native-vector-icons . Это специально используется для значка назад в заголовке.
|
1
|
npm install —save react-native-vector-icons
|
Откройте файл android/app/build.gradle и добавьте ссылку на пакет:
|
1
2
3
4
|
dependencies {
//rest of the dependencies are here at the top
compile project(‘:react-native-vector-icons’) //add this
}
|
Сделайте то же самое с файлом android/settings.gradle , добавив следующее внизу:
|
1
2
|
include ‘:react-native-vector-icons’
project(‘:react-native-vector-icons’).projectDir = new File(rootProject.projectDir, ‘../node_modules/react-native-vector-icons/android’)
|
Откройте android/app/src/main/java/com/react-native-common-screens/MainApplication.java и импортируйте пакет:
|
1
2
3
4
|
import java.util.Arrays;
import java.util.List;
import com.oblador.vectoricons.VectorIconsPackage;
|
Наконец, инициализируйте пакет:
|
1
2
3
4
5
6
7
|
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new VectorIconsPackage() //add this
);
}
|
Создание страницы новостей
Хорошо, теперь, когда вы попытались самостоятельно кодировать макет (не читерство, верно?), Я покажу вам, как я построил свою реализацию.
Вы, наверное, уже заметили эту тенденцию. Я организовал их в соответствии с трудностями или, по крайней мере, в соответствии с тем, что мне было трудно Таким образом, этот последний экран является главным боссом среди других экранов, которые вы создали до сих пор. Впрочем, не волнуйтесь, я все равно буду шаг за шагом руководить вами.
Вам понадобится несколько изображений для предварительного просмотра каждой новости. Я добавил несколько изображений в репо, которые вы можете использовать, если хотите.
Начните с добавления стандартного кода:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
import React, { Component } from ‘react’;
import {
StyleSheet,
Text,
View,
ScrollView,
Image
} from ‘react-native’;
import Icon from ‘react-native-vector-icons/FontAwesome’;
import Button from ‘../components/Button’;
import NewsItem from ‘../components/NewsItem’;
|
На этот раз появился новый компонент с именем NewsItem ( src/components/NewsItem ). Как следует из названия, он используется для отображения каждого элемента новостей. Мы вернемся к этому позже, но сначала посмотрим на функцию constructor() . Как и на экране галереи ранее, здесь используется состояние для хранения источника данных для новостей. Названия и резюме взяты из New York Times , но изображения взяты из Google Images (и помечены для повторного использования их соответствующими владельцами).
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
constructor(props) {
super(props);
this.state = {
news_items: [
{
pretext: ‘Gray Matter’,
title: ‘Art Makes You Smart’,
summary: ‘Museum visits increase test scores, generate social responsibility and increase appreciation of the arts by students.’,
image: require(‘../images/pink.jpg’),
},
{
pretext: »,
title: ‘Tension and Flaws Before Health Website Crash’,
summary: ‘Interviews and documents offer new details into how the rollout of President Obama\’s signature program turned into a major humiliation.’,
image: require(‘../images/beach.jpg’)
},
{
pretext: »,
title: ’36 Hours in Charleston, SC’,
summary: ‘Crowds are thinner and temperatures are mild during winter in this..’,
image: require(‘../images/rails.jpg’)
},
]
};
}
|
Содержание делится на три части: заголовок, текст инструкции и новости. Заголовок очень похож на заголовок с экрана календаря ранее; единственное отличие состоит в том, что вместо трех есть только два видимых элемента. (Если вы хотите освежить в памяти то, как был создан экран календаря , продолжайте читать этот учебник.)
Я говорю «видимый», потому что на самом деле есть три элемента — последний просто скрыт! Это позволяет легко центрировать текст по центру. Если у вас в заголовке только два элемента, будет сложно выяснить, как разделить пространство между двумя элементами, и при этом средний элемент будет отображаться по центру. Но если у вас есть три элемента, у каждого из них может быть одинаковое значение flex , и вы можете просто использовать textAlign для позиционирования текста или alignItems для позиционирования компонентов View .
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
render() {
return (
<View style={styles.container}>
<View style={styles.header}>
<Button
noDefaultStyles={true}
styles={{button: styles.header_button}}
onPress={this.press.bind(this)}
>
<View style={styles.back_button}>
<Icon name=»chevron-left» size={20} color=»#397CA9″ />
<Text style={[styles.back_button_label]}> Sections</Text>
</View>
</Button>
<View style={styles.header_text}>
<Text style={styles.header_text_label}>Most E-Mailed</Text>
</View>
<View style={styles.whitespace}></View>
</View>
<View style={styles.instruction}>
<Text style={styles.instruction_text}>SWIPE ACROSS SECTIONS</Text>
</View>
<ScrollView style={styles.news_container}>
{ this.renderNews() }
</ScrollView>
</View>
);
}
|
Функция renderNews() — это та, которая просматривает все новостные элементы в состоянии и отображает их с NewsItem компонента NewsItem .
|
1
2
3
4
5
|
renderNews() {
return this.state.news_items.map((news, index) => {
return <NewsItem key={index} index={index} news={news} />
});
}
|
Далее идет код для компонента NewsItem . Начните с добавления стандартного кода компонента React. Как вы видели ранее, этот компонент принимает key , index и news качестве реквизита. Вам действительно нужны только index и news . key — это просто способ React Native для уникальной идентификации каждой строки в списке. Вам необходимо Array.map его каждый раз, когда вы используете Array.map для рендеринга; в противном случае он будет жаловаться.
Когда вы используете функциональные компоненты, реквизиты передаются как один аргумент. Ниже отдельные реквизиты извлекаются с использованием назначения деструктурирования , поэтому { news, index } основном извлекают свойства news и index из реквизита. Оттуда вы можете получить номер для визуализации.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
import React, { Component } from ‘react’;
import {
StyleSheet,
Text,
View,
Image
} from ‘react-native’;
import Button from ‘./Button’;
const NewsItem = ({ news, index }) => {
let number = (index + 1).toString();
return (
…
);
}
|
Если вы посмотрите на снимок экрана, сделанный ранее, то увидите, что каждый новостной элемент можно разделить на две группы: одну, которая отображает текст новости (номер, заголовок и отрывок), а другую — изображение функции.
Это решает проблему с изображением объекта, так как это всего лишь один элемент. Но для новостного текста вам все равно придется делить его дальше. Как вы могли заметить, номер находится в той же позиции, даже если у заголовка есть предлог (например, «Серый вопрос»). Предлог также имеет другой стиль от названия и номера.
Используя эти знания, вы можете сделать вывод, что число, предлог и заголовок не должны быть объединены в одном контейнере. Кроме того, предлог, заголовок и отрывок выглядят так, как будто они сложены вертикально, поэтому вы можете поместить их в один контейнер. Только номер должен быть выведен. С этим вы получите следующую разметку:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
<Button
key={index}
noDefaultStyles={true}
onPress={onPress.bind(this, news)}
>
<View style={styles.news_item}>
<View style={styles.news_text}>
<View style={styles.number}>
<Text style={styles.title}>{number}.</Text>
</View>
<View style={styles.text_container}>
{ getPretext(news) }
<Text style={styles.title}>{news.title}</Text>
<Text>{news.summary}</Text>
</View>
</View>
<View style={styles.news_photo}>
<Image source={news.image} style={styles.photo} />
</View>
</View>
</Button>
|
Функция getPretext() позволяет вам условно визуализировать компонент Text только тогда, когда в элементе новостей есть предлог.
|
1
2
3
4
5
6
7
|
function getPretext(news) {
if(news.pretext){
return (
<Text style={styles.pretext}>{news.pretext}</Text>
);
}
}
|
Вот функция onPress . Все, что он делает, это предупреждает заголовок новости, но в реальном приложении это должно перейти к самой статье:
|
1
2
3
|
function onPress(news) {
alert(news.title);
}
|
На этом этапе страница теперь будет выглядеть так:

Теперь добавьте следующие стили на страницу новостей:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
const styles = StyleSheet.create({
container: {
flex: 1
},
header: {
flexDirection: ‘row’,
backgroundColor: ‘#FFF’,
padding: 20,
justifyContent: ‘space-between’,
borderBottomColor: ‘#E1E1E1’,
borderBottomWidth: 1
},
header_button: {
flex: 1,
},
whitespace: {
flex: 1
},
back_button: {
flexDirection: ‘row’,
alignItems: ‘center’
},
back_button_label: {
color: ‘#397CA9’,
fontSize: 20,
},
instruction: {
alignSelf: ‘center’,
marginTop: 5
},
instruction_text: {
color: ‘#A3A3A3’
},
header_text: {
flex: 1,
alignSelf: ‘center’
},
header_text_label: {
fontSize: 20,
textAlign: ‘center’
},
news_container: {
flex: 1,
flexDirection: ‘column’
},
});
|
Я больше не буду рассказывать о том, что делает каждая строка кода, поскольку в ней применяются те же концепции, которые вы изучили в предыдущих уроках этой серии. Вот как будет выглядеть страница после применения вышеуказанных стилей:

Затем добавьте стили для каждой новости. У каждого news_item есть flexDirection row так что текст новости и показанное изображение находятся на одной строке. news_text занимает две трети доступного пространства, в то время как news_photo занимает оставшееся пространство.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
const styles = StyleSheet.create({
news_item: {
flex: 1,
flexDirection: ‘row’,
paddingRight: 20,
paddingLeft: 20,
paddingTop: 30,
paddingBottom: 30,
borderBottomWidth: 1,
borderBottomColor: ‘#E4E4E4’
},
news_text: {
flex: 2,
flexDirection: ‘row’,
padding: 10
},
});
|

Затем вам нужно добавить стили для решения проблемы с текстом, перекрывающимся с изображением предварительного просмотра. Вы можете сделать это, присвоив значение flex news_text элементам news_text . Значение flex уже назначено для news_text , но поскольку внутри него используется представление, вам также необходимо назначить значение flex чтобы они не выходили за пределы родительского View .
Мы text_container number text_container 0.5 для number , в то время как text_container будет иметь значение 3 . С этими значениями text_container будет занимать в шесть раз больше места, чем number .
|
1
2
3
4
5
6
|
number: {
flex: 0.5,
},
text_container: {
flex: 3
},
|

Теперь все, что вам нужно сделать, это добавить последние штрихи к стилю текста:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
pretext: {
color: ‘#3F3F3F’,
fontSize: 20
},
title: {
fontSize: 28,
fontWeight: ‘bold’,
color: ‘#000’,
fontFamily: ‘georgia’
},
news_photo: {
flex: 1,
justifyContent: ‘center’,
alignItems: ‘center’
},
photo: {
width: 120,
height: 120
}
|
И не забудьте экспортировать компонент!
|
1
|
export default NewsItem;
|
Последние мысли
Это оно! В этой заключительной части этой серии вы узнали, как реализовать макет, обычно используемый на страницах новостей. Этот урок собрал все, что вы узнали в предыдущих частях серии. Вы использовали как flexDirection: 'row' и flexDirection: 'column' чтобы завершить стили для каждого элемента новостей. Вы также использовали свои знания в выравнивании изображений для предварительного просмотра изображения.
Если вы попытались реализовать эти макеты самостоятельно, потерпели неудачу, а затем повторили попытку, у вас уже должно быть достаточно навыков для реализации любого макета. Вы можете применить то, что вы узнали здесь, для реализации даже сложных макетов, которые вы обычно видите в популярных приложениях. Если вы хотите больше практики, попробуйте воссоздать макеты, которые вы видите в популярных приложениях, таких как Facebook или YouTube.
Как мое решение сравнилось с вашим? Дайте нам знать в дискуссионном форуме ниже. А пока ознакомьтесь с другими нашими учебниками по React Native и Flexbox.





