React Native 项目实战 -- DoubanProject

引言:本文是我研究react-native时写的一个简单的demo,代码里有详细的注释,好废话不多说,直接上代码。

1.项目目录

 

2.index.android.js

1
2
3
4
5
6
7
8
9
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
/**
 * index.android.js 入口文件
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */
// icon={require("image!book")}
// icon={require("image!movie")}
 
import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  Image,
  StatusBar
} from 'react-native';
 
// 导入导航器
var Navigation = require("./android_views/common/navigation");
// 导入BookList
var BookList = require("./android_views/book/book_list");
// 导入MovieList
var MovieList = require("./android_views/movie/movie_list");
 
// tab组件
import TabNavigator from 'react-native-tab-navigator';
 
// 隐藏状态栏
StatusBar.setHidden(true);
 
// TabNavigator管理两个模块:图书、电影
var DoubanProject = React.createClass({
  getInitialState: function() {
    return {
      selectedTab: "图书"
    };
  },
  render: function() {
    return (
      <TabNavigator>
        <TabNavigator.Item
          // 标题
          title="图书"
          // 设置选中的位置
          selected={this.state.selectedTab=="图书"}
          // 点击Event
          onPress={() => {
            this.setState({
              selectedTab:"图书"
            })
          }}
          //图标
          renderIcon={() => <Image style={styles.icon} source={require("./res/images/book.png")} />}
          //选中时图标
          renderSelectedIcon={() => <Image style={[styles.icon,{tintColor:'#2f8fe6'}]} source={require("./res/images/book.png")} />}>
          <Navigation component={BookList}/>
        </TabNavigator.Item>
           
        <TabNavigator.Item
          // 标题
          title="电影"
          // 设置选中的位置
          selected={this.state.selectedTab=="电影"}
          // 点击Event
          onPress={() => {
            this.setState({
              selectedTab:"电影"
            })
          }}
          //图标
          renderIcon={() => <Image style={styles.icon} source={require("./res/images/movie.png")} />}
          //选中时图标
          renderSelectedIcon={() => <Image style={[styles.icon,{tintColor:'#2f8fe6'}]} source={require("./res/images/movie.png")} />}>
          <Navigation component={MovieList}/>
        </TabNavigator.Item>
      </TabNavigator>
    )
  }
});
 
const styles = StyleSheet.create({
    icon: {
        width: 22,
        height: 22
    }
});
 
AppRegistry.registerComponent('DoubanProject', () => DoubanProject);

 

3.service.js

1
2
3
4
5
6
7
8
9
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
47
48
49
50
/*
    1.接口 API
    基于豆瓣开放API的图书、电影
*/
 
var BaseURL = "https://api.douban.com/v2/";
 
var Douban_APIS = {
    /*
    图书搜索
 
    image  图书缩略图
    title  图书名称
    publisher  出版社
    author  作者
    price  价格
    pages  图书总页数
    */
    book_search: BaseURL + "book/search",
 
    /*
    图书详情
 
    image  图书缩略图
    title  图书名称
    publisher  出版社
    author  作者
    price  价格
    pages  图书总页数
    summary  图书简介
    author_intro  作者简介
    */
    book_detail_id: BaseURL + "book/",
 
    /*
    电影搜索
 
    images.medium  电影图像
    title  电影名称
    casts  电影演员  数据需要再处理
    rating.average  电影评分
    year  电影上映时间
    genres  电影标签
    alt  电影详情url
    */
    movie_search: BaseURL + "movie/search",
}
 
// 导出
module.exports = Douban_APIS;

 

4.util.js

1
2
3
4
5
6
7
8
9
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
/*
    2.定义工具类
 
    实现功能:定义多个属性,在项目中会使用的一些功能。包括:获取屏幕尺寸、loading组件、GET请求方法
 
    包含组件:
 
    外部引用:
 
        GET请求方法需要从外部引入url、请求成功的回调方法、请求失败的回调方法。
*/
 
import React, { Component } from 'react';
import {
    AppRegistry,
    StyleSheet,
    Text,
    View,
    Dimensions, // 用于获取设备屏幕的宽高
    ActivityIndicator // loading组件
} from 'react-native';
 
// 定义对象,将提供的功能作为属性存放
var Util = {
    // 屏幕尺寸
    windowSize: {
        width: Dimensions.get("window").width,
        height: Dimensions.get("window").height
    },
 
    // 基于fetch的get方法  只负责下载数据,下载后的处理操作在回调方法中实现
    // successCallback 数据下载成功的回调方法,在组件中实现
    // failCallback 数据下载失败的回调方法,在组件中实现
    getRequest: function(url, successCallback, failCallback) {
        fetch(url)
            .then((response) => response.json())
            .then((responseData) => successCallback(responseData))
            .catch((error) => failCallback(error));
    },
 
    // loading效果
    loading: <ActivityIndicator style={{marginTop:200}} />
}
 
// 导出
module.exports = Util;

 

5.searchBar.js

1
2
3
4
5
6
7
8
9
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
/*
    3.实现功能:封装搜索栏组件,包括文本输入框和搜索按钮
 
    包含组件:
 
    外部传入:
        输入框和按钮的属性设置由外部引入。例如:placeholder、onPress、onChangeText
        使用...this.props将外部传入的属性设置给TextInput和TouchableOpacity
 
        注意:指定高度、边框颜色、边框线框
*/
 
import React, { Component } from 'react';
import {
    AppRegistry,
    StyleSheet,
    Text,
    View,
    TextInput,
    TouchableOpacity
} from 'react-native';
 
// 定义组件
var SearchBar = React.createClass({
    render: function() {
        return (
            <View style={styles.container}>
                <View style={styles.inputContainer}>
                    <TextInput style={styles.input} {...this.props} />
                </View>
 
                <TouchableOpacity style={styles.btn} {...this.props}>
                    <Text style={styles.search}>搜索</Text>
                </TouchableOpacity>
            </View>
        );
    }
});
 
var styles = StyleSheet.create({
    container: {
        flexDirection: "row",
        justifyContent: "flex-end",
        alignItems: "center",
        height: 44,
        marginTop: 10
    },
    inputContainer: {
        flex: 1,
        marginLeft: 5
    },
    input: {
        flex: 1,
        height: 44,
        borderWidth: 1,
        borderRadius: 4,
        borderColor: "#CCC",
        paddingLeft: 5
    },
    btn: {
        width: 55,
        height: 44,
        marginLeft: 5,
        marginRight: 5,
        backgroundColor: "#23BEFF",
        borderRadius: 4,
        justifyContent: "center",
        alignItems: "center"
    },
    search: {
        flex: 1,
        color: "#fff",
        fontSize: 15,
        fontWeight: "bold",
        textAlign: "center",
        lineHeight: 34
    }
});
 
module.exports = SearchBar;

 

6.left_icon.js

1
2
3
4
5
6
7
8
9
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
/*
    4.实现功能:封装返回按钮图标,不使用图片
 
    包含组件:
 
    外部传入:
*/
import React, { Component } from 'react';
import {
    AppRegistry,
    StyleSheet,
    Text,
    View
} from 'react-native';
 
// 定义组件 返回图标
var Icon = React.createClass({
    render: function() {
        return (
            <View>
                <View style={styles.go}></View>
            </View>
        );
    }
});
 
var styles = StyleSheet.create({
    go: {
        width: 15,
        height: 15,
        borderLeftWidth: 2,
        borderBottomWidth: 2,
        borderColor: "#fff",
        marginLeft: 10,
        transform: [{rotate: "45deg"}] // 将一个矩形框旋转了45度
    }
});
 
module.exports = Icon;

 

7.header.js

