surmon.me.native代码分析笔记
来源:互联网 发布:淘宝互刷群沈阳 编辑:程序博客网 时间:2024/05/17 05:55
surmon.me.native代码分析笔记
surmon.me.native是一个适合入门学习的react-native博客内容展示项目,代码组织优良值得借鉴。
基础样式
作者把基础样式进行了统一编写,方便维护修改。
style/size.js
import { Dimensions, Platform } from 'react-native';const { width, height } = Dimensions.get('window');const screenHeight = width < height ? height : width;const screenWidth = width < height ? width : height;export default { // Window Dimensions screen: { height: screenHeight, width: screenWidth, widthHalf: screenWidth * 0.5, widthThird: screenWidth * 0.333, widthTwoThirds: screenWidth * 0.666, widthQuarter: screenWidth * 0.25, widthThreeQuarters: screenWidth * 0.75, }, // Navbar navbarHeight: (Platform.OS === 'ios') ? 50 : 50, statusBarHeight: (Platform.OS === 'ios') ? 16 : 24, // Padding padding: 20};
style/fonts.js
import { Platform } from 'react-native';function lineHeight(fontSize) { const multiplier = (fontSize > 20) ? 0.1 : 0.33; return parseInt(fontSize + (fontSize * multiplier), 10);}const base = { fontSize: 14, lineHeight: lineHeight(14), ...Platform.select({ ios: { // fontFamily: 'HelveticaNeue', }, android: { fontFamily: 'Roboto', }, }),};export default { base: { ...base }, h1: { ...base, fontSize: base.fontSize * 1.75, lineHeight: lineHeight(base.fontSize * 2) }, h2: { ...base, fontSize: base.fontSize * 1.5, lineHeight: lineHeight(base.fontSize * 1.75) }, h3: { ...base, fontSize: base.fontSize * 1.25, lineHeight: lineHeight(base.fontSize * 1.5) }, h4: { ...base, fontSize: base.fontSize * 1.1, lineHeight: lineHeight(base.fontSize * 1.25) }, h5: { ...base },};
欢迎页 welcome.js
编写欢迎页面提高用户体验,据说可以防止启动白屏。
// Init Layoutimport Layout from './layout.js';// Stylesimport { AppColors, AppSizes } from '@app/style';const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: AppColors.background, alignItems: 'center', justifyContent: 'center' }, launchImage: { position: 'absolute', left: 0, top: 0, width: AppSizes.screen.width, height: AppSizes.screen.height }});class Welcome extends Component { componentWillMount () { var navigator = this.props.navigator; setTimeout (() => { navigator.replace({component: Layout, passProps: { navigator }}); }, 1666); } render () { return ( <View style={styles.container}> <StatusBar translucent={true} backgroundColor={'#rgba(0, 0, 0, 0)'} barStyle="light-content" showHideTransition='slide' hidden={false} /> <Image style={styles.launchImage} source={require('@app/images/android-launch/launch-image.png')}></Image> </View> ); }}export default Welcome;
菜单页 menu.js
作者实现了安卓版本的屏幕左划出效果,编写子组件MneuHeader(菜单头部),MenuList(菜单列表)、MenuItem(菜单列表项)组合成Menu组件。
class Menu extends Component { constructor(props) { super(props); this.state = { selectedItem: props.initialEntry || props.entries[0].id//默认指向第一个列表 } } //点击菜单任意列表时,关闭菜单 _onSectionChange = (section) => { this.setState({selectedItem: section}); this._drawer.closeDrawer(); } //打开菜单 _openMenu = () => { this._drawer.openDrawer(); } //渲染菜单 _renderNavigationView = () => { return ( <View style={this.props.containerStyle}> <MneuHeader userInfo={this.props.userInfo}/> <MenuList items={this.props.entries} selectedItem={this.state.selectedItem} tintColor={this.props.tintColor} onSectionChange={this._onSectionChange} /> </View> ) } //渲染菜单内容 _renderContent() { const element = this.props.entries.find(entry => entry.id === this.state.selectedItem).element; if (element) { return React.cloneElement(element, {openMenu: this._openMenu}); } } //使用DrawerLayoutAndroid组件,渲染菜单 render() { return ( <DrawerLayoutAndroid ref={(ref) => {this._drawer = ref}} {...this.props} renderNavigationView={this._renderNavigationView}> {this._renderContent()} </DrawerLayoutAndroid> ) }}export default Menu;
API
作者将网络请求进行封装,增加了API的复用性。
import showToast from '@app/utils/toast';// apiconst baseApi = 'https://api.surmon.me';const fetchService = (url, options = {}) => { return fetch(url, options) .then(response => { return response.json(); }) .then(json => { showToast(json.message); return json; }) .catch(error => { showToast('网络错误'); console.warn(error); });};// apisexport default class Api { // 获取文章列表 static getArticleList(page) { const queryParams = page ? `?page=${page}` : ''; return fetchService(`${baseApi}/article${queryParams}`); } // 获取文章详情 static getArticleDetail(article_id) { return fetchService(`${baseApi}/article/${article_id}`); } // 给文章或主站点赞 static likeArticleOrSite(like_data) { return fetchService(`${baseApi}/like`, { method: 'POST', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', }, body: JSON.stringify(like_data) }) } // 获取用户信息 static getUserInfo() { return fetchService(`${baseApi}/auth`) }}
Pages
程序主要包含文章列表页、文章内容页、项目页、关于页等4个页面。
文章列表页:
class ArticleList extends Component { constructor(props) { super(props); this.state = { loading: false, firstLoader: true }; // 获取本地存储的点赞记录 AsyncStorage.getItem('user_like_history') .then(historyLikes => { this.historyLikes = historyLikes ? JSON.parse(historyLikes) : [] }).catch(err => { console.log(err) }) } // 文章列表项目渲染 renderRowView(article, sectionID, rowID) { let liked = false; if (this.historyLikes && this.historyLikes.length && this.historyLikes.includes(article.id)) { liked = true; } return ( <ArticleListItem article={article} rowID={rowID} liked={liked} key={`sep:${sectionID}:${rowID}`} navigator={this.props.navigator} /> ) } // 请求文章数据 getArticles(page = 1, callback, options) { this.setState({ loading: true }); Api.getArticleList(page) .then(data => { this.setState({ loading: false, firstLoader: false }); const pagination = data.result.pagination; callback(data.result.data, { allLoaded: pagination.total_page < 2 || pagination.current_page >= pagination.total_page, }); }) .catch(err => { console.log(err); }); } // 在第一次读取时没有行显示时呈现视图,refreshcallback函数的函数调用刷新列表 renderEmptyView(refreshCallback) { return ( <View style={customListStyles.defaultView}> <Text style={customListStyles.defaultViewTitle}>暂无数据,下拉刷新重试</Text> <TouchableHighlight underlayColor={AppColors.textDefault} onPress={refreshCallback}> <Ionicon name="md-refresh" size={22} style={{color: AppColors.textDefault}}/> </TouchableHighlight> </View> ); } // 翻页正常状态 renderPaginationWaitingView(paginateCallback) { return ( <TouchableHighlight onPress={paginateCallback} underlayColor={AppColors.textMuted} style={customListStyles.paginationView}> <Text style={ [customListStyles.actionsLabel, { fontSize: AppFonts.base.fontSize, color: AppColors.textDefault }] }>加载更多</Text> </TouchableHighlight> ); } // 翻页在请求时的状态 renderPaginationFetchingView() { return ( <View style={[customListStyles.paginationView, { backgroundColor: 'transparent' }]}> <AutoActivityIndicator size={'small'} /> </View> ) } // 翻页在文章全部加载完时的状态 renderPaginationAllLoadedView() { return ( <View style={[customListStyles.paginationView, { height: AppSizes.padding * 1.5, marginBottom: AppSizes.padding / 2, backgroundColor: AppColors.background }]}> <Text style={ [customListStyles.actionsLabel, { fontSize: AppFonts.base.fontSize, color: AppColors.textMuted }] }>到底啦~</Text> </View> ) } render(){ return ( <View style={styles.listViewContainer}> <GiftedListView style={styles.ArticleListView} firstLoader={true} initialListSize={10} withSections={false} enableEmptySections={true} rowView={this.renderRowView.bind(this)} onFetch={this.getArticles.bind(this)} rowHasChanged={(r1,r2) => { r1.id !== r2.id }} emptyView={this.renderEmptyView} refreshable={true} refreshableTitle={'更新数据...'} refreshableTintColor={AppColors.brand.black} refreshableColors={[AppColors.brand.primary]} pagination={true} paginationFetchingView={this.renderPaginationFetchingView} paginationWaitingView={this.renderPaginationWaitingView} paginationAllLoadedView={this.renderPaginationAllLoadedView} /> { this.state.firstLoader ? <View style={styles.ArticleListIndicator}> <AutoActivityIndicator /> { Platform.OS == 'ios' ? <Text style={styles.ArticleListIndicatorTitle}>数据加载中...</Text> : null } </View> : null } </View> ) }}export default ArticleList;
文章内容页使用react-native-simple-markdown组件展示文章内容。
// componentclass Detail extends Component { constructor(props) { super(props); this.state = { loading: false, article: this.props.article, articleContent: '', liked: false } // 获取本地存储记录 AsyncStorage.getItem('user_like_history') .then(historyLikes => { this.historyLikes = historyLikes ? JSON.parse(historyLikes) : [] }).catch(err => { console.log(err) }) } // 组件加载完成 componentDidMount() { BackAndroid.addEventListener('hardwareBackPress', HandleBackBtnPress.bind(this)); this.setState({loading: true}); Api.getArticleDetail(this.props.article.id).then(data => { this.setState({ loading: false, article: data.result, articleContent: data.result.content }) if (this.historyLikes.length) { if (this.historyLikes.includes(this.state.article.id)) { this.setState({ liked: true }); } } }).catch(err => { console.log(err); }) } // 喜欢文章 likeArticle() { if (this.state.liked) return false; Api.likeArticleOrSite({ type: 2, id: this.props.article.id }).then(data => { this.state.liked = true; this.state.article.meta.likes += 1; this.forceUpdate(); this.historyLikes.push(this.state.article.id); AsyncStorage.setItem('user_like_history', JSON.stringify(this.historyLikes)) }).catch(err => { console.log(err); }) } // 去留言板 toComment() { Alert.alert('功能还没做'); } // 组件即将释放 componentWillUnmount() { BackAndroid.removeEventListener('hardwareBackPress', HandleBackBtnPress.bind(this)); } render() { const { detail, article, loading, articleContent } = this.state; const _navigator = this.props.navigator; return ( <View style={{flex: 1, backgroundColor: '#fff'}}> <ScrollView style={{height: AppSizes.screen.height - 200}}> <Image source={buildThumb(article.thumb)} style={styles.image}> <View style={styles.innerImage}> <Text style={styles.title}>{ article.title }</Text> <View style={styles.meta}> { article.category.length ? <View style={[styles.metaItem, styles.metaItemLeft]}> <CommunityIcon name="book-open-variant" size={17} style={[styles.metaIcon, styles.metaText]}/> <Text style={styles.metaText}>{ String(article.category.map(c => c.name).join('、')) }</Text> </View> : null } <View style={[styles.metaItem, styles.metaItemLeft]}> <CommunityIcon name="eye" size={17} style={[styles.metaIcon, styles.metaText]}/> <Text style={styles.metaText}>{ article.meta.views }</Text> </View> <View style={styles.metaItem}> <CommunityIcon name="clock" size={17} style={[styles.metaIcon, styles.metaText, styles.metaDateIcon]}/> <Text style={styles.metaText}>{ toYMD(article.create_at) }</Text> </View> </View> </View> </Image> { loading ? <AutoActivityIndicator style={styles.indicator}/> : <View style={styles.content}> <Markdown styles={markdownStyles} rules={markdownRules} blacklist={['list']}>{articleContent}</Markdown> </View> } <NavBar leftOn={true} navigator={this.props.navigator} containerStyle={{backgroundColor: 'transparent'}} /> </ScrollView> <View style={styles.footer}> <TouchableOpacity style={styles.footerItem} onPress={this.toComment}> <Icon name="comment" size={17} style={styles.footerItemIcon}/> <Text style={styles.footerItemIconText}>{ `评论 (${article.meta.comments})` }</Text> </TouchableOpacity> <TouchableOpacity style={styles.footerItem} onPress={this.likeArticle.bind(this)}> <Icon name={this.state.liked ? 'favorite' : 'favorite-border'} size={17} style={[styles.footerItemIcon, { color: this.state.liked ? 'red' : AppColors.textTitle }]}/> <Text style={[styles.footerItemIconText, { color: this.state.liked ? 'red' : AppColors.textTitle }]}>{ `${this.state.liked ? '已' : ''}喜欢 (${article.meta.likes})` }</Text> </TouchableOpacity> </View> </View> ) }}export default Detail;
项目页通过WebView组件链接到作者的github仓储目录下。
class Projects extends Component { constructor(props) { super(props); this.state = { loading: true, canGoBack: false }; } componentDidMount() { BackAndroid.addEventListener('hardwareBackPress', this.handleBackAction.bind(this)); } componentWillUnmount() { BackAndroid.removeEventListener('hardwareBackPress', this.handleBackAction.bind(this)); } // webview状态改变 onNavigationStateChange(navState) { this.setState({ canGoBack: navState.canGoBack }); // console.log(this.refs.WEBVIEW_REF); } // 返回上一页Webview backWebViewPrevPage() { this.refs.WEBVIEW_REF.goBack(); } // 刷新当前webview reloadWebView() { this.refs.WEBVIEW_REF.reload(); } handleBackAction() { // webview有回退页则返回 if (this.state.canGoBack) { this.backWebViewPrevPage(); // 否则执行路由返回解析 } else { HandleBackBtnPress.bind(this)(); } } render() { return ( <View style={styles.container}> <WebView style={styles.webview} ref="WEBVIEW_REF" onNavigationStateChange={this.onNavigationStateChange.bind(this)} source={{uri:'https://github.com/surmon-china?tab=repositories'}} onLoad={() => this.setState({loading: true})} onLoadEnd={() => this.setState({loading: false})} startInLoadingState={true} domStorageEnabled={true} javaScriptEnabled={true} /> <NavBar leftOn={true} title={this.props.title} leftIsBack={Platform.OS === 'ios' && this.state.canGoBack} onLeftPress={ Platform.OS === 'ios' ? this.handleBackAction.bind(this) : this.props.openMenu } rightOn={true} rightText={ Platform.OS === 'ios' ? <Ionicons name='ios-refresh' size={32} color={AppColors.textPrimary} /> : <Ionicons name='md-refresh' size={24} color={AppColors.textPrimary} /> } onRightPress={this.reloadWebView.bind(this)} /> </View> ) }}export default Projects;
关于页面代码相对简单。
class About extends Component { constructor(props) { super(props); } componentDidMount() { BackAndroid.addEventListener('hardwareBackPress', HandleBackBtnPress.bind(this)); } componentWillUnmount() { BackAndroid.removeEventListener('hardwareBackPress', HandleBackBtnPress.bind(this)); } openSocial(url) { Linking.openURL(url).catch(error => console.warn('An error occurred: ', error)) } render() { const userInfo = this.props.userInfo; return ( <View style={styles.container}> <View style={[styles.container, styles.userContent]}> <Image style={styles.userGravatar} source={ userInfo ? {uri: userInfo.gravatar} : require('@app/images/gravatar.jpg') }/> <Text style={styles.userName}>{ userInfo ? userInfo.name : 'Surmon' }</Text> <Text style={styles.userSlogan}>{ userInfo ? userInfo.slogan : 'Talk is cheap. Show me the code.' }</Text> <View style={styles.userSocials}> <TouchableOpacity style={styles.userSocialItem} onPress={() => { this.openSocial('https://github.com/surmon-china')}}> <Ionicon name="logo-github" size={26} style={styles.userSocialIcon}/> </TouchableOpacity> <TouchableOpacity style={styles.userSocialItem} onPress={() => { this.openSocial('https://stackoverflow.com/users/6222535/surmon?tab=profile')}}> <FontAwesome name="stack-overflow" size={22} style={styles.userSocialIcon}/> </TouchableOpacity> <TouchableOpacity style={styles.userSocialItem} onPress={() => { this.openSocial('https://weibo.com/nocower')}}> <FontAwesome name="weibo" size={27} style={styles.userSocialIcon}/> </TouchableOpacity> <TouchableOpacity style={styles.userSocialItem} onPress={() => { this.openSocial('https://www.facebook.com/surmon.me')}}> <Ionicon name="logo-facebook" size={30} style={styles.userSocialIcon}/> </TouchableOpacity> <TouchableOpacity style={styles.userSocialItem} onPress={() => { this.openSocial('https://twitter.com/surmon_me')}}> <Ionicon name="logo-twitter" size={28} style={styles.userSocialIcon}/> </TouchableOpacity> <TouchableOpacity style={styles.userSocialItem} onPress={() => { this.openSocial('http://www.linkedin.com/in/surmon-ma-713bb6a2/')}}> <Ionicon name="logo-linkedin" size={30} style={styles.userSocialIcon}/> </TouchableOpacity> </View> </View> <Navbar leftOn={true} title={this.props.title} onLeftPress={ () => { Platform.OS === 'android' && this.props.openMenu(); }}/> </View> ) }}export default About;
阅读全文
0 0
- surmon.me.native代码分析笔记
- react-native-quan代码分析笔记
- HEVC ME之 整像素TZ搜索代码分析
- HEVC ME之 整像素TZ搜索代码分析
- Android app native代码性能分析
- Android app native代码性能分析
- Android app native代码性能分析
- Android Native 代码开发学习笔记
- Android Native 代码开发学习笔记
- Android Native 代码开发学习笔记
- CALL ME 插件代码
- Mahout笔记--代码分析
- HelloRN代码分析笔记
- freeswitch代码分析笔记
- ndk-stack 使用(分析native代码stack)
- JNI 开发笔记 - Native代码开发以及核心API介绍
- React-Native学习笔记之代码智能提醒(webstorm)
- React Native 学习笔记十六(细节分析)
- The forked VM terminated without saying properly goodbye VM crash or System.exit called?
- Sublime Text3激活及个性化配置
- Weex This isn't a hierarchical URI.
- Socket 错误
- 获取服客户端ip
- surmon.me.native代码分析笔记
- 十个主题,最全的优秀 TensorFlow 相关资源列表
- 会呼吸的圆
- android studio安装apk安装失败的问题。
- centos 防火墙添加删除规则
- C#的值类型和引用类型
- Centos7安装Jenkins
- MarkDown 公式编辑参照
- csdn 赚取积分