传感器与地理位置


1 传感器

设备中包含许多传感器,如加速度传感器、角速度传感器、磁场传感器、温度传感器、大气压力传感器等等。这些传感器为设备提供了感知外界环境的能力。例如,借助加速度传感器,手机可以获知其姿态信息,运行在手机上的应用程序可以根据这些信息对横放、竖放、平放等不同姿态做出相应的反馈。

在鸿蒙系统中,所有关于传感器的API都被封装在ohos.sensor包的六个类中。这些类的名字均以Category开头,并以Agent结尾,中间单词为Motion、Environment、Orientation、Light、Body和Other,分别表示运动、环境、方向、光线、健康和其它传感器。

划分这些类的依据是面向应用的能力集,而非实现其功能的物理设备。一个传感器类可能会涉及多个不同的传感器设备,一个传感器设备也可能同时支持多个不同的传感器类。

传感器提供的信息随时都可能发生变化。为了满足数据更新的实时性,应用程序通常采用订阅的方式获取传感器的输出,即由传感器在数据发生变化时,主动通知应用程序,应用程序在与这些通知对应的回调方法中,被动地对传感器数据做出响应。

1.1 加速度传感器

z       y
|      /
    ______
   /     /
  /     /
 /     /
/_____/ _x

加速度传感器可以感知重力加速度在三个轴上的分量。

1.1.1 权限

"reqPermissions": [
  {
    "name": "ohos.permission.ACCELEROMETER"
  }
]

1.1.2 代理

CategoryMotionAgent agent = new CategoryMotionAgent();

1.1.3 对象

CategoryMotion motion = agent.getSingleSensor(
        CategoryMotion.SENSOR_TYPE_ACCELEROMETER);

1.1.4 回调

ICategoryMotionDataCallback callback =
    new ICategoryMotionDataCallback() {
        @Override
        public void onSensorDataModified(
            CategoryMotionData categoryMotionData) {
            // 数据更新回调
            ...
        }

        @Override
        public void onAccuracyDataModified(
            CategoryMotion categoryMotion, int i) {
            // 精度改变回调
            ...
        }

        @Override
        public void onCommandCompleted(
            CategoryMotion categoryMotion) {
            // 命令完成回调
            ...
        }
    };

1.1.5 订阅

agent.setSensorDataCallback(callback, motion,
    SensorAgent.SENSOR_SAMPLING_RATE_UI);

1.1.6 退订

agent.releaseSensorDataCallback(callback, motion);

例程:Gravity

...\Gravity\entry\src\main\config.json

{
  ...
  "module": {
    ...
    "reqPermissions": [
      {
        "name": "ohos.permission.ACCELEROMETER"
      }
    ]
  }
}

...\Gravity\entry\src\main\resources\base\layout\ability_main.xml