1
2
3
4
5
6
7
8
9
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
/*
5.实现功能:封装header,在头部展示标题和返回按钮
 
包含组件:
 
外部传入:
    navigator 点击返回按钮返回上一级页面
    initObj(backName、barTitle)  返回按钮的名称、标题
*/
 
import React, { Component } from 'react';
import {
    AppRegistry,
    StyleSheet,
    Text,
    View,
    TouchableOpacity
} from 'react-native';
 
// 导入左侧按钮
var Icon = require("./left_icon");
 
var Header = React.createClass({
    render: function() {
        // 获取obj对象,包括:backName(按钮名称)、barTitle
        var headerContent = this.props.initObj;
 
        return (
            <View style={styles.header}>
                <TouchableOpacity style={styles.left_btn} onPress={this._pop}>
                    <Icon />
                    <Text style={styles.btn_next}>{headerContent.backName}</Text>
                </TouchableOpacity>
 
                <View style={styles.title_container}>
                    <Text style={styles.title} numberOfLines={1}>{headerContent.barTitle}</Text>
                </View>
            </View>
        );
    },
    // 返回按钮事件处理方法
    _pop: function() {
        this.props.navigator.pop();
    }
});
 
var styles = StyleSheet.create({
    header: {
        height: 44,
        backgroundColor: "#3497FF",
        flexDirection: "row",
        justifyContent: "center",
        alignItems: "center"
    },
    left_btn: {
        flexDirection: "row",
        justifyContent: "center",
        alignItems: "center"
    },
    btn_text: {
        color: "#fff",
        fontSize: 17,
        fontWeight: "bold"
    },
    title_container: {
        flex: 1,
        justifyContent: "center",
        alignItems: "center"
    },
    title: {
        color: "#fff",
        fontSize: 18,
        fontWeight: "bold",
        lineHeight: 18,
        width: 200
    }
});
 
module.exports = Header;

  

8.navigation.js

1
2
3
4
5
6
7
8
9
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
47
48
49
50
51
52
/*
    6.实现功能:封装导航器初始化设置
 
    包含组件:Navigator
 
    外部传入:
        component  需要展示的页面组件
        route对象  必须添加component属性; 如果需要传值可以添加passProps属性
*/
 
import React, { Component } from 'react';
import {
    AppRegistry,
    StyleSheet,
    Text,
    View
} from 'react-native';
 
// npm install react-native-deprecated-custom-components --save
 
import CustomerComponents, { Navigator } from 'react-native-deprecated-custom-components';
 
var Navigation = React.createClass({
    render: function() {
        // 创建route对象,约定格式
        var rootRoute = {
            component: this.props.component,
            passProps: {
                 
            }
        };
 
        return (
            <Navigator
                initialRoute={rootRoute}
                configureScene={() => {return Navigator.SceneConfigs.PushFromRight}}
                renderScene={(route,navigator) => {
                    var Component = route.component;
                    return (
                        <View style={{flex:1}}>
                            <Component
                                navigator={navigator}
                                route={route}
                                {...route.passProps}/>
                        </View>
                    );
                }}/>
        );
    }
});
 
module.exports = Navigation;

  

9.customWebView.js

1
2
3
4
5
6
7
8
9
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
/*
    7.实现功能:封装WebView,根据传入的url展示网页信息
 
    包含组件:Header、WebView
 
    外部传入:
        给Header设置:navigator、initObj(backName、title)
        给WebView设置:source(url)
*/
 
import React, { Component } from 'react';
import {
    AppRegistry,
    StyleSheet,
    Text,
    View,
    WebView
} from 'react-native';
 
var Header = require("./header");
 
var CustomWebView = React.createClass({
    render: function() {
        return (
            <View style={{backgroundColor:"white",flex:1}}>
                <Header
                    navigator={this.props.navigator}
                    initObj={{
                        backName:this.props.backName,
                        barTitle:this.props.title
                    }}/>
                <WebView
                    startInLoadingState={true}
                    contentInset={{top:-44,bottom:-120}}
                    source={{uri:this.props.url}}/>
            </View>
        );
    }
});
 
module.exports = CustomWebView;

  

10.book_item.js

1
2
3
4
5
6
7
8
9
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/*
    8.图书列表 item
 
    实现功能:展示图书信息,点击item进入图书详情页面
 
    包含组件:基本组件
 
    外部传入:
 
    book  图书对象
    onPress事件处理方法  通过...this.props绑定,需要设置参数,即图书id
 
    需要使用的字段:
 
        image  图书缩略图
        title  图书名称
        publisher  出版社
        author  作者
        price  价格
        pages  图书总页数
*/
 
import React, { Component } from 'react';
import {
    AppRegistry,
    StyleSheet,
    Text,
    View,
    Image,
    TouchableOpacity
} from 'react-native';
 
var BookItem = React.createClass({
    render: function() {
        var book = this.props.book;
 
        return (
            <TouchableOpacity style={styles.item} {...this.props}>
                {/* 图书图像 */}
                <View style={styles.imageContainer}>
                    <Image style={styles.image} source={{uri:book.image}}/>
                </View>
                {/* 图书信息 */}
                <View style={styles.contentContainer}>
                    <View style={styles.textContainer}>
                        <Text numberOfLines={1}>{book.title}</Text>
                    </View>
                    <View style={styles.textContainer}>
                        <Text style={styles.publisher_author} numberOfLines={1}>{book.publisher}</Text>
                    </View>
                    <View style={styles.textContainer}>
                        <Text style={styles.publisher_author} numberOfLines={1}>{book.author}</Text>
                    </View>
                    <View style={{flexDirection:"row",flex:1,alignItems:"center"}}>
                        <Text style={styles.price}>{book.price}</Text>
                        <Text style={styles.pages}>{book.pages}页</Text>
                    </View>
                </View>
            </TouchableOpacity>
        )
    }
});
 
var styles = StyleSheet.create({
    item: {
        flexDirection: "row",
        height: 120,
        padding: 10
    },
    imageContainer: {
        justifyContent: "center",
        alignItems: "center"
    },
    image: {
        width: 80,
        height: 100
    },
    contentContainer: {
        flex: 1,
        marginLeft: 15
    },
    textContainer: {
        flex: 1,
        justifyContent: "center"
    },
    publisher_author: {
        color: "#A3A3A3",
        fontSize: 13
    },
    price: {
        color: "#2BB2A3",
        fontSize: 16
    },
    pages: {
        marginLeft: 10,
        color: "#A7A0A0"
    }
});
 
module.exports = BookItem;

  

11.book_list.js

1
2
3
4
5
6
7
8
9
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
/*
    9.图书列表模块:搜索栏、图书列表
    图书列表的内容:通过调用图书搜索接口获得多条图书数据
    图书列表Item是单独封装的
*/
 
import React, { Component } from 'react';
import {
    AppRegistry,
    StyleSheet,
    Text,
    View,
    Image,
    TouchableOpacity,
    ListView,
    ScrollView
} from 'react-native';
 
// 从common模块导入内容
var Util = require("./../common/util");
var SearchBar = require("./../common/searchBar");
var ServiceURL = require("./../common/service");
var BookItem = require("./book_item");
var BookDetail = require("./book_detail");
 
var BookList = React.createClass({
    getInitialState: function() {
        var ds = new ListView.DataSource({
            rowHasChanged: (oldRow, newRow) => oldRow !== newRow
        });
 
        return {
            // dataSource
            dataSource: ds,
            // 网络请求状态标识
            show: false,
            // 搜索关键字
            // 作用:1.搜索接口需要设置搜索内容 2.点击搜索按钮时,修改关键字内容,重新请求数据,重新渲染
            keywords: "react"
        };
    },
    getData: function() {
        // 开启loading,每次搜索时都需要重新下载显示数据
        this.setState({
            show: false
        });
 
        // 请求数据
        var that = this;
        var url = ServiceURL.book_search + "?count=20&q=" + this.state.keywords;
 
        Util.getRequest(url, function(data) {
            // 请求成功回调函数
            /*
                如果没有相关书籍,使用alert提示
                https://api.douban.com/v2/book/search?count=20&q=react
                {"count":0,"start":0,"total":0,"books":[]}
             */
 
            if (!data.books || data.books.length == 0) {
                return alert("未查询到相关书籍");
            }
 
            // 设置下载状态和数据源
            var ds = new ListView.DataSource({
                rowHasChanged: (oldRow, newRow) => oldRow !== newRow
            });
 
            that.setState({
                show: true,
                dataSource: ds.cloneWithRows(data.books)
            });
 
        }, function(error) {
            // 请求失败回调函数
            alert(error);
        })
    },
    // TextInput的onChangeText事件处理方法
    _changeText: function(text) {
        this.setState({
            keywords: text
        });
    },
    _searchPress: function() {
        this.getData();
    },
    _showDetail:function(bookID){
        var detailRoute = {
            component: BookDetail,
            passProps:{
                bookID: bookID
            }
        }
 
        this.props.navigator.push(detailRoute);
    },
    // 布局
    render: function() {
        return (
            <ScrollView>
                <SearchBar
                    placeholder="请输入图书的名称"
                    onPress={this._searchPress}
                    onChangeText={this._changeText}/>
                {
                    // 请求数据时显示loading,数据请求成功后显示ListView
                    this.state.show ?
                        <ListView
                            dataSource={this.state.dataSource}
                            initialListSize={10}
                            renderRow={this._renderRow}
                            renderSeparator={this._renderSeparator}/>
                    : Util.loading
                }
            </ScrollView>
        );
    },
    componentDidMount: function() {
        // 请求数据
        this.getData();
    },
    _renderRow: function(book) {
        return <BookItem book={book} onPress={this._showDetail.bind(this,book.id)}/>
    },
    // 分割线
    _renderSeparator: function(sectionID: number, rowID: number) {
        var style = {
            height: 1,
            backgroundColor: "#CCCCCC"
        }
 
        return <View style={style} key={sectionID+rowID} />
    }
});
 
var styles = StyleSheet.create({
    //
});
 
module.exports = BookList;

  

12.book_detail.js

1
2
3
4
5
6
7
8
9
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
/*
    10.图书详情
 
    实现功能:展示图书详情,包括:图书信息、图书简介、作者简介
 
    包含组件:基本组件、BookItem(图书信息使用BookItem展示)
 
    外部传入:
 
    需要使用的字段:
 
        image  图书缩略图
        title  图书名称
        publisher  出版社
        author  作者
        price  价格
        pages  图书总页数
        summary  图书简介
        author_intro  作者简介
*/
 
import React, { Component } from 'react';
import {
    AppRegistry,
    StyleSheet,
    Text,
    View,
    Image,
    ScrollView
} from 'react-native';
 
// 引入
var ServiceURL = require("./../common/service");
var Util = require("./../common/util");
var Header = require("./../common/header");
var BookItem = require("./book_item");
 
// 创建组件类
var BookDetail = React.createClass({
    getInitialState:function() {
        return {
            bookData:null // 图书对象详情信息
        }
    },
    getData:function(){
        // 获取图书信息
        var that = this;
        var url = ServiceURL.book_detail_id + this.props.bookID;
        Util.getRequest(url,function(data){
            that.setState({
                bookData:data
            });
        },function(error){
            alert(error);
        });
    },
    render:function(){
        return (
            <ScrollView style={styles.container}>
                {
                    this.state.bookData ?
                        <View>
                            <Header
                                initObj={{backName:"图书",barTitle:this.state.bookData.title}}
                                navigator={this.props.navigator}/>
                            <BookItem book={this.state.bookData}/>
                            <View>
                                <Text style={styles.title}>图书简介</Text>
                                <Text style={styles.text}>{this.state.bookData.summary}</Text>
                            </View>
                            <View style={{marginTop:10}}>
                                <Text style={styles.title}>作者简介</Text>
                                <Text style={styles.text}>{this.state.bookData.author_intro}</Text>
                            </View>
                            <View style={{height:55}}></View>
                        </View>
                    : Util.loading
                }
            </ScrollView>
        );
    },
    // 组件挂载以后,进行网络请求
    componentDidMount:function(){
        // 请求图书详情
        this.getData();
    }
});
 
var styles = StyleSheet.create({
    container:{
        flex:1,
        backgroundColor:"white"
    },
    title:{
        fontSize:16,
        marginTop:10,
        marginLeft:10,
        marginBottom:10,
        fontWeight:"bold"
    },
    text:{
        marginLeft:10,
        marginRight:10,
        color:"#000D22"
    }
});
 
module.exports = BookDetail;

  

13.movie_item.js

1
2
3
4
5
6
7
8
9
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
/*
    11.电影列表item
 
    实现功能:展示电影信息,点击item进入电影详情页面
 
    包含组件:基本组件
 
    外部传入:
 
    movie  电影对象
    onPress  通过...this.props绑定,需要设置参数:电影名称、电影详情页面url
 
    需要使用的字段:
        images.medium  电影图像
        title  电影名称
        casts  电影演员  数据需要再处理
        rating.average  电影评分
        year  电影上映时间
        genres  电影标签
        alt  电影详情url
*/
import React, { Component } from 'react';
import {
    AppRegistry,
    StyleSheet,
    Text,
    View,
    Image,
    TouchableOpacity
} from 'react-native';
 
var MovieItem = React.createClass({
    render:function() {
        var movie = this.props.movie;
 
        // 提取演员姓名
        // 原始数据结构:数组元素是描述演员的对象,对象中包含演员名字
        // 需要遍历数组,把每个演员的名字存在一个新的数组中
        var actors = [];
        for(var i in movie.casts){
            actors.push(movie.casts[i].name);
        }
 
        return (
            <TouchableOpacity style={styles.item} {...this.props}>
                <View style={styles.imageContainer}>
                    <Image style={styles.image} resizeMode="contain" source={{uri:movie.images.medium}}/>
                </View>
                <View style={styles.contentContainer}>
                    <View style={styles.textContainer}>
                        <Text style={styles.text} numberOfLines={1}>名称:{movie.title}</Text>
                    </View>
                    <View style={styles.textContainer}>
                        <Text style={styles.text} numberOfLines={1}>演员:{actors}</Text>
                    </View>
                    <View style={styles.textContainer}>
                        <Text style={styles.text} numberOfLines={1}>评分:{movie.rating.average}</Text>
                    </View>
                    <View style={styles.textContainer}>
                        <Text style={styles.text} numberOfLines={1}>时间:{movie.year}</Text>
                    </View>
                    <View style={styles.textContainer}>
                        <Text style={styles.text} numberOfLines={1}>标签{movie.genres}</Text>
                    </View>
                </View>
            </TouchableOpacity>
        );
    }
});
 
var styles = StyleSheet.create({
    item:{
        flexDirection:"row",
        height:120,
        padding:10
    },
    imageContainer:{
        justifyContent:"center",
        alignItems:"center"
    },
    image:{
        width:80,
        height:110
    },
    contentContainer:{
        flex:1,
        marginLeft:15
    },
    textContainer:{
        flex:1,
        justifyContent:"center"
    },
    text:{
        color:"black"
    }
});
 
module.exports = MovieItem;

 

14.movie_list.js

