2017-09-09 13:56:17

React Native: 安卓控件的iOS兼容(其二)

兼容TouchableOpacity

TouchableOpacity默认情况下会在滑动时触发透明度变化,体验并不好。TouchableNativeFeedback在安卓上的默认delayPressIn为140毫秒,因此照例给TouchableOpacity设置上

import {
  TouchableOpacity
} from 'react-native'

class ExtendedTouchableOpacity extends Component<any, any> {
  constructor(props) {
    super(props)
  }

  render() {
    return <TouchableOpacity {...this.props} delayPressIn={140} />
  }
}

兼容TouchableNativeFeedback

TouchableNativeFeedback是安卓上唯一一个RN未做兼容的组件,会在安卓5以下直接崩溃。这里使用上面修改过的ExtendedTouchableOpacity 作为备选组件

let TouchableComponent

if (!isIOS) {
  TouchableComponent = Platform.Version <= 20 ? ExtendedTouchableOpacity : TouchableNativeFeedback
} else {
  TouchableComponent = ExtendedTouchableOpacity
}

if (TouchableComponent !== TouchableNativeFeedback) {
  TouchableComponent.SelectableBackground = () => ({})
  TouchableComponent.SelectableBackgroundBorderless = () => ({})
  TouchableComponent.Ripple = () => ({})
}

兼容ActivityIndicator

如上文所说,安卓上经常使用的size属性会导致iOS崩溃。

class IOSActivityIndicator extends Component<any, any> {
  constructor(props) {
    super(props)
  }

  render() {
    const { size, ...props } = this.props
    return (
      <ActivityIndicator  {...props} />
    )
  }
}

兼容Picker

iOS上的默认Picker和安卓上的差别太大了,占地过大,一般来说没那么多地方给它使用,因此需要使用ActionSheet来模拟拾取窗口。不要忘了Picker还有静态组件Item存在,简单给个null即可

class PickerIOS extends Component<any, any> {
  constructor(props) {
    super(props)
  }

  render() {
    const { style, prompt = '', selectedValue, onValueChange, children = [] } = this.props
    // console.log(Object.keys(children[0]), children[0].props, 'hehe ===>')
    const arr = children.length ? children.map(item => (item as any).props).slice() : []
    const data = arr.map(item => {
      return {
        value: item.value,
        label: item.label,
        key: item.label,
        color: item.color
      }
    })
    const valueLabel = arr.reduce((prev, curr) => {
      prev[curr.value] = curr.label
      return prev
    }, {})
    const { borderWidth, ...targetStyle } = style

    return (
      <ModalPicker
        title={prompt}
        data={data}
        initValue={valueLabel[selectedValue]}
        style={targetStyle}
        selectedStyle={targetStyle}
        onChange={(option) => {
          onValueChange(option.value)
         }} />
    )
  }
}

(PickerIOS as any).Item = class extends Component<any, any> {
  render() { return null }
}

ModalPicker的实现如下:

export default class PickerIOSItem extends React.Component<any, any> {
  constructor(props) {
    super(props)
  }

  actionSheet = () => {
    const { title, data, onChange } = this.props
    ActionSheetIOS.showActionSheetWithOptions({
      options: data.map(item => item.label).concat('取消'),
      cancelButtonIndex: data.length,
      title
    }, (index) => {
      if (index !== data.length) {
        onChange(data[index])
      }
    })
  }

  render() {
    // const color = modeInfo.backgroundColor
    const { initValue } = this.props
    const style: any = StyleSheet.flatten(this.props.style)
    const { color } = style
    return (
      <TouchableOpacity onPress={this.actionSheet} style={[{flex: 1}, style]}>
        <View style={{margin:2}}>
          <Text numberOfLines={1} ellipsizeMode='tail' style={{
            color,
            fontSize: 15,
            textAlign: 'center',
            textAlignVertical: 'center'
          }}>{initValue}</Text>
          <global.Icons style={{
            position: 'absolute',
            right: 8,
            top: 0,
            bottom: 0
          }} name='md-arrow-dropdown' size={20} color={color} />
        </View>
      </TouchableOpacity>
    )
  }
}

这个兼容组件有一个缺点,即拾取弹窗中的文字并不支持自定义色彩。

兼容ProgressBarAndroid

ProgressBarAndroid的背景颜色比主颜色浅一些,我们可以使用values.js这个库达到目的

import Values from 'values.js'

class ProgressBarIOS extends React.Component<any, any> {
  constructor(props) {
    super(props)
  }

  render() {
    const { color, progress, style } = this.props
    const tintColor = new Values(color)
    return (
      <ProgressViewIOS style={style} progress={progress}
        progressTintColor={color} trackTintColor={tintColor.tint(75).hexString()} progressViewStyle={'bar'}/>
    )
  }
}

兼容DrawerLayoutAndroid

感谢react-native-drawer-layout吧,只需要一行

import DrawerLayout from 'react-native-drawer-layout'

兼容Image组件

Image组件在安卓上能够缓存图片,但在iOS上行为相当诡异,兼容可以参见博主接下来的文章。

本文链接:https://smallpath.me/post/react-native-android-control-to-ios-2

-- EOF --