<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:height="match_parent"
    ohos:width="match_parent"
    ohos:alignment="center"
    ohos:orientation="vertical"
    ohos:background_element="#000000">

    <DirectionalLayout
        ohos:height="match_content"
        ohos:width="match_parent"
        ohos:orientation="horizontal">

        <Text
            ohos:height="match_content"
            ohos:width="match_parent"
            ohos:weight="0.3"
            ohos:text="Gx"
            ohos:text_size="28fp"
            ohos:text_color="#ffffff"
            ohos:text_alignment="right"
            />

        <Text
            ohos:height="match_content"
            ohos:width="match_parent"
            ohos:weight="0.1"
            ohos:text="="
            ohos:text_size="28fp"
            ohos:text_color="#ffffff"
            ohos:text_alignment="horizontal_center"
            />

        <Text
            ohos:id="$+id:txtGx"
            ohos:height="match_content"
            ohos:width="match_parent"
            ohos:weight="0.6"
            ohos:text_size="28fp"
            ohos:text_color="#ff7f27"
            ohos:text_alignment="left"
            />

    </DirectionalLayout>

    <DirectionalLayout
        ohos:height="match_content"
        ohos:width="match_parent"
        ohos:orientation="horizontal">

        <Text
            ohos:height="match_content"
            ohos:width="match_parent"
            ohos:weight="0.3"
            ohos:text="Gy"
            ohos:text_size="28fp"
            ohos:text_color="#ffffff"
            ohos:text_alignment="right"
            />

        <Text
            ohos:height="match_content"
            ohos:width="match_parent"
            ohos:weight="0.1"
            ohos:text="="
            ohos:text_size="28fp"
            ohos:text_color="#ffffff"
            ohos:text_alignment="horizontal_center"
            />

        <Text
            ohos:id="$+id:txtGy"
            ohos:height="match_content"
            ohos:width="match_parent"
            ohos:weight="0.6"
            ohos:text_size="28fp"
            ohos:text_color="#ff7f27"
            ohos:text_alignment="left"
            />

    </DirectionalLayout>

    <DirectionalLayout
        ohos:height="match_content"
        ohos:width="match_parent"
        ohos:orientation="horizontal">

        <Text
            ohos:height="match_content"
            ohos:width="match_parent"
            ohos:weight="0.3"
            ohos:text="Gz"
            ohos:text_size="28fp"
            ohos:text_color="#ffffff"
            ohos:text_alignment="right"
            />

        <Text
            ohos:height="match_content"
            ohos:width="match_parent"
            ohos:weight="0.1"
            ohos:text="="
            ohos:text_size="28fp"
            ohos:text_color="#ffffff"
            ohos:text_alignment="horizontal_center"
            />

        <Text
            ohos:id="$+id:txtGz"
            ohos:height="match_content"
            ohos:width="match_parent"
            ohos:weight="0.6"
            ohos:text_size="28fp"
            ohos:text_color="#ff7f27"
            ohos:text_alignment="left"
            />

    </DirectionalLayout>

</DirectionalLayout>

...\Gravity\entry\src\main\java\com\minwei\gravity\slice\MainAbilitySlice.java

public class MainAbilitySlice extends AbilitySlice {
    private Text txtGx, txtGy, txtGz;

    private CategoryMotionAgent agent;
    private CategoryMotion motion;
    private ICategoryMotionDataCallback callback;

    @Override
    public void onStart(Intent intent) {
        ...
        txtGx = (Text)findComponentById(ResourceTable.Id_txtGx);
        txtGy = (Text)findComponentById(ResourceTable.Id_txtGy);
        txtGz = (Text)findComponentById(ResourceTable.Id_txtGz);

        agent = new CategoryMotionAgent();
        motion = agent.getSingleSensor(
            CategoryMotion.SENSOR_TYPE_ACCELEROMETER);
        callback = new ICategoryMotionDataCallback() {
                @Override
                public void onSensorDataModified(
                    CategoryMotionData categoryMotionData) {
                    getUITaskDispatcher().asyncDispatch(() -> {
                        txtGx.setText(String.valueOf(
                            categoryMotionData.values[0]));
                        txtGy.setText(String.valueOf(
                            categoryMotionData.values[1]));
                        txtGz.setText(String.valueOf(
                            categoryMotionData.values[2]));
                    });
                }

                @Override
                public void onAccuracyDataModified(
                    CategoryMotion categoryMotion, int i) {
                }

                @Override
                public void onCommandCompleted(
                    CategoryMotion categoryMotion) {
                }
            };

        agent.setSensorDataCallback(callback, motion,
            SensorAgent.SENSOR_SAMPLING_RATE_UI);
    }
    ...
    @Override
    protected void onStop() {
        super.onStop();

        agent.releaseSensorDataCallback(callback, motion);
    }
}

运行效果如下图所示:

1.2 传感器的操作方法与分类

1.2.1 传感器的一般操作流程

1.2.2 传感器分类

分类 代理 测量值
运动 CategoryMotionAgent 加速度、角速度、步数
环境 CategoryEnvironmentAgent 温度、湿度、磁场、气压
方向 CategoryOrientationAgent 自由度、屏向、设备方向、旋转矢量
光线 CategoryLightAgent 接近光、环境光、色温、ToF
健康 CategoryBodyAgent 心率、佩戴
其它 CategoryOtherAgent 霍尔、手握
CategoryMotionAgent agent = new CategoryMotionAgent();
        \    /
         分类
        /    \