1
2
3
4
5
6
7
8
9
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/*
    12.电影列表模块:搜索框、电影列表
    电影列表的内容:通过调用电影搜索接口获得多条电影数据
    电影列表Item是单独封装的
*/
import React, { Component } from 'react';
import {
    AppRegistry,
    StyleSheet,
    Text,
    View,
    Image,
    TouchableOpacity,
    ListView,
    ScrollView
} from 'react-native';
 
var SearchBar = require("./../common/searchBar");
var Util = require("./../common/util");
var ServiceURL = require("./../common/service");
var MovieItem = require("./movie_item");
var MovieWebView = require("./../common/customWebView");
 
var MovieList = React.createClass({
    getInitialState:function() {
        var ds = new ListView.DataSource({
            rowHasChanged:(oldRow,newRow) => oldRow!==newRow
        });
 
        return {
            dataSource: ds,
            show: false,
            keywords:"哈利波特"
        };
    },
    _changeText:function(text){
        this.setState({
            keywords:text
        });
    },
    _searchPress:function(){
        this.getData();
    },
    _showDetail:function(title,url){
        var detailRoute = {
            component:MovieWebView,
            passProps:{
                backName:"电影",
                title:title,
                url:url
            }
        };
 
        // 推出
        this.props.navigator.push(detailRoute);
    },
    getData:function(){
        this.setState({
            show:false
        });
 
        var that = this;
        var url = ServiceURL.movie_search + "?count=20&q=" + this.state.keywords;
 
        /*
            https://api.douban.com/v2/movie/search?count=20&q=哈利波特
            {"count":0,"start":0,"total":0,"books":[]}
         */
 
        Util.getRequest(url,function(data){
            if(!data.subjects||data.subjects.length==0){
                return alert("未找到相关电影");
            }
 
            var ds = new ListView.DataSource({
                rowHasChanged:(oldRow,newRow) => oldRow!==newRow
            });
 
            var movies = data.subjects;
 
            that.setState({
                show:true,
                dataSource:ds.cloneWithRows(movies)
            });
        },function(error){
            alert(error);
        });
 
    },
    render:function(){
        return (
            <ScrollView>
                <SearchBar
                    placeholder="请输入电影的名称"
                    onPress={this._searchPress}
                    onChangeText={this._changeText}/>
                {
                    this.state.show ?
                        <ListView
                            dataSource={this.state.dataSource}
                            initialListSize={10}
                            renderRow={this._renderRow}
                            renderSeparator={this._renderSeparator}/>
                    : Util.loading
                }
            </ScrollView>
        );
    },
    componentDidMount:function(){
        // 请求数据
        this.getData();
    },
    _renderRow:function(movie){
        return <MovieItem movie={movie} onPress={this._showDetail.bind(this, movie.title, movie.alt)}/>;
    },
    // 分割线
    _renderSeparator:function(sectionID:number,rowID:number){
        var style = {
            height: 1,
            backgroundColor:"#CCCCCC"
        };
 
        return <View style={style} key={sectionID+rowID}></View>
    }
});
 
var styles = StyleSheet.create({
 
});
 
module.exports = MovieList;

 

15.效果图

 

 

 

posted @   每天都要进步一点点  阅读(302)  评论(0)    收藏  举报
编辑推荐:
· 从零实现富文本编辑器#3-基于Delta的线性数据结构模型
· 记一次 .NET某旅行社酒店管理系统 卡死分析
· 长文讲解 MCP 和案例实战
· Hangfire Redis 实现秒级定时任务,使用 CQRS 实现动态执行代码
· Android编译时动态插入代码原理与实践
阅读排行:
· 使用TypeScript开发微信小程序(云开发)-入门篇
· 没几个人需要了解的JDK知识,我却花了3天时间研究
· 在SqlSugar的开发框架中增加对低代码EAV模型(实体-属性-值)的WebAPI实现支持
· C#高性能开发之类型系统:从 C# 7.0 到 C# 14 的类型系统演进全景
· .NET Core中的配置Configuration实战
点击右上角即可分享
微信分享提示