react-native 拉起第三方地图并传值

这个功能其实挺常用的,之前我都是用原生来写这个功能.
感觉还是用套JS 写 更通用点 这里放出源码 ,方便大家复制粘贴


image.png

这里给张图片 iOS模拟器的

本JS 会读取本机是否安装了 第三方地图 拉进行判断
如果没有安装任何地图 会显示常用的 高德 百度 腾讯 地图然后跳转到对应的 下载地址

源码:
MapLinking.js 文件

import { Linking, ActionSheetIOS, Platform } from "react-native";
import ActionSheet from "../../components/ActionSheet/index";

// 腾讯地图开发者key
let tmapKey = "";

// 下载地图app地址
const DownloadUrl = {
  android: {
    GaoDe: "http://mobile.amap.com",
    BaiDu: "http://map.baidu.com",
    // TengXun: `https://pr.map.qq.com/j/tmap/download?key=${tmapKey}`
  },
  ios: {
    GaoDe:
      "https://itunes.apple.com/cn/app/gao-tu-zhuan-ye-shou-ji-tu/id461703208?mt=8",
    BaiDu:
      "https://itunes.apple.com/cn/app/bai-du-tu-shou-ji-tu-lu-xian/id452186370?mt=8",
    // TengXun: `https://pr.map.qq.com/j/tmap/download?key=${tmapKey}`
  }
};

// 第三方地图应用Url
const openUrl = ({ startLocation, destLocation, mode, type, appName }) => {
  // 高德地图参数配置
  const GaoDeDev = type === "gcj02" ? 0 : 1; // 起终点是否偏移(0:lat 和 lon 是已经加密后的,不需要国测加密; 1:需要国测加密)
  const GaoDeT =
    mode === "drive" ? "0" : mode === "bus" ? "1" : mode === "walk" ? "2" : "3"; // 导航模式: t = 0(驾车)= 1(公交)= 2(步行)= 3(骑行)= 4(火车)= 5(长途客车)

  // 百度地图参数配置
  const BaiDuMode =
    mode === "drive"
      ? "driving"
      : mode === "bus"
      ? "transit"
      : mode === "ride"
      ? "riding"
      : "walking"; // 导航模式: 可选transit(公交)、driving(驾车)、walking(步行)和riding(骑行)
  const BaiDuCoordType = type === "gcj02" ? "gcj02" : "wgs84"; // 坐标类型,必选参数

  // 腾讯地图参数配置
  const TengXunType =
    mode === "drive"
      ? "drive"
      : mode === "bus"
      ? "bus"
      : mode === "ride"
      ? "bike"
      : "walk"; // 路线规划方式参数: 公交:bus 驾车:drive 步行:walk 骑行:bike

  // IOS系统地图配置
  const IOSDirflg = mode === "drive" ? "d" : mode === "bus" ? "r" : "w";

  if (Platform.OS === "android") {
    return [
      [
        "高德地图",
        `amapuri://route/plan/?sourceApplication=${appName}&slat=${
          startLocation.lat
        }&slon=${startLocation.lng}&sname=${startLocation.title}&dlat=${
          destLocation.lat
        }&dlon=${destLocation.lng}&dname=${
          destLocation.title
        }&dev=${GaoDeDev}&m=0&t=${GaoDeT}&rideType=elebike`
      ],
      [
        "百度地图",
        `baidumap://map/direction?origin=name:${startLocation.title}|latlng:${
          startLocation.lat
        },${startLocation.lng}&destination=name:${destLocation.title}|latlng:${
          destLocation.lat
        },${
          destLocation.lng
        }&mode=${BaiDuMode}&coord_type=${BaiDuCoordType}&src=${appName}`
      ],
      [
        "腾讯地图",
        `qqmap://map/routeplan?type=${TengXunType}&from=${
          startLocation.title
        }&fromcoord=${startLocation.lat},${startLocation.lng}&to=${
          destLocation.title
        }&tocoord=${destLocation.lat},${destLocation.lng}&referer=${tmapKey}`
      ]
    ];
  }
  return [
    [
      "高德地图",
      `iosamap://path?sourceApplication=${appName}&slat=${
        startLocation.lat
      }&slon=${startLocation.lng}&sname=${startLocation.title}&dlat=${
        destLocation.lat
      }&dlon=${destLocation.lng}&dname=${
        destLocation.title
      }&dev=${GaoDeDev}&m=0&t=${GaoDeT}&rideType=elebike`
    ],
    [
      "百度地图",
      `baidumap://map/direction?origin=name:${startLocation.title}|latlng:${
        startLocation.lat
      },${startLocation.lng}&destination=name:${destLocation.title}|latlng:${
        destLocation.lat
      },${
        destLocation.lng
      }&mode=${BaiDuMode}&coord_type=${BaiDuCoordType}&src=${appName}`
    ],
    [
      "腾讯地图",
      `qqmap://map/routeplan?type=${TengXunType}&from=${
        startLocation.title
      }&fromcoord=${startLocation.lat},${startLocation.lng}&to=${
        destLocation.title
      }&tocoord=${destLocation.lat},${destLocation.lng}&referer=${tmapKey}`
    ],
    [
      "Apple地图",
      `http://maps.apple.com/?ll=${destLocation.lat},${destLocation.lng}&q=${
        destLocation.title
      }&dirflg=${IOSDirflg}`
    ]
  ];
};

/**
 * 系统内没有任何第三方地图应用,提示推荐下载列表
 */
const downloadTip = () => {
  const obj = Platform.OS === "android" ? ActionSheet : ActionSheetIOS;
  obj.showActionSheetWithOptions(
    {
      title: "未安装任何第三方地图应用,请选择其中一个应用",
      options: ["取消", "高德地图", "百度地图","腾讯地图"],
      cancelButtonIndex: 0
    },
    buttonIndex => {
      const { GaoDe, BaiDu, TengXun } = DownloadUrl[Platform.OS];
      let url = 0;
      switch (buttonIndex) {
        case 1:
          url = GaoDe;
          break;
        case 2:
          url = BaiDu;
          break;
        case 3:
          url = TengXun;
          break;
        default:
          url = GaoDe;
      }
      Linking.canOpenURL(url).then(result => {
        if (result) {
          Linking.openURL(url);
        }
      });
    }
  );
};

/**
 * 显示已经存在的第三方地图应用
 */
const showExistApp = maps => {
  const obj = Platform.OS === "android" ? ActionSheet : ActionSheetIOS;
  const options = ["取消", ...maps.map(item => item[0])];
  obj.showActionSheetWithOptions(
    {
      title: "请选择其中一个地图应用",
      options,
      cancelButtonIndex: 0
    },
    buttonIndex => {
      if (buttonIndex !== 0) {
        Linking.openURL(maps[buttonIndex - 1][1]);
      }
    }
  );
};

/**
 * 规划路线
 */
const planRoute = ({
  startLocation = {},
  destLocation = {},
  mode = "ride",
  type = "gcj02",
  appName = "MapLinking"
}) => {
  const maps = openUrl({ startLocation, destLocation, mode, type, appName });
  const promises = maps.map(item => {
    return Linking.canOpenURL(item[1]).then((supported) => {
        if (supported) {
            // console.log("Can't handle url: " + url);
            return item;
        } else {
            return false;
        }
    })
    .catch((err) => console.error('An error occurred', err));;
  });

  Promise.all(promises)
    .then(results => {
      return maps.filter((item, index) => results[index]);
    })
    .then(choices => {
      if (!choices.length) {
        // 系统内没有任何地图,展示推荐下载列表
        downloadTip();
      } else {
        showExistApp(choices);
      }
    });
};

/**
 * 初始化值(如果想兼容腾讯地图,需要传入腾讯开发者Key)
 * 【申请地址】 https://lbs.qq.com/console/key.html
 */
const init = ({ tmapKey: refererKey = "" }) => {
  tmapKey = refererKey;
};

const MapLinking = () => {};

MapLinking.planRoute = planRoute;
MapLinking.init = init;

export default MapLinking;

ActionSheet.js 文件

import React from 'react';
import { ActionSheetIOS, Platform } from 'react-native';
import ActionSheetContainer from './ActionSheetContainer';
import RootSiblings from 'react-native-root-siblings';

let instance = null;

const ActionSheet = {
    Container: ActionSheetContainer,
    useActionSheetIOS: true,
    showActionSheetWithOptions: (config, callback) => {
        if (Platform.OS === 'ios' && ActionSheet.useActionSheetIOS) {
            ActionSheetIOS.showActionSheetWithOptions(config, callback);
            return;
        }
        if (instance) {
            return;
        }
        instance = new RootSiblings(
            <ActionSheet.Container
                config={config}
                callback={(index) => {
                    instance && instance.destroy(() => {
                        instance = null;
                        setTimeout(() => {
                            callback && callback(index);
                        }, 0);
                    });
                }}
            />
        );
    },
};

export default ActionSheet;

ActionSheetContainer.js 文件

import React from 'react';
import { Dimensions, Modal, ScrollView, StyleSheet, Text, TouchableHighlight, TouchableWithoutFeedback, View } from 'react-native';
import { getSafeAreaInset, isLandscape } from './SafeArea';

export default class extends React.PureComponent {
    static defaultProps = {
        backgroundColor: 'rgba(0, 0, 0, 0.3)',
        contentBackgroundColor: '#f9f9f9',
        separatorColor: '#d7d7d7',
        fontSize: 18,
        color: '#007aff',
        titleStyle: {
            fontSize: 15,
            fontWeight: 'bold',
            color: '#8f8f8f',
        },
        messageStyle: {
            fontSize: 15,
            color: '#8f8f8f',
        },
        destructiveButtonStyle: {
            color: '#d11f1f',
        },
        cancelButtonStyle: {
            fontWeight: 'bold',
        },
        touchableUnderlayColor: '#dddddd',
        supportedOrientations: ['portrait', 'landscape'],
    };

    constructor(props) {
        super(props);
        this.onWindowChange = this._onWindowChange.bind(this);
        this.state = {
            isLandscape: isLandscape(),
        };
    }

    componentDidMount() {
        Dimensions.addEventListener('change', this.onWindowChange);
    }

    componentWillUnmount() {
        Dimensions.removeEventListener('change', this.onWindowChange);
    }

    render() {
        const { config, backgroundColor, supportedOrientations } = this.props;
        const { cancelButtonIndex } = config;
        const closeFunc = this._click.bind(this, cancelButtonIndex);
        return (
            <View style={[styles.view, {backgroundColor}]}>
                <Modal
                    visible={true}
                    supportedOrientations={supportedOrientations}
                    onRequestClose={closeFunc}
                    transparent={true}
                    animationType={'slide'}
                >
                    <TouchableWithoutFeedback style={styles.touchview} onPress={closeFunc}>
                        <View style={styles.touchview} />
                    </TouchableWithoutFeedback>
                    {this._renderSections()}
                </Modal>
            </View>
        );
    }

    _renderSections = () => {
        const {width, height} = Dimensions.get('window');
        const inset = getSafeAreaInset();
        const { config } = this.props;
        const { title, message, options, cancelButtonIndex } = config;
        const contentStyle = {
            paddingHorizontal: 10,
            marginBottom: inset.bottom > 0 ? inset.bottom : 10,
            marginTop: this.state.isLandscape ? inset.top + 10 : inset.top + 44,
        };
        contentStyle.maxHeight = height - contentStyle.marginBottom - contentStyle.marginTop;
        if (this.state.isLandscape) {
            contentStyle.width = Math.max(width / 3, height - 10 * 2);
            contentStyle.alignSelf = 'center';
        }
        const section = [];
        let cancelView = null;
        (title || message) && section.push(this._renderTitle(title, message));
        options.forEach((item, index) => {
            const itemView = this._renderItem(item, index);
            if (index === cancelButtonIndex) {
                cancelView = itemView;
            } else {
                section.push(itemView);
            }
        });
        const sections = cancelView ? [section, cancelView] : [section];
        return (
            <View style={[styles.content, contentStyle]}>
                {sections.map((sectionItem, index) => {
                    const style = index > 0 ? {
                        marginTop: 9,
                    } : {};
                    return this._renderSection(sectionItem, index, style);
                })}
            </View>
        );
    };

    _renderTitle = (title, message) => {
        const { titleStyle, messageStyle } = this.props;
        const style = {marginVertical: 6};
        return (
            <View style={styles.title} key={'title'}>
                {title && (
                    <Text style={[style, titleStyle]}>
                        {title}
                    </Text>
                )}
                {message && (
                    <Text style={[style, messageStyle]}>
                        {message}
                    </Text>
                )}
            </View>
        );
    };

    _renderItem = (item, index) => {
        const { config, destructiveButtonStyle, cancelButtonStyle, touchableUnderlayColor, fontSize, color } = this.props;
        const { destructiveButtonIndex, cancelButtonIndex } = config;
        const isCancel = index === cancelButtonIndex;
        const isDestructive = index === destructiveButtonIndex;
        const textStyle = isCancel ? cancelButtonStyle :
            isDestructive ? destructiveButtonStyle : null;
        return (
            <TouchableHighlight
                key={index}
                style={styles.button}
                underlayColor={touchableUnderlayColor}
                onPress={this._click.bind(this, index)}
            >
                <View style={styles.buttonView}>
                    <Text style={[{fontSize, color}, textStyle]}>
                        {item}
                    </Text>
                </View>
            </TouchableHighlight>
        );
    };

    _renderSection(items, index, style) {
        const {contentBackgroundColor: backgroundColor} = this.props;
        const isArray = Array.isArray(items);
        const Component = isArray ? ScrollView : View;
        const props = isArray ? {
            bounces: false,
        } : {};
        return (
            <Component
                key={index}
                style={[styles.section, style, {backgroundColor}]}
                {...props}
            >
                {isArray ? items.map((item, innerIndex) => {
                    const views = [item];
                    if (innerIndex < items.length - 1) {
                        views.push(this._renderSeparatorLine('sepline' + innerIndex));
                    }
                    return views;
                }) : items}
            </Component>
        );
    }

    _renderSeparatorLine(key) {
        const {separatorColor: backgroundColor} = this.props;
        return (
            <View
                key={key}
                style={[styles.seperator, {backgroundColor}]}
            />
        );
    }

    _click(index) {
        this.props.callback && this.props.callback(index);
    }

    _onWindowChange() {
        this.setState({isLandscape: isLandscape()});
    }
}

const styles = StyleSheet.create({
    view: {
        position: 'absolute',
        top: 0,
        left: 0,
        right: 0,
        bottom: 0,
        opacity: 1,
    },
    touchview: {
        flex: 1,
        backgroundColor: 'transparent',
    },
    content: {
        flex: 0,
    },
    section: {
        borderRadius: 11,
        overflow: 'hidden',
    },
    seperator: {
        height: StyleSheet.hairlineWidth,
    },
    title: {
        justifyContent: 'center',
        alignItems: 'center',
        paddingVertical: 10,
    },
    button: {
        alignItems: 'center',
        justifyContent: 'center',
        paddingHorizontal: 16,
        height: 57,
    },
    buttonView: {
        justifyContent: 'center',
        alignItems: 'center',
    },
});

SafeArea.js

import { Dimensions, Platform, StatusBar } from 'react-native';

export const isIos = Platform.OS === 'ios';
export const isAndroid = Platform.OS === 'android';
export const isWeb = Platform.OS === 'web';

const {width: D_WIDTH, height: D_HEIGHT} = Dimensions.get('window');

function _compare(targetWidth, targetHeight) {
    return D_WIDTH === targetWidth && D_HEIGHT === targetHeight ||
        D_WIDTH === targetHeight && D_HEIGHT === targetWidth;
}

export const isIPhoneX = function () {
    // X + XS
    const X_WIDTH = 375;
    const X_HEIGHT = 812;
    // XR + XS Max
    const XSMAX_WIDTH = 414;
    const XSMAX_HEIGHT = 896;
    return isIos && (
        _compare(X_WIDTH, X_HEIGHT) ||
        _compare(XSMAX_WIDTH, XSMAX_HEIGHT)
    );
}();

export const isNewIPadPro = function () {
    const IPADPRO11_WIDTH = 834;
    const IPADPRO11_HEIGHT = 1194;
    const IPADPRO129_HEIGHT = 1024;
    const IPADPRO129_WIDTH = 1366;
    return isIos && (
        _compare(IPADPRO11_WIDTH, IPADPRO11_HEIGHT) ||
        _compare(IPADPRO129_WIDTH, IPADPRO129_HEIGHT)
    );
}();

export const isIPad = function () {
    if (!isIos || isIPhoneX) return false;
    const PAD_WIDTH = 768;
    const minEdge = Math.min(D_WIDTH, D_HEIGHT);
    return minEdge >= PAD_WIDTH;
}();

export function getSafeAreaInset(landscape, translucent = false) {
    if (landscape === undefined) {
        landscape = isLandscape();
    }
    const inset = (top, right, bottom, left) => ({top, right, bottom, left});
    if (isIos) {
        if (isIPhoneX) {
            return landscape ? inset(0, 44, 21, 44) : inset(44, 0, 34, 0);
        } else if (isNewIPadPro) {
            return inset(24, 0, 20, 0);
        } else if (isIPad) {
            return inset(20, 0, 0, 0);
        } else {
            return landscape ? inset(0, 0, 0, 0) : inset(20, 0, 0, 0);
        }
    } else if (isAndroid) {
        const top = translucent ? StatusBar.currentHeight : 0;
        return inset(top, 0, 0, 0);
    } else {
        return inset(0, 0, 0, 0);
    }
}

export function isLandscape() {
    const {width, height} = Dimensions.get('window');
    return width > height;
}

这里是使用:

handleChange = () => {
    // starting location info
    const startLocation = {
      lng: 106.534892,
      lat: 29.551891,
      title: '公园地址'
    };

    // end location info
    const destLocation = {
      lng: 106.27613,
      lat: 29.972084,
      title: '邮局'
    };

    MapLinking.planRoute({ startLocation, destLocation });
  };

  render() {
    return (
      <TouchableOpacity onPress={this.handleChange}>
        <Text>start navigation</Text>
      </TouchableOpacity>
    );
  }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,290评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,399评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,021评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,034评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,412评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,651评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,902评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,605评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,339评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,586评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,076评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,400评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,060评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,851评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,685评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,595评论 2 270

推荐阅读更多精彩内容