CategoryMotion motion = agent.getSingleSensor(
        CategoryMotion.SENSOR_TYPE_ACCELEROMETER);
                       \_______________________/
                                 测量值

1.2.3 方向传感器与指南针

CategoryOrientationAgent agent = new CategoryOrientationAgent();
CategoryOrientation orientation = agent.getSingleSensor(
    CategoryOrientation.SENSOR_TYPE_ORIENTATION);
ICategoryOrientationDataCallback callback =
    new ICategoryOrientationDataCallback() {
        @Override
        public void onSensorDataModified(
            ...
        }

        @Override
        public void onAccuracyDataModified(
            CategoryOrientation categoryOrientation, int i) {
            ...
        }

        @Override
        public void onCommandCompleted(
            CategoryOrientation categoryOrientation) {
            ...
        }
    };
agent.setSensorDataCallback(callback, orientation,
    SensorAgent.SENSOR_SAMPLING_RATE_UI);
agent.releaseSensorDataCallback(callback, orientation);

例程:Compass

...\Compass\entry\src\main\config.json

{
  ...
  "module": {
    ...
    "abilities": [
      {
        ...
        "orientation": "portrait",
        ...
      }
    ]
  }
}

...\Compass\entry\src\main\resources\base\media\compass.png

...\Compass\entry\src\main\resources\base\media\arrow.png

...\Compass\entry\src\main\resources\base\layout\ability_main.xml

<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:height="match_parent"
    ohos:width="match_parent"
    ohos:alignment="center"
    ohos:orientation="vertical"
    ohos:background_element="#1b1c20">

    <StackLayout
        ohos:height="match_content"
        ohos:width="match_content">

        <Image
            ohos:id="$+id:img"
            ohos:height="match_content"
            ohos:width="match_content"
            ohos:top_margin="60vp"
            ohos:image_src="$media:compass"
            />

        <DirectionalLayout
            ohos:height="match_content"
            ohos:width="match_parent"
            ohos:alignment="center"
            ohos:orientation="vertical">

            <Text
                ohos:id="$+id:txt"
                ohos:height="match_content"
                ohos:width="match_parent"
                ohos:text_size="28fp"
                ohos:text_color="#d04c40"
                ohos:text_alignment="horizontal_center"/>

            <Image
                ohos:height="match_content"
                ohos:width="match_content"
                ohos:top_margin="20vp"
                ohos:image_src="$media:arrow"
                />

        </DirectionalLayout>

    </StackLayout>

</DirectionalLayout>

...\Compass\entry\src\main\java\com\minwei\compass\slice\MainAbilitySlice.java

public class MainAbilitySlice extends AbilitySlice {
    private Text txt;
    private Component img;

    private CategoryOrientationAgent agent;
    private CategoryOrientation orientation;
    private ICategoryOrientationDataCallback callback;

    @Override
    public void onStart(Intent intent) {
        ...
        txt = (Text)findComponentById(ResourceTable.Id_txt);
        img = findComponentById(ResourceTable.Id_img);

        agent = new CategoryOrientationAgent();
        orientation = agent.getSingleSensor(
            CategoryOrientation.SENSOR_TYPE_ORIENTATION);
        callback = new ICategoryOrientationDataCallback() {
                @Override
                public void onSensorDataModified(
                    CategoryOrientationData categoryOrientationData) {
                    getUITaskDispatcher().asyncDispatch(() -> {
                        txt.setText("北偏" + String.valueOf(
                            (int)categoryOrientationData.values[0]) + "°");
                        img.setRotation(-categoryOrientationData.values[0]);
                    });
                }

                @Override
                public void onAccuracyDataModified(
                    CategoryOrientation categoryOrientation, int i) {
                }

                @Override
                public void onCommandCompleted(
                    CategoryOrientation categoryOrientation) {
                }
            };

        agent.setSensorDataCallback(callback, orientation,
            SensorAgent.SENSOR_SAMPLING_RATE_UI);
    }
    ...
    @Override
    protected void onStop() {
        super.onStop();

        agent.releaseSensorDataCallback(callback, orientation);
    }
}

运行效果如下图所示:

2 地理位置

2.1 位置信息的获取

其中请求参数中包含了与精度和功耗有关的各种优先级和应用场景,如:

获取位置信息需要ohos.permission.LOCATION权限,该权限需要动态申请。

Locator locator = new Locator(this);
RequestParam param = new RequestParam(RequestParam.SCENE_NAVIGATION);
LocatorCallback callback = new LocatorCallback() {
    @Override
    public void onLocationReport(Location location) {
        ...
    }

    @Override
    public void onStatusChanged(int i) {
        ...
    }

    @Override
    public void onErrorReport(int i) {
        ...
    }
};
locator.startLocating(param, callback);
locator.stopLocating(callback);

例程:Locator

...\Locator\entry\src\main\config.json

{
  ...
  "module": {
    ...
    "reqPermissions": [
      {
        "name": "ohos.permission.LOCATION"
      }
    ]
  }
}

...\Locator\entry\src\main\resources\base\layout\ability_main.xml

<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:height="match_parent"
    ohos:width="match_parent"
    ohos:alignment="center"
    ohos:orientation="vertical"
    ohos:background_element="#000000">

    <DirectionalLayout
        ohos:height="match_content"
        ohos:width="match_parent"
        ohos:orientation="horizontal">

        <Text
            ohos:height="match_content"
            ohos:width="match_parent"
            ohos:weight="0.3"
            ohos:text="经度"
            ohos:text_size="28fp"
            ohos:text_color="#ffffff"
            ohos:text_alignment="right"
            />

        <Text
            ohos:height="match_content"
            ohos:width="match_parent"
            ohos:weight="0.1"
            ohos:text="="
            ohos:text_size="28fp"
            ohos:text_color="#ffffff"
            ohos:text_alignment="horizontal_center"
            />

        <Text
            ohos:id="$+id:txtLongitude"
            ohos:height="match_content"
            ohos:width="match_parent"
            ohos:weight="0.6"
            ohos:text_size="28fp"
            ohos:text_color="#22b14c"
            ohos:text_alignment="left"
            />

    </DirectionalLayout>

    <DirectionalLayout
        ohos:height="match_content"
        ohos:width="match_parent"
        ohos:orientation="horizontal">

        <Text
            ohos:height="match_content"
            ohos:width="match_parent"
            ohos:weight="0.3"
            ohos:text="纬度"
            ohos:text_size="28fp"
            ohos:text_color="#ffffff"
            ohos:text_alignment="right"
            />

        <Text
            ohos:height="match_content"
            ohos:width="match_parent"
            ohos:weight="0.1"
            ohos:text="="
            ohos:text_size="28fp"
            ohos:text_color="#ffffff"
            ohos:text_alignment="horizontal_center"
            />

        <Text
            ohos:id="$+id:txtLatitude"
            ohos:height="match_content"
            ohos:width="match_parent"
            ohos:weight="0.6"
            ohos:text_size="28fp"
            ohos:text_color="#22b14c"
            ohos:text_alignment="left"
            />

    </DirectionalLayout>

</DirectionalLayout>

...\Locator\entry\src\main\java\com\minwei\locator\slice\MainAbilitySlice.java

public class MainAbilitySlice extends AbilitySlice {
    private Text txtLongitude, txtLatitude;

    private Locator locator;
    private LocatorCallback callback;

    @Override
    public void onStart(Intent intent) {
        ...
        requestPermissions();

        txtLongitude = (Text)findComponentById(
            ResourceTable.Id_txtLongitude);
        txtLatitude = (Text)findComponentById(
            ResourceTable.Id_txtLatitude);

        locator = new Locator(this);
        RequestParam param = new RequestParam(
            RequestParam.SCENE_NAVIGATION);
        callback = new LocatorCallback() {
            @Override
            public void onLocationReport(Location location) {
                getUITaskDispatcher().asyncDispatch(() -> {
                    txtLongitude.setText(String.valueOf(
                        location.getLongitude()));
                    txtLatitude.setText(String.valueOf(
                        location.getLatitude()));
                });
            }

            @Override
            public void onStatusChanged(int i) {
            }

            @Override
            public void onErrorReport(int i) {
            }
        };

        locator.startLocating(param, callback);
    }
    ...
    @Override
    protected void onStop() {
        super.onStop();

        locator.stopLocating(callback);
    }

    private void requestPermissions() {
        List<String> reqPermissions = new ArrayList<>();

        String[] permissions = {"ohos.permission.LOCATION"};
        for (String permission : permissions)
            if (verifySelfPermission(permission) != 0 &&
                canRequestPermission(permission))
                reqPermissions.add(permission);

        requestPermissionsFromUser(
            reqPermissions.toArray(new String[0]), 0);
    }
}

运行效果如下图所示:

2.2 地理编码

通过地理编码(Geocode)可以实现地名地址和地理坐标(经纬度)之间的相互转换:

使用地理编码需要访问网络,应先申请ohos.permission.INTERNET权限。

GeoConvert geoConvert = new GeoConvert(Locale.getDefault());
List<GeoAddress> geoAddresses = geoConvert
    .getAddressFromLocationName(name, 1);
List<GeoAddress> geoAddresses = geoConvert
    .getAddressFromLocation(latitude, longitude, 1);

例程:Geocode

...\Geocode\entry\src\main\config.json

{
  ...
  "module": {
    ...
    "reqPermissions": [
      {
        "name": "ohos.permission.INTERNET"
      }
    ]
  }
}

...\Geocode\entry\src\main\resources\base\graphic\background_button.xml

<?xml version="1.0" encoding="utf-8"?>
<shape
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:shape="rectangle">
    <corners ohos:radius="100"/>
    <solid ohos:color="#00a2e8"/>
</shape>

...\Geocode\entry\src\main\resources\base\layout\ability_main.xml

<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:height="match_parent"
    ohos:width="match_parent"
    ohos:left_padding="80vp"
    ohos:right_padding="80vp"
    ohos:alignment="center"
    ohos:orientation="vertical">

    <Text
        ohos:height="match_content"
        ohos:width="match_content"
        ohos:text="地理编码"
        ohos:text_size="28fp"
        ohos:text_color="#00a2e8"
        />

    <Text
        ohos:height="2vp"
        ohos:width="match_parent"
        ohos:top_margin="10vp"
        ohos:bottom_margin="5vp"
        ohos:background_element="#00a2e8"
        />

    <Button
        ohos:id="$+id:btnCode"
        ohos:height="match_content"
        ohos:width="match_parent"
        ohos:padding="8vp"
        ohos:top_margin="10vp"
        ohos:background_element="$graphic:background_button"
        ohos:text="地址转坐标"
        ohos:text_size="24fp"
        ohos:text_color="#ffffff"
        />

    <Button
        ohos:id="$+id:btnDecode"
        ohos:height="match_content"
        ohos:width="match_parent"
        ohos:padding="8vp"
        ohos:top_margin="10vp"
        ohos:background_element="$graphic:background_button"
        ohos:text="坐标转地址"
        ohos:text_size="24fp"
        ohos:text_color="#ffffff"
        />

</DirectionalLayout>

...\Geocode\entry\src\main\java\com\minwei\geocode\slice\MainAbilitySlice.java

public class MainAbilitySlice extends AbilitySlice {
    private static final HiLogLabel label = new HiLogLabel(
        HiLog.LOG_APP, 0x00101,
        MainAbilitySlice.class.getCanonicalName());

    @Override
    public void onStart(Intent intent) {
        ...
        findComponentById(ResourceTable.Id_btnCode)
            .setClickedListener(component -> onCode());
        findComponentById(ResourceTable.Id_btnDecode)
            .setClickedListener(component -> onDecode());
    }
    ...
    private void onCode() {
        getGlobalTaskDispatcher(TaskPriority.DEFAULT)
            .asyncDispatch(() -> code("北京动物园"));
    }

    private void onDecode() {
        getGlobalTaskDispatcher(TaskPriority.DEFAULT)
            .asyncDispatch(() -> decode(116.333719, 39.940016));
    }

    private void code(String name) {
        GeoConvert geoConvert = new GeoConvert(Locale.getDefault());

        try {
            final List<GeoAddress> geoAddresses =
                geoConvert.getAddressFromLocationName(name, 1);

            for (GeoAddress geoAddress : geoAddresses)
                printAddress(geoAddress);
        }
        catch (Exception exception) {
            HiLog.info(label, exception.getLocalizedMessage());
        }
    }

    private void decode(double longitude, double latitude) {
        GeoConvert geoConvert = new GeoConvert(Locale.getDefault());

        try {
            final List<GeoAddress> geoAddresses =
                geoConvert.getAddressFromLocation(latitude, longitude, 1);

            for (GeoAddress geoAddress : geoAddresses)
                printAddress(geoAddress);
        }
        catch (Exception exception) {
            HiLog.info(label, exception.getLocalizedMessage());
        }
    }

    private void printAddress(GeoAddress geoAddress) {
        String s = geoAddress.getLocale().toString();
        if (s != null && !s.isEmpty())
            HiLog.info(label, "   语言:%{public}s", s);

        s = geoAddress.getCountryCode();
        if (s != null && !s.isEmpty())
            HiLog.info(label, " 国家代码:%{public}s", s);

        s = geoAddress.getCountryName();
        if (s != null && !s.isEmpty())
            HiLog.info(label, " 国家名称:%{public}s", s);

        s = geoAddress.getAdministrativeArea();
        if (s != null && !s.isEmpty())
            HiLog.info(label, "省级行政区:%{public}s", s);

        s = geoAddress.getLocality();
        if (s != null && !s.isEmpty())
            HiLog.info(label, "地级行政区:%{public}s", s);

        s = geoAddress.getSubAdministrativeArea();
        if (s != null && !s.isEmpty())
            HiLog.info(label, "乡级行政区:%{public}s", s);

        s = geoAddress.getSubLocality();
        if (s != null && !s.isEmpty())
            HiLog.info(label, "县级行政区:%{public}s", s);

        s = geoAddress.getRoadName();
        if (s != null && !s.isEmpty())
            HiLog.info(label, " 道路名称:%{public}s", s);

        s = geoAddress.getSubRoadName();
        if (s != null && !s.isEmpty())
            HiLog.info(label, "道路子名称:%{public}s", s);

        s = geoAddress.getPlaceName();
        if (s != null && !s.isEmpty())
            HiLog.info(label, "   地名:%{public}s", s);

        s = geoAddress.getPremises();
        if (s != null && !s.isEmpty())
            HiLog.info(label, "单元房间号:%{public}s", s);

        s = geoAddress.getPostalCode();
        if (s != null && !s.isEmpty())
            HiLog.info(label, " 邮政编码:%{public}s", s);

        s = geoAddress.getPhoneNumber();
        if (s != null && !s.isEmpty())
            HiLog.info(label, " 电话号码:%{public}s", s);

        if (geoAddress.hasLongitude())
            HiLog.info(label, "   经度:%{public}f",
                geoAddress.getLongitude());

        if (geoAddress.hasLatitude())
            HiLog.info(label, "   纬度:%{public}f",
                geoAddress.getLatitude());

        for (int i = 0; i < geoAddress.getDescriptionsSize(); ++i)
            HiLog.info(label, " 描述信息:%{public}s",
                geoAddress.getDescriptions(i));
    }
}

运行效果如下图所示:

2.3 轻量级地图组件TinyMap

TinyMap是HarmonyOS技术的先行者——董昱先生开发的首个可在鸿蒙系统上运行的开源地图组件。其开源地址为:

https://gitee.com/dongyu1009/tiny-map-for-harmony-os

其包结构如下:

com
 │
 └─dongyu
     │
     └─tinymap
          │
          ├─Element.java
          ├─Tile.java
          └─TinyMap.java

由于TinyMap使用的是高德在线地图,因此需要申请网络访问权限:

"reqPermissions": [
  {
    "name": "ohos.permission.INTERNET"
  }
]

在页面布局中添加TinyMap组件:

<com.dongyu.tinymap.TinyMap
    ohos:id="$+id:tm"
    ohos:height="match_parent"
    ohos:width="match_parent"/>

并在Java代码中获取该组件:

TinyMap tm = (TinyMap)findComponentById(ResourceTable.Id_tm);

TinyMap的五个常用方法:

例程:Map

...\Map\entry\src\main\config.json

{
  ...
  "module": {
    ...
    "reqPermissions": [
      {
        "name": "ohos.permission.INTERNET"
      },
      {
        "name": "ohos.permission.LOCATION"
      }
    ]
  }
}

...\Map\entry\src\main\resources\base\media\dot.png

...\Map\entry\src\main\resources\base\graphic\background_blue_button.xml

<?xml version="1.0" encoding="utf-8"?>
<shape
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:shape="rectangle">
    <corners ohos:radius="32"/>
    <solid ohos:color="#c000a2e8"/>
</shape>

...\Map\entry\src\main\resources\base\graphic\background_green_button.xml

<?xml version="1.0" encoding="utf-8"?>
<shape
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:shape="rectangle">
    <corners ohos:radius="32"/>
    <solid ohos:color="#c022b14c"/>
</shape>

...\Map\entry\src\main\resources\base\graphic\background_orange_button.xml

<?xml version="1.0" encoding="utf-8"?>
<shape
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:shape="rectangle">
    <corners ohos:radius="32"/>
    <solid ohos:color="#c0ff7f27"/>
</shape>

...\Map\entry\src\main\resources\base\graphic\background_red_button.xml

<?xml version="1.0" encoding="utf-8"?>
<shape
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:shape="rectangle">
    <corners ohos:radius="32"/>
    <solid ohos:color="#c0ff4040"/>
</shape>

...\Map\entry\src\main\resources\base\layout\ability_main.xml

<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:height="match_parent"
    ohos:width="match_parent">

    <StackLayout
        ohos:height="match_parent"
        ohos:width="match_parent">

        <com.dongyu.tinymap.TinyMap
            ohos:id="$+id:tm"
            ohos:height="match_parent"
            ohos:width="match_parent"/>

        <DirectionalLayout
            ohos:height="match_parent"
            ohos:width="match_parent"
            ohos:margin="10vp">

            <Button
                ohos:id="$+id:btnZoomin"
                ohos:height="match_content"
                ohos:width="match_content"
                ohos:padding="8vp"
                ohos:layout_alignment="right"
                ohos:background_element="$graphic:background_blue_button"
                ohos:text="放大"
                ohos:text_size="20fp"
                ohos:text_color="#ffffff"
                />

            <Button
                ohos:id="$+id:btnZoomout"
                ohos:height="match_content"
                ohos:width="match_content"
                ohos:padding="8vp"
                ohos:top_margin="5vp"
                ohos:layout_alignment="right"
                ohos:background_element="$graphic:background_green_button"
                ohos:text="缩小"
                ohos:text_size="20fp"
                ohos:text_color="#ffffff"
                />

            <Button
                ohos:id="$+id:btnSwitch"
                ohos:height="match_content"
                ohos:width="match_content"
                ohos:padding="8vp"
                ohos:top_margin="5vp"
                ohos:layout_alignment="right"
                ohos:background_element="$graphic:background_orange_button"
                ohos:text="切换"
                ohos:text_size="20fp"
                ohos:text_color="#ffffff"
                />


            <Button
                ohos:id="$+id:btnLocator"
                ohos:height="match_content"
                ohos:width="match_content"
                ohos:padding="8vp"
                ohos:top_margin="5vp"
                ohos:layout_alignment="right"
                ohos:background_element="$graphic:background_red_button"
                ohos:visibility="hide"
                ohos:text="定位"
                ohos:text_size="20fp"
                ohos:text_color="#ffffff"
                />

        </DirectionalLayout>

    </StackLayout>

</DirectionalLayout>

...\Map\entry\src\main\java\com\minwei\map\slice\MainAbilitySlice.java

public class MainAbilitySlice extends AbilitySlice {
    private TinyMap tm;

    private Locator locator;
    private LocatorCallback callback;

    private double longitude, latitude;
    private int source;

    @Override
    public void onStart(Intent intent) {
        ...
        requestPermissions();

        tm = (TinyMap)findComponentById(ResourceTable.Id_tm);
        switchSource(0);

        findComponentById(ResourceTable.Id_btnZoomin)
            .setClickedListener(component -> onZoomin());
        findComponentById(ResourceTable.Id_btnZoomout)
            .setClickedListener(component -> onZoomout());
        findComponentById(ResourceTable.Id_btnSwitch)
            .setClickedListener(component -> onSwitch());
        findComponentById(ResourceTable.Id_btnLocator)
            .setClickedListener(component -> onLocator());

        locator = new Locator(this);
        RequestParam param = new RequestParam(
            RequestParam.SCENE_NAVIGATION);
        callback = new LocatorCallback() {
            @Override
            public void onLocationReport(Location location) {
                longitude = location.getLongitude();
                latitude = location.getLatitude();

                getUITaskDispatcher().asyncDispatch(() -> {
                    findComponentById(ResourceTable.Id_btnLocator)
                        .setVisibility(Component.VISIBLE);
                });
            }

            @Override
            public void onStatusChanged(int i) {
            }

            @Override
            public void onErrorReport(int i) {
            }
        };

        locator.startLocating(param, callback);
    }
    ...
    @Override
    protected void onStop() {
        super.onStop();

        locator.stopLocating(callback);
    }

    private void requestPermissions() {
        List<String> reqPermissions = new ArrayList<>();

        String[] permissions = {"ohos.permission.LOCATION"};
        for (String permission : permissions)
            if (verifySelfPermission(permission) != 0 &&
                canRequestPermission(permission))
                reqPermissions.add(permission);

        requestPermissionsFromUser(
            reqPermissions.toArray(new String[0]), 0);
    }

    private void onZoomin() {
        tm.zoomIn();
    }

    private void onZoomout() {
        tm.zoomOut();
    }

    private void onSwitch() {
        ListDialog dialog = new ListDialog(this,
            ListDialog.SINGLE);
        dialog.setAutoClosable(true);

        dialog.setSingleSelectItems(new String[] {
            "高德地图 - 矢量", "高德地图 - 道路", "高德地图 - 卫星"}, source);
        dialog.setOnSingleSelectListener(
            new IDialog.ClickedListener() {
                @Override
                public void onClick(IDialog iDialog, int i) {
                    switchSource(i);
                }
            });

        dialog.show();
        dialog.getListContainer().setPaddingLeft(42);
    }

    private void onLocator() {
        double[] mercator = toMercator(longitude, latitude);
        tm.addElement((float)mercator[0], (float)mercator[1],
            ResourceTable.Media_dot);
    }

    private void switchSource(int src) {
        switch (source = src) {
            case 0:
                tm.setMapSource(MapSource.GAODE_VECTOR);
                break;

            case 1:
                tm.setMapSource(MapSource.GAODE_ROAD);
                break;

            case 2:
                tm.setMapSource(MapSource.GAODE_SATELLITE);
                break;
        }

        tm.refreshMap();
    }

    private double[] toMercator(double longitude, double latitude) {
        double x = longitude * 20037508.342789 / 180;
        double y = Math.log(Math.tan((latitude + 90) * Math.PI / 360)) /
            (Math.PI / 180) * 20037508.342789 / 180;
        return new double[] {x, y};
    }
}

运行效果如下图所示:

更多精彩,敬请期待……


达内集团C++教学部 2021年10月12日