看到一个很好的BBCNews新闻客户端,但只有IOS版本,再次将它改造成Android版本。
主要技术
- 访问网络请求, 过滤内容, 获取数据.
- 显示多媒体页面, 图片, 视频, 链接等.
配置项目
初始化项目BBCNews, 直接用npm install,在package.json中写的可能版本冲突
Html解析库: htmlparser, 时间处理库: moment, 线性梯度库: react-native-linear-gradient, 视频库: react-native-video.
初始化主模块index.android.js, 使用Navigator导航页面, 首页组件Feed模块.
import Feed from './components/Feed.js';
class BBCNew extends Component {
render() {
let defaultName='Feed';
let defaultComponent=Feed;
return (
<Navigator
initialRoute=
//配置场景
configureScene=
{
(route) => {
//这个是页面之间跳转时候的动画,具体有哪些?可以看这个目录下,有源代码的: node_modules/react-//native/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js
return Navigator.SceneConfigs.VerticalDownSwipeJump;
}
}
renderScene={
(route, navigator) =>
{
let Component = route.component;
return <Component {...route.params} navigator={navigator}
route={route}
// Function to call when a new scene should be displayed
/>
}
} />
);
}
}
新闻列表
Feed页面, 主要以列表形式, 即ListView标签, 显示新闻. 未加载完成时, 调用页面加载提示符ActivityIndicatorIOS, 显示动画
render() {
// 未加载完成时, 调用加载页面
if (!this.state.loaded) {
return this._renderLoading();
}
// ...
}
_renderLoading() {
return (
<View style=>
<ActivityIndicator
animating={this.state.isAnimating}
style=
size="large"/>
</View>
);
加载完成后, 调用ListView显示页面, renderRow渲染每一行, refreshControl加载页面的过场.
return (
<ListView
testID={"Feed Screen"}
dataSource={this.state.dataSource}
renderRow={this._renderStories.bind(this)}
style=
contentInset=
scrollEventThrottle={200}
{...this.props}
refreshControl={
<RefreshControl
refreshing={this.state.isRefreshing}
onRefresh={this._fetchData.bind(this)}
tintColor='#BB1919'
title="Loading..."
progressBackgroundColor="#FFFF00"
/>}
/>
);
每一行使用Story模块渲染.
_renderStories(story) {
return (
<Story story={story} navigator={this.props.navigator}/>
);
}
启动页面的时候, 使用fetch方法加载数据.
componentDidMount() {
this._fetchData();
}
通过访问BBC的网络请求, 异步获取数据. 使用_filterNews过滤需要的数据, 把数据设置入每一行, 修改状态setState, 重新渲染页面.
_fetchData() {
this.setState({isRefreshing: true});
fetch(`http://trevor-producer-cdn.api.bbci.co.uk/content${this.props.collection || '/cps/news/world'}`)
.then((response) => response.json())
.then((responseData) => this._filterNews(responseData.relations))
.then((newItems) => {
this.setState({
dataSource: this.state.dataSource.cloneWithRows(newItems),
loaded: true,
isRefreshing: false,
isAnimating: false
})
}).done();
}
then语法是promiser的chaing结构
Because the then method returns a Promise, you can easily chain then calls. Values returned from the onFulfilled or onRejected callback functions will be automatically wrapped in a resolved promise.
列表项提供分类显示功能, 点击类别, 可以重新加载所选类型的新闻, 把Feed页面再次添加至导航navigator, 即页面栈.
_pressedCollection(collection) {
this.props.navigator.push({
component: Feed,
title: collection.content.name,
params: {
collection: collection.content.id,
navigator: this.props.navigator
}
});
}
点击列表项, 跳转至详情页面StoryDetail.
_pressedStory(story) {
this.props.navigator.push({
component: StoryDetail,
title: this._truncateTitle(story.content.name),
params: {story, navigator: this.props.navigator}
});
}
新闻详情
主要是解析HTML页面, 加载并显示, 除了文字之外, 会显示图片\视频\超链接等样式. 渲染使用动态元素, 状态state的elements属性.
render() {
if (this.state.loading) {
return (
<Text>Loading</Text>
);
}
return this.state.elements;
}
页面启动时, 加载数据. 在_fetchStoryData方法中, 进行处理, 使用回调返回数据. 主要内容body与多媒体media通过滚动视图ScrollView的形式显示出来.
componentDidMount() {
this._fetchStoryData(
// media表示视频或图片.
(result, media) => {
const rootElement = result.find(item => {
return item.name === 'body';
});
XMLToReactMap.createReactElementsWithXMLRoot(rootElement, media)
.then(array => {
var scroll = React.createElement(ScrollView, {
contentInset: {top: 0, left: 0, bottom: 64, right: 0},
style: {flex: 1, flexDirection: 'column', backgroundColor: 'white'},
accessibilityLabel: "Story Detail"
}, array);
this.setState({loading: false, elements: scroll});
});
}
);
}
处理数据, 使用fetch方法, 分离视频与图片, 还有页面, 通过回调cb(callback)的处理返回数据.
_fetchStoryData(cb) {
// 提取数据, 转换JSON格式, 图片过滤, 视频过滤, 组合relations, 解析.
fetch(`http://trevor-producer-cdn.api.bbci.co.uk/content${this.props.story.content.id}`)
.then((response) => response.json())
.then((responseData) => {
const images = responseData.relations.filter(item => {
return item.primaryType === 'bbc.mobile.news.image';
});
const videos = responseData.relations.filter(item => {
return item.primaryType === 'bbc.mobile.news.video';
});
const relations = {images, videos};
this._parseXMLBody(responseData.body, (result) => {
cb(result, relations);
});
}).done();
}
使用Tautologistics解析dom数据与body数据. DOM, 即Document Object Model, 文件对象模型.
_parseXMLBody(body, cb) {
var handler = new Tautologistics.NodeHtmlParser.DefaultHandler(
function (error, dom) {
cb(dom)
}, {enforceEmptyTags: false, ignoreWhitespace: true});
var parser = new Tautologistics.NodeHtmlParser.Parser(handler);
parser.parseComplete(body);
}
XML解析类XMLToReactMap比较复杂, 不做过多介绍, 参考源码.
Codes
References
React Native 实例 - BBC新闻客户端 Mystra