Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 基於Redux的ReactNative項目開發總結(一)

基於Redux的ReactNative項目開發總結(一)

編輯:關於Android編程

寫在前面

上周把基於Redux的單頁應用開發完 緊接著就開始了ReactNative的開發。真的快得不可思議,只花了一周時間,我們兩個人就分工把APP也開發完了,並且同時兼容IOS操作系統和Android操作系統。內部測試了一輪,流暢性和用戶體驗方面也都相當給力! 接下去幾篇依次介紹項目開發中領悟的技巧和遇到的坑。

項目架構

React開發的單頁應用不同,ReactNative開發不需要依賴webpackfacebook已經提供的一套基於NodeJS的轉換和運行工具,這裡不多做介紹。項目的架構如下:

|---- project 
    |---- android                          // android外殼 
    |---- ios                              // ios外殼
    |---- node_modules                     // 項目依賴的node模塊
    |---- app                              // 本項目的js代碼
        |---- actions                      // Redux的actions
        |---- assets                       // 項目使用的icon
        |---- components                   // 項目自定義的組件
        |---- containers                   // 項目的容器頁面
        |---- mixins                       // 全局使用的工具方法
        |---- modules                      // 全局使用的自定義模塊  
        |---- reducers                     // Redux的reducers
        |---- configureStore.js            // Redux的store配置
        |---- index.js                     // APP入口頁面
    |---- index.ios.js                     // android入口
    |---- index.android.js                 // ios入口
    |---- packge.json                      // 項目包信息

Redux不清楚的童鞋可以出門看我之前的《基於Redux的單頁應用開發總結》 。其實除了路由,其他和單頁應用的結構差別不大。

依賴的Node模塊

ReactNative開發離不開NodeJS的支持,相比單頁應用,這裡依賴的模塊比較少,除了Redux相關的模塊,其他都是項目中用到的三方組件。

"dependencies": {
    "@remobile/react-native-toast": "^1.0.4",          // 用於錯誤提示的小彈窗
    "base-64": "^0.1.0",                              
    "react": "^0.14.8",
    "react-native": "^0.25.1",
    "react-native-animatable": "^0.6.0",               // 動畫庫
    "react-native-communications": "^2.0.0",           // 調用手機通訊功能
    "react-native-image-picker": "^0.18.17",           // 圖片選擇
    "react-native-modalbox": "^1.3.3",                 // 模態框
    "react-native-tab-navigator": "^0.2.18",           // 導航欄
    "react-native-viewpager": "^0.2.1",                // 圖片輪播切換
    "react-redux": "^4.4.5",
    "redux": "^3.5.2",
    "redux-thunk": "^2.0.1"
  }

錯誤提示

App裡錯誤提示和網站稍有不同,網站頁面寬度大,錯誤提示信息一般出現在輸入框的後面或者懸浮在右上角。但是在App裡,需要考慮用戶不同的屏幕尺寸,錯誤信息應該放在一個相對獨立且不影響其他元素顯示的位置,目前主流的展現形式是這樣的:
這裡寫圖片描述
即懸浮放置在頁面的中底部,並且定時自動關閉。這個玩意自己寫一個也不難,不過為了省事和好的兼容性,就直接使用三方的@remobile喎?/kf/ware/vc/" target="_blank" class="keylink">vcmVhY3QtbmF0aXZlLXRvYXN0PC9jb2RlPsHLoaPKudPDt723qMjnz8KjujwvcD4NCjxwcmUgY2xhc3M9"brush:java;"> import Toast from '@remobile/react-native-toast'; // ... Toast.showShortBottom('用戶名或密碼不正確');

對外的API有以下幾個,顧名思義:

Toast.showShortTop = function (message) {
  showToast(message, "short", "top");
};

Toast.showShortCenter = function (message) {
  showToast(message, "short", "center");
};

Toast.showShortBottom = function (message) {
  showToast(message, "short", "bottom");
};

Toast.showLongTop = function (message) {
  showToast(message, "long", "top");
};

Toast.showLongCenter = function (message) {
  showToast(message, "long", "center");
};

Toast.showLongBottom = function (message) {
  showToast(message, "long", "bottom");
};

Toast.show = function (message) {
  showToast(message, "short", "bottom");
};

Toast.hide = function () {
  RCTToast.hide();
};

安裝和使用方法請查看 官方文檔

動畫庫

之前花時間研究過CSS3的動畫庫,其實目前主流的動畫類型和動畫創意就那麼些,感興趣的可以 clone一下 myAnimate 這個項目。一句話,CSS3裡使用的動畫方案,ReactNative裡也應有盡有。我這邊使用的是 react-native-animatable 組件。使用方式如下:

import * as Animatable from 'react-native-animatable';

class ExampleView extends Component {
  render() {
    return (
       this.setState({fontSize: (this.state.fontSize || 10) + 5 })}>
          Size me up, Scotty
      
    );
  }
}

下面是官方的Demo

這裡寫圖片描述

調用手機通訊功能

HybridApp裡實現這個功能還是挺麻煩的,需要客戶端封裝好接口給H5調用,但是在ReactNative裡,一個組件就能搞定—— react-native-communications,安裝請查看官方文檔

這個組件安裝很簡單,支持的功能有:撥號、發短信、發Email、打開網頁 等 ,下面是官方一個綜合的例子:

import React, { Component }  from 'react';

import {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  TouchableOpacity
} from 'react-native';

import Communications from 'react-native-communications';

class RNCommunications extends Component({

  render() {
    return (
      
         Communications.phonecall('0123456789', true)}>
          
            Make phonecall
          
        
         Communications.email(['emailAddress1', 'emailAddress2'],null,null,'My Subject','My body text')}>
          
            Send an email
          
        
         Communications.text('0123456789')}>
          
            Send a text/iMessage
          
        
         Communications.web('https://github.com/facebook/react-native')}>
          
            Open react-native repo on Github
          
        
      
    );
  }
});

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    backgroundColor: 'rgb(253,253,253)',
  },
  holder: {
    flex: 0.25,
    justifyContent: 'center',
  },
  text: {
    fontSize: 32,
  },
});

AppRegistry.registerComponent('RNCommunications', () => RNCommunications);

訪問手機相冊

調取手機相冊和上傳圖片是個老生常談的問題,ReactNative裡可以通過react-native-image-picker 來處理,安裝請查看官方文檔

拎一段代碼片段:

import ImagePicker from 'react-native-image-picker' 

const options = {
    title: '選擇上傳圖片', // specify null or empty string to remove the title
    cancelButtonTitle: '取消',
    takePhotoButtonTitle: '拍照...', // specify null or empty string to remove this button
    chooseFromLibraryButtonTitle: '從庫中選擇...', // specify null or empty string to remove this button
    //customButtons: {
    //    'Choose Photo from Facebook': 'fb', // [Button Text] : [String returned upon selection]
    //},
    cameraType: 'back', // 'front' or 'back'
    mediaType: 'photo',
    //videoQuality: 'high', // 'low', 'medium', or 'high'
    maxWidth: 200, // photos only
    maxHeight: 200, // photos only
    allowsEditing: true,
    noData: false,
}

//... 

onUpload() {
        ImagePicker.showImagePicker(options, (response) => {
            if (response.didCancel) {
                //console.log('User cancelled image picker');
            }
            else if (response.error) {
                //console.log('ImagePicker Error: ', response.error);
            } else {
                let source = {uri: response.uri.replace('file://', ''), isLocal: true, isStatic: true};
                this.setState({ form: {...this.state.form, avatar: source} })
            }
        })
    }

模態框

模態框在App裡使用的也比較多,比如確認模態、加載模態、輸入模態等,出於良好的用戶體驗和兼容性考慮,我這裡底層采用react-native-modalbox,然後根據不同功能進行二次加工。

ConfirmModal

很常見,不多做介紹,copy下面代碼 直接可以使用

import React, { Component } from 'react';
import {
    Dimensions,
    StyleSheet,
    Text,
    TouchableOpacity,
    View,
} from 'react-native';
import ModalBox from 'react-native-modalbox';
const styles = StyleSheet.create({
    modal: {
        borderRadius: 10,
    },
    modalContent: {
        flex: 1,
        paddingLeft: 10,
        paddingRight: 10,
    },
    h2: {
        marginTop: 15,
        fontSize: 20,
        color: '#555',
        textAlign: 'center',
    },
    modalOption: {
        flexDirection: 'row',
        borderTopWidth: 1,
        borderTopColor: '#ddd',
    },
    modalCancel: {
        flex: 1,
        padding: 15,
    },
    modalCancelText: {
        fontSize: 16,
        textAlign: 'center',
    },
    modalConfirm: {
        flex: 1,
        padding: 15,
        borderLeftWidth: 1,
        borderLeftColor: '#ddd',
    },
    modalConfirmText: {
        fontSize: 16,
        textAlign: 'center',
    },
    message: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
    },
    messageText: {
        color: '#555',
        fontSize: 16,
    },
});
export default class ConfirmModal extends Component {
    constructor(props) {
        super(props);
    }
    open() {
        this.refs.modal.open()
    }
    close() {
        this.refs.modal.close()
    }
    render() {
        let { width } = Dimensions.get('window');
        return (
            
                
                    { this.props.title || '提示' }
                    { this.props.message }
                
                
                     this.refs.modal.close() }>
                        取消
                    
                     this.props.onConfirm() }>
                        確定
                    
                
            
        )
    }
}

LoadingModal

這個也很常見,copy下面代碼 直接可以使用

import React, { Component } from 'react';
import {
    StyleSheet,
} from 'react-native';
import ModalBox from 'react-native-modalbox';
const styles = StyleSheet.create({
    modal: {
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: 'transparent'
    },
});

export default class LoadingModal extends Component {
    constructor(props) {
        super(props);
    }
    open() {
        this.refs.modal.open()
    }
    close() {
        this.refs.modal.close()
    }
    render() {
        return (
            
            
        );
    }
}

PickerModal

這個特別講解一下,PickerModal用於頁面上的Picker的處理,顯示效果如下:

這裡寫圖片描述

import React, { Component } from 'react';
import {
    Dimensions,
    StyleSheet,
    Text,
    TouchableOpacity,
    Picker,
    View,
} from 'react-native';
import ModalBox from 'react-native-modalbox'
import dismissKeyboard from '../mixins/dismiss-keyboard'
const styles = StyleSheet.create({
    popup: {
    },
    popupContent: {
        flex: 1,
        paddingLeft: 10,
        paddingRight: 10,
    },
    h2: {
        marginTop: 15,
        fontSize: 20,
        color: '#555',
        textAlign: 'center',
    },
    popupOption: {
        flexDirection: 'row',
        borderTopWidth: 1,
        borderTopColor: '#ddd',
    },
    popupCancel: {
        flex: 1,
        padding: 15,
    },
    popupCancelText: {
        fontSize: 16,
        textAlign: 'center',
    },
    popupConfirm: {
        flex: 1,
        padding: 15,
        borderLeftWidth: 1,
        borderLeftColor: '#ddd',
    },
    popupConfirmText: {
        fontSize: 16,
        textAlign: 'center',
    },
    message: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
    },
    messageText: {
        color: '#555',
        fontSize: 16,
    },
});

export default class PickerModal extends Component {
    constructor(props) {
        super(props);
    }
    open() {
        dismissKeyboard()
        this.refs.modal.open()
    }
    close() {
        this.refs.modal.close()
    }
    _renderPickerItems(data) {
        data.map((item)=>{
            return [
                
            ]
        })
    }
    render() {
        let { width } = Dimensions.get('window');
        return (
            
                
                    
                        {this.props.dataSource.map((item,i)=> {
                            if (item.length) return 
                        })}
                    
                
            
        )
    }
}

補充說明一下dismissKeyboard()這個方法,該方法用於關閉頁面的keyboard(鍵盤),ReactNative 默認沒有這種方法,需要自己編寫:

import { TextInput } from 'react-native';
const { State: TextInputState } = TextInput;
export default function dismissKeyboard() {
    TextInputState.blurTextInput(TextInputState.currentlyFocusedField());
}

導航條

這個組件其實ReactNative提供了原生版本的,但是樣式和功能上不好控制,建議自己手寫一個,代碼如下:

import React, { Component } from "react";
import {
    Image,
    Platform,
    StyleSheet,
    Text,
    TouchableOpacity,
    View,
} from 'react-native';

const styles = StyleSheet.create({
    leftButton: {
        marginLeft: 5,
    },
    rightButton: {
        marginRight: 5,
    },
    button: {
        width: 44,
        height: 44,
        justifyContent: 'center',
        alignItems: 'center',
    },
    nav: {
        backgroundColor: '#f9f9f9',
        flexDirection: 'row',
        alignItems: 'center',
    },
    title: {
        flex: 1,
        height: 44,
        justifyContent: 'center',
    },
    btnText: {
        fontSize: 16,
        color: '#777',
    },
    marginForIOS: {
        marginTop: 20,
    },
    titleText: {
        fontSize: 20,
        textAlign: 'center',
        color: '#555'
    }
});

export class RightButton extends Component {
    render() {
        return (
            
                { this.props.text ? {this.props.text} : null }
                { this.props.icon ?  : null }
            
        );
    }
}

export class NavigatorBar extends Component {
    _leftButton() {
        if (this.props.navigator.getCurrentRoutes().length > 1) return (
             this.props.navigator.pop() }>
                
            
        )
    }

    _rightButton() {
        if (this.props.rightButton) return (
            
        )
    }

    render() {
        return (
            
                
                    {this._leftButton()}
                
                
                    { this.props.name }
                
                
                    {this._rightButton()}
                
            
        );
    }
}

然後在container裡就可以使用了:

import { NavigatorBar } from '../components/navigator' 

// 沒有右側按鈕 
 

// 右側按鈕為圖標  
{this.props.navigator.push({component: Setting})}, icon: require('../../assets/icon-set.png')}} />

// 右側按鈕為文字 
 this.props.navigator.push({component: OrderHitory}) }} /> 

圖片輪播

建議使用三方的react-native-viewpager組件,安裝請查看 官方文檔

下面是一個demo:

var ViewPager = require('react-native-viewpager');
 {
    // Use the horizontal velocity of the swipe gesture
    // to affect the length of the transition so the faster you swipe
    // the faster the pages will transition
    var velocity = Math.abs(gestureState.vx);
    var baseDuration = 300;
    var duration = (velocity > 1) ? 1/velocity * baseDuration : baseDuration;

    return Animated.timing(animatedValue,
    {
      toValue: toValue,
      duration: duration,
      easing: Easing.out(Easing.exp)
    });
  }}
/>
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved