vue与安卓原生定位的交互

  • Post author:
  • Post category:vue




网页定位的不足

我之前写了篇vue调用百度地图 js api实现定位的文章,


vue 实现定位到当前位置


但是实际使用的时候,发现百度定位js经常定位不精确,这个不足,官网也有提,

在这里插入图片描述

为了精确,还是使用原生app调用高德地图api来实现定位,这样就要用到js与原生的交互,js调用某个方法,然后触发原生的定位,原生定位完成,调用js方法,回传给js,我这边做的兼容性比较高,js传入不同的坐标系,原生会在回传给js的方法中,将坐标系转换



实现


vue定义方法


vue定义两个方法,方法

getLocationFromNative(String type)

,调起定位功能,type是坐标类型,默认gcj02,bd09:百度坐标系,gcj02:火星坐标系,gps:GPS坐标系(WGS84坐标系),原生提供根据type,转换坐标系的方法

定位成功或失败,回调

notifyLocationFromApp

,传入json数据,json数据自己组装,我这边是这样的

{"data":{"address":"xxx","city":"xx市",
"country":"中国","description":"在xx附近","district":"xx区","latitude":31.4xx8,
"locationDetail":"#csid:4d4xxx7039e594xx3",
"longitude":120.30xx1,"province":"xx省","street":"xx大道","streetNum":"xx号"}
,"isOk":true}
<template>
  <div class="phoneContant">
    <a :href="tel">点击拨打电话</a>
    <van-button type="warning" @click="goToNext">进入下一页</van-button>
    <van-button type="warning" @click="getLocation">从app获取定位</van-button>
  </div>
</template>

<script>
//判断是否是安卓还是ios
var config = {
  isAndroid: /Android/i.test(navigator.userAgent), //判断是否为移动端
  isIos: !!navigator.userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), //判断是否为IOS
};
export default {
  name: "Home",
  data() {
    return {
      msg: "我是首页",
      store_info: {
        phone: 15xxxx753,
      },
      nativeLocationSuccess:false,
      loadDefault:false
    };
  },
  methods: {
    goToNext() {
      this.$router.push("/second");
    },
    getLocation() {
       setTimeout(() => {
          if(this.nativeLocationSuccess==false){
              console.log("定位失败,加载默认的接口");
              this.loadDefault=true
          }
        
      }, 10000);//10秒未返回数据,加载默认的,防止前端没有getLocationFromNative和notifyLocationFromApp回调
      /**
       * 经纬度坐标系,默认gcj02
        bd09:百度坐标系
        gcj02:火星坐标系
        gps:GPS坐标系(WGS84坐标系)
       */
      if (config.isAndroid && window.hesAndroidNative) {
        window.hesAndroidNative.getLocationFromNative("");
      } else if (config.isIos && window.webkit) {
        window.webkit.messageHandlers.getLocationFromNative.postMessage("");
      } else {
      }
     
    },
    notifyLocationFromApp(jsonStr) {
      this.nativeLocationSuccess=true;
      if(this.loadDefault==true){
          return
      }
      console.log("json string" + jsonStr);
      let valueObj=JSON.parse(jsonStr)
         console.log("valueObj " + valueObj);
      let value = jsonStr+"||||测试解析结果:"+valueObj.data.address

         if(valueObj.isOk==false){
      console.log("失败");
       }else{
           console.log("成功",valueObj.data.longitude, valueObj.data.latitude);
       }
      this.$dialog
        .alert({
          confirmButtonText: "是",
          closeOnClickOverlay: false,
          title: "提示",
          showCancelButton: false,
          messageAlign: "center",
          message: value,
        })
        .then(() => {
          return false;
        })
        .catch(() => {});
    },
  },
  computed: {
    tel() {
      return "tel:" + Number(this.store_info.phone);
    },
  },
  created() {
       window.notifyLocationFromApp = this.notifyLocationFromApp;
      this.getLocation()
  },
};
</script>

<style>
</style>

大概逻辑就是进入页面调用原生的定位,然后等待10s,10秒未返回数据,加载默认的,防止前端没有getLocationFromNative和notifyLocationFromApp回调,因为js如果检测到方法未定义,直接报错,也没回调,这样写,能保证程序正常运行


原生实现

public class WebviewBase extends BaseCompatActivity{

    @BindView(R.id.iv_back)
    protected ImageView ivBack;
    @BindView(R.id.iv_close)
    protected ImageView ivClose;
    @BindView(R.id.tv_title)
    protected TextView tvTitle;
    @BindView(R.id.rl_top)
    protected  RelativeLayout rlTop;
    @BindView(R.id.pbar_more)
    protected ProgressBar progressBar;
    @BindView(R.id.webView)
    protected WebView webView;
    protected LocationReslut locationReslut=new LocationReslut();
    protected AMapLocationClient mLocationClient = null;
    protected String locationType="";
    protected AMapLocationListener mLocationListener = new AMapLocationListener()
    {
        @Override
        public void onLocationChanged(AMapLocation aMapLocation)
        {
            if (aMapLocation != null)
            {
                if (aMapLocation.getErrorCode() == 0)
                {
                    LocationReslut.Data locationData=locationReslut.new Data();
                    locationData.setAddress(aMapLocation.getAddress());
                    locationData.setCity(aMapLocation.getCity());
                    locationData.setCountry(aMapLocation.getCountry());
                    locationData.setStreetNum(aMapLocation.getStreetNum());
                    locationData.setStreet(aMapLocation.getStreet());
                    locationData.setDistrict(aMapLocation.getDistrict());
                    locationData.setProvince(aMapLocation.getProvince());
                    locationData.setLatitude(aMapLocation.getLatitude());
                    locationData.setLongitude(aMapLocation.getLongitude());
                    locationReslut.setData(locationData);
                    locationReslut.setOk(true);

                    locationResultToJs();

                    mLocationClient.stopLocation();
                }
                else
                {
                    locationResultToJs();
                    Log.e("定位错误", aMapLocation.getErrorCode() + ":" + aMapLocation.getErrorInfo());
                }
            }
            else
            {

                locationResultToJs();
                Log.e("定位失败", "");
            }
        }
    };

    private void locationResultToJs() {
        Gson gson = new Gson();
        Log.i("location",gson.toJson(locationReslut));
        LocationReslut.Data data=locationReslut.getData();
        if(locationReslut.isOk){
            double[] gps=null;
            switch (locationType){
                case "bd09":
                    gps= GPSUtil.gcj02_To_Bd09(data.getLatitude(),data.getLongitude());
                    data.setLatitude(gps[0]);
                    data.setLongitude(gps[1]);
                    break;
                case "gps":
                    gps= GPSUtil.gcj02_To_Gps84(data.getLatitude(),data.getLongitude());
                    data.setLatitude(gps[0]);
                    data.setLongitude(gps[1]);
                    break;
                default:
                    break;
            }
        }

        //原生回调js通知
        webView.evaluateJavascript("javascript:notifyLocationFromApp('" + gson.toJson(locationReslut) + "')", new ValueCallback<String>() {
            @Override
            public void onReceiveValue(String value) {
                //此处为 js 返回的结果

            }
        });
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_webview_privacy);
        ButterKnife.bind(this);
        initWebView();
			init();
        locationReslut.setData(null);
        locationReslut.setOk(false);
        ....省略各种初始化方法
    }

    protected void init()
    {
        mLocationClient = new AMapLocationClient(WebviewBase.this);

        mLocationClient.setLocationListener(mLocationListener);
        if (ActivityCompat.checkSelfPermission(WebviewBase.this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)
        {
            ActivityCompat.requestPermissions(WebviewBase.this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 100);
        }
        else
        {
            //mLocationClient.startLocation();
        }
    }
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
    {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }


    private void initWebView() {

        WebSettings webSettings = webView.getSettings();
        webSettings.setJavaScriptEnabled(true);
        // 设置字符编码
        webSettings.setDefaultTextEncodingName("utf-8");
        webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
        webSettings.setAllowFileAccess(true);
        webSettings.setLoadsImagesAutomatically(true);
        // webSettings.setAppCacheEnabled(true);
        webSettings.setBlockNetworkImage(false);
        // webSettings.setGeolocationEnabled(true);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
        }
        webSettings.setDefaultTextEncodingName("UTF-8");
        webSettings.setSupportZoom(true);
        // webSettings.setBuiltInZoomControls(true);
        // webSettings.setPluginsEnabled(true);
        //webView.addJavascriptInterface(this, "AppInterface");

        webSettings.setDomStorageEnabled(true);
        webSettings.setDatabaseEnabled(true);
        final String dbPath = getApplicationContext().getDir("db", Context.MODE_PRIVATE).getPath();
        webSettings.setDatabasePath(dbPath);

        final String cachePath = getApplicationContext().getDir("cache", Context.MODE_PRIVATE).getPath();
        webSettings.setAppCachePath(cachePath);
        webSettings.setAppCacheMaxSize(10 * 1024 * 1024);
        //设置加载进来的页面自适应手机屏幕
        webSettings.setUseWideViewPort(true);
        webSettings.setLoadWithOverviewMode(true);


        webView.setWebChromeClient(new MyWebChromeClient());
        webView.setWebViewClient(new MyWebViewClient());

        if (userAgent != null && !StringUtil.isEmpty(userAgent)) {
            String ua = webSettings.getUserAgentString();// 获取默认的UA
            webSettings.setUserAgentString(ua + "; " + userAgent);// UA追加自定义标识符
        }

        // 通过addJavascriptInterface()将Java对象映射到JS对象
        //参数1:Javascript对象名
        //参数2:Java对象名
   
        webView.addJavascriptInterface(new HesAndroidtoJs(), "hesAndroidNative");

    }
    // 继承自Object类
    public class HesAndroidtoJs 
    {

        // 定义JS需要调用的方法
        // 被JS调用的方法必须加入@JavascriptInterface注解


        @JavascriptInterface
        public void getDomainDataFromNative() {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    //原生回调js通知
                    webView.evaluateJavascript("javascript:notifyDomainDataFromApp('" + domainData + "')", new ValueCallback<String>() {
                        @Override
                        public void onReceiveValue(String value) {
                            //此处为 js 返回的结果

                        }
                    });
                }
            });

        }
        /**
         * 经纬度坐标系,默认gcj02
         * bd09:百度坐标系
         * gcj02:火星坐标系
         * gps:GPS坐标系(WGS84坐标系)
         * @param type
         */
        @JavascriptInterface
        public void getLocationFromNative(String type) {
            locationType=type;
            if(mLocationClient!=null){
                mLocationClient.startLocation();
            }

        }
    }

    private class LocationReslut{
        private boolean isOk;
        private Data data;

        public boolean isOk() {
            return isOk;
        }

        public void setOk(boolean ok) {
            isOk = ok;
        }

        public Data getData() {
            return data;
        }

        public void setData(Data data) {
            this.data = data;
        }
        public class Data{
            String country;
            String province;
            String city;
            String street;
            String streetNum;
            String address;
            double latitude;
            double longitude;
            String district;

            public String getCountry() {
                return country;
            }

            public void setCountry(String country) {
                this.country = country;
            }

            public String getProvince() {
                return province;
            }

            public void setProvince(String province) {
                this.province = province;
            }

            public String getCity() {
                return city;
            }

            public void setCity(String city) {
                this.city = city;
            }
            public String getStreet() {
                return street;
            }

            public void setStreet(String street) {
                this.street = street;
            }

            public String getStreetNum() {
                return streetNum;
            }

            public void setStreetNum(String streetNum) {
                this.streetNum = streetNum;
            }

            public String getAddress() {
                return address;
            }

            public void setAddress(String address) {
                this.address = address;
            }

            public double getLatitude() {
                return latitude;
            }

            public void setLatitude(double latitude) {
                this.latitude = latitude;
            }

            public double getLongitude() {
                return longitude;
            }

            public void setLongitude(double longitude) {
                this.longitude = longitude;
            }

            public String getDistrict() {
                return district;
            }

            public void setDistrict(String district) {
                this.district = district;
            }


        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if(mLocationClient!=null){
            mLocationClient.stopLocation();
        }

    }
}

GPSUtil

public class GPSUtil
{
    public static double pi = 3.1415926535897932384626;
    public static double x_pi = 3.14159265358979324 * 3000.0 / 180.0;
    public static double a = 6378245.0;
    public static double ee = 0.00669342162296594323;

    public static double transformLat(double x, double y)
    {
        double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
        ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(y * pi) + 40.0 * Math.sin(y / 3.0 * pi)) * 2.0 / 3.0;
        ret += (160.0 * Math.sin(y / 12.0 * pi) + 320 * Math.sin(y * pi / 30.0)) * 2.0 / 3.0;
        return ret;
    }

    public static double transformLon(double x, double y)
    {
        double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
        ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(x * pi) + 40.0 * Math.sin(x / 3.0 * pi)) * 2.0 / 3.0;
        ret += (150.0 * Math.sin(x / 12.0 * pi) + 300.0 * Math.sin(x / 30.0 * pi)) * 2.0 / 3.0;
        return ret;
    }

    public static double[] transform(double lat, double lon)
    {
        if (outOfChina(lat, lon))
        {
            return new double[]{lat, lon};
        }
        double dLat = transformLat(lon - 105.0, lat - 35.0);
        double dLon = transformLon(lon - 105.0, lat - 35.0);
        double radLat = lat / 180.0 * pi;
        double magic = Math.sin(radLat);
        magic = 1 - ee * magic * magic;
        double sqrtMagic = Math.sqrt(magic);
        dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);
        dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi);
        double mgLat = lat + dLat;
        double mgLon = lon + dLon;
        return new double[]{mgLat, mgLon};
    }

    public static boolean outOfChina(double lat, double lon)
    {
        if (lon < 72.004 || lon > 137.8347)
            return true;
        if (lat < 0.8293 || lat > 55.8271)
            return true;
        return false;
    }

    /**
     * 84 to 火星坐标系 (GCJ-02) World Geodetic System ==> Mars Geodetic System
     *
     * @param lat
     * @param lon
     * @return
     */
    public static double[] gps84_To_Gcj02(double lat, double lon)
    {
        if (outOfChina(lat, lon))
        {
            return new double[]{lat, lon};
        }
        double dLat = transformLat(lon - 105.0, lat - 35.0);
        double dLon = transformLon(lon - 105.0, lat - 35.0);
        double radLat = lat / 180.0 * pi;
        double magic = Math.sin(radLat);
        magic = 1 - ee * magic * magic;
        double sqrtMagic = Math.sqrt(magic);
        dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);
        dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi);
        double mgLat = lat + dLat;
        double mgLon = lon + dLon;
        return new double[]{mgLat, mgLon};
    }

    /**
     * * 火星坐标系 (GCJ-02) to 84 * * @param lon * @param lat * @return
     */
    public static double[] gcj02_To_Gps84(double lat, double lon)
    {
        double[] gps = transform(lat, lon);
        double lontitude = lon * 2 - gps[1];
        double latitude = lat * 2 - gps[0];
        return new double[]{latitude, lontitude};
    }

    /**
     * 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的转换算法 将 GCJ-02 坐标转换成 BD-09 坐标
     *
     * @param lat
     * @param lon
     */
    public static double[] gcj02_To_Bd09(double lat, double lon)
    {
        double x = lon, y = lat;
        double z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * x_pi);
        double theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * x_pi);
        double tempLon = z * Math.cos(theta) + 0.0065;
        double tempLat = z * Math.sin(theta) + 0.006;
        double[] gps = {tempLat, tempLon};
        return gps;
    }

    /**
     * * 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的转换算法 * * 将 BD-09 坐标转换成GCJ-02 坐标 * * @param
     * bd_lat * @param bd_lon * @return
     */
    public static double[] bd09_To_Gcj02(double lat, double lon)
    {
        double x = lon - 0.0065, y = lat - 0.006;
        double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);
        double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);
        double tempLon = z * Math.cos(theta);
        double tempLat = z * Math.sin(theta);
        double[] gps = {tempLat, tempLon};
        return gps;
    }

    /**
     * 将gps84转为bd09
     *
     * @param lat
     * @param lon
     * @return
     */
    public static double[] gps84_To_bd09(double lat, double lon)
    {
        double[] gcj02 = gps84_To_Gcj02(lat, lon);
        double[] bd09 = gcj02_To_Bd09(gcj02[0], gcj02[1]);
        return bd09;
    }

    public static double[] bd09_To_gps84(double lat, double lon)
    {
        double[] gcj02 = bd09_To_Gcj02(lat, lon);
        double[] gps84 = gcj02_To_Gps84(gcj02[0], gcj02[1]);
        //保留小数点后六位
        gps84[0] = retain6(gps84[0]);
        gps84[1] = retain6(gps84[1]);
        return gps84;
    }

    /**
     * 保留小数点后六位
     *
     * @param num
     * @return
     */
    private static double retain6(double num)
    {
        String result = String.format("%.6f", num);
        return Double.valueOf(result);
    }

}



总结

这个只是拿定位做个例子,其实这个就是原生和js交互的通用方法,可以直接使用,完成其他需要相互交互的逻辑



版权声明:本文为jifashihan原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。