前言场景设想:孩子戴着手表去上补习班、在小区内玩耍等等,手表可以将实时位置发送给家长,超出设置的活动范围内时提醒家长,实现孩子安全防患于未然。
本次项目由于缺少鸿蒙手表设备,故用手机或平板替代。
概述孩子端设备界面如下,每隔五秒钟将实时位置发送给家长端设备:
家长端设备每隔五秒钟将孩子端设备的实时位置显示出来:
孩子端设备不在设定的经纬度范围时会显示“孩子已超出设定范围”,并发出声音提醒家长:
孩子端设备在设定的经纬度范围时会显示“孩子没有超出设定范围”,并停止发出声音:
当没有设定范围或设定的经纬度范围不合理时即经度小于0°大于180°,纬度小于0°大于90°时,显示“没有设定范围或者输入的经度不在0到180之间、输入的纬度不在0到90之间”:
正文一、创建一个空白的工程1. 安装和配置DevEco Studio 2.1 ReleaseDevEco Studio 2.1 Release下载、DevEco Studio 2.1 Release安装
2. 创建一个Empty Java Phone应用DevEco Studio下载安装成功后,打开DevEco Studio,点击左上角的File,点击New,再选择New Project,选择Empty Ability(Java)选项,点击Next按钮。
将文件命名为ChildrenLocation(文件名不能出现中文或者特殊字符,否则将无法成功创建项目文件),选择保存路径,选择API5,设备勾选Phone、Tablet,最后点击Finish按钮。
3. 准备工作在entry>src>main>config.json文件中最下方"launchType": "standard"的后面添加以下代码,这样就可以实现去掉应用上方的标签栏了。
config.json最下方部分代码:
- "orientation": "unspecified",
- "name": "com.test.qixifestival.MainAbility",
- "icon": "$media:icon",
- "description": "$string:mainability_description",
- "label": "$string:entry_MainAbility",
- "type": "page",
- "launchType": "standard",
- "metaData": {
- "customizeData": [
- {
- "name": "hwc-theme",
- "value": "androidhwext:style/Theme.Emui.Light.NoTitleBar",
- "extra": ""
- }
- ]
- }
- }
- ]
- }
- }
复制代码 二、实现界面布局1. 孩子端设备界面在ChildrenLocation>entry>src>main>resources>base>layout>ability_main.xml添加布局代码。
删除已有的Text组件。
- <?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">
- </DirectionalLayout>
复制代码 添加一个名为ability_wearable.xml布局文件,布局代码如下,将相应图片文件放到文件夹media中,并且命名为image。- <?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:orientation="vertical">
- <Image
- ohos:height="match_parent"
- ohos:width="match_parent"
- ohos:image_src="$media:image"
- ohos:layout_alignment="center"/>
- </DirectionalLayout>
复制代码 2. 家长端设备界面添加一个名为ability_phone.xml布局文件,布局代码如下。
在ChildrenLocation>entry>src>main>java>com.test.qixifestival>slice>MainAbilitySlice.java编写以下代码。
1)如果是不同类型的设备,例如手机和平板,那可以根据设备类型来区分家长端和孩子端。
2)如果是相同类型不同型号的设备,例如HUAWEI P40和nova 5 Pro,那可以根据设备型号来区分家长端和孩子端。
3)如果是相同类型相同型号的设备,那只能根据设备的id来区分家长端和孩子端,但是不推荐此方法。- public void onStart(Intent intent) {
- super.onStart(intent);
- //1)根据设备类型来区分家长端和孩子端
- /*String s = KvManagerFactory.getInstance().createKvManager(new KvManagerConfig(this)).getLocalDeviceInfo().getType();
- if(s.equals("14")){
- super.setUIContent(ResourceTable.Layout_ability_phone);
- }else{
- super.setUIContent(ResourceTable.Layout_ability_wearable);
- }*/
- //2)根据设备型号来区分家长端和孩子端
- String s = KvManagerFactory.getInstance().createKvManager(new KvManagerConfig(this)).getLocalDeviceInfo().getName();
- if(s.equals("HUAWEI P40")){
- super.setUIContent(ResourceTable.Layout_ability_phone);
- }else{
- super.setUIContent(ResourceTable.Layout_ability_wearable);
- }
- //3)根据设备的id来区分家长端和孩子端
- /*String s = KvManagerFactory.getInstance().createKvManager(new KvManagerConfig(this)).getLocalDeviceInfo().getId();
- if(s.equals("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")){
- super.setUIContent(ResourceTable.Layout_ability_phone);
- }else{
- super.setUIContent(ResourceTable.Layout_ability_wearable);
- }*/
- }
复制代码 三、获取孩子端设备实时位置1. 添加权限在config.json添加权限。
- "reqPermissions": [
- {
- "name": "ohos.permission.LOCATION"
- }
- ]
复制代码 2. 获取孩子端位置在MainAbility.java中动态获取权限,并创建一个静态成员变量用于获取位置信息。
- public class MainAbility extends Ability {
- Locator locator;
- MyLocatorCallback locatorCallback;
- RequestParam requestParam;
- public static Location location = null;
- public void onStart(Intent intent) {
- super.onStart(intent);
- super.setMainRoute(MainAbilitySlice.class.getName());
- // 动态判断权限
- if (verifySelfPermission("ohos.permission.LOCATION") != IBundleManager.PERMISSION_GRANTED) {
- // 应用未被授予权限
- if (canRequestPermission("ohos.permission.LOCATION")) {
- // 是否可以申请弹框授权(首次申请或者用户未选择禁止且不再提示)
- requestPermissionsFromUser(new String[]{"ohos.permission.LOCATION"}, 0);
- }
- } else {
- initLocator();
- }
- }
- private void initLocator() {
- locator = new Locator(this);
- requestParam = new RequestParam(RequestParam.SCENE_NAVIGATION);
- locatorCallback = new MyLocatorCallback();
- locator.startLocating(requestParam, locatorCallback);
- }
- @Override
- protected void onStop() {
- super.onStop();
- locator.stopLocating(locatorCallback);
- }
-
- public class MyLocatorCallback implements LocatorCallback {//获取定位信息
- [url=home.php?mod=space&uid=2735960]@Override[/url]
- public void onLocationReport(Location location) {
- if (location != null) {
- MainAbility.location = location;
- }
- }
- @Override
- public void onStatusChanged(int type) {
-
- }
- @Override
- public void onErrorReport(int type) {
- }
- }
- }
复制代码 3. 添加一个分布式数据库在config.json添加权限。
- "reqPermissions": [
- {
- "name": "ohos.permission.DISTRIBUTED_DATASYNC"
- },
- {
- "name": "ohos.permission.LOCATION"
- }
- ]
复制代码 在MainAbility.java中动态申请权限。- public void onStart(Intent intent) {
- super.onStart(intent);
- super.setMainRoute(MainAbilitySlice.class.getName());
- // 动态判断权限
- if (verifySelfPermission("ohos.permission.LOCATION") != IBundleManager.PERMISSION_GRANTED || verifySelfPermission("ohos.permission.DISTRIBUTED_DATASYNC") != IBundleManager.PERMISSION_GRANTED) {
- // 应用未被授予权限
- if (canRequestPermission("ohos.permission.LOCATION") || canRequestPermission("ohos.permission.DISTRIBUTED_DATASYNC")) {
- // 是否可以申请弹框授权(首次申请或者用户未选择禁止且不再提示)
- requestPermissionsFromUser(new String[]{"ohos.permission.LOCATION", "ohos.permission.DISTRIBUTED_DATASYNC"}, 0);
- }
- } else {
- initLocator();
- }
- }
复制代码 在MainAbilitySlice.java中编写以下代码。
添加四个变量Longitude、Latitude、PlaceName和CountryName,并初始化为“null”。
创建分布式数据库管理器实例KvManager,并借助KvManager创建SINGLE_VERSION分布式数据库,最后订阅分布式数据库中数据变化。- public static final String STORE_ID = "contact_db1";//分布式数据库标识
- public static SingleKvStore singleKvStore;
- public static KvManager kvManager;
- private static String Longitude = "null";
- private static String Latitude = "null";
- private static String PlaceName = "null";
- private static String CountryName = "null";
- public void initDbManager() {
- kvManager = createManager();
- singleKvStore = createDb(kvManager);
- subscribeDb(singleKvStore);
- }
- public KvManager createManager() {//创建分布式数据库管理器实例KvManager
- KvManager manager = null;
- try {
- KvManagerConfig config = new KvManagerConfig(this);
- manager = KvManagerFactory.getInstance().createKvManager(config);
- }
- catch (KvStoreException exception) {
- }
- return manager;
- }
- private SingleKvStore createDb(KvManager kvManager) {//借助KvManager创建SINGLE_VERSION分布式数据库
- SingleKvStore kvStore = null;
- try {
- Options options = new Options();
- options.setCreateIfMissing(true).setEncrypt(false).setKvStoreType(KvStoreType.SINGLE_VERSION);
- kvStore = kvManager.getKvStore(options, STORE_ID);
- } catch (KvStoreException exception) {
- }
- return kvStore;
- }
- private void subscribeDb(SingleKvStore singleKvStore) {//订阅分布式数据库中数据变化
- class KvStoreObserverClient implements KvStoreObserver {
- @Override
- public void onChange(ChangeNotification notification) {
- Longitude = queryContact("Longitude");
- Latitude = queryContact("Latitude");
- PlaceName = queryContact("PlaceName");
- CountryName = queryContact("CountryName");
- }
- }
- KvStoreObserver kvStoreObserverClient = new KvStoreObserverClient();
- singleKvStore.subscribe(SubscribeType.SUBSCRIBE_TYPE_ALL, kvStoreObserverClient);
- }
- public static void writeData(String key, String value) {//数据插入
- if (key == null || key.isEmpty() || value == null || value.isEmpty()) {
- return;
- }
- singleKvStore.putString(key, value);
- }
- public static String queryContact(String key) {//数据查询
- String value = singleKvStore.getString(key);
- return value;
- }
复制代码 4. 将实时位置写入分布式数据库中通过location.getLongitude()方法获取经度,location.getLatitude()方法获取纬度,geoAddress.getPlaceName()方法获取位置,geoAddress.getCountryName()方法获取国家。
创建时间任务,每隔五秒钟获取一次孩子端设备位置信息,并将信息写入分布式数据库中。
- import static com.test.childrenlocation.MainAbility.location;
- private Timer timer_wearable;
- public void onStart(Intent intent) {
- super.onStart(intent);
- //1)根据设备类型来区分家长端和孩子端
- /*String s = KvManagerFactory.getInstance().createKvManager(new KvManagerConfig(this)).getLocalDeviceInfo().getType();
- if(s.equals("14")){
- super.setUIContent(ResourceTable.Layout_ability_phone);
- }else{
- super.setUIContent(ResourceTable.Layout_ability_wearable);
- }*/
- //2)根据设备型号来区分家长端和孩子端
- String s = KvManagerFactory.getInstance().createKvManager(new KvManagerConfig(this)).getLocalDeviceInfo().getName();
- initDbManager();
- if(s.equals("HUAWEI P40")){
- super.setUIContent(ResourceTable.Layout_ability_phone);
- }else{
- super.setUIContent(ResourceTable.Layout_ability_wearable);
- setLocation();
- }
- //3)根据设备的id来区分家长端和孩子端
- /*String s = KvManagerFactory.getInstance().createKvManager(new KvManagerConfig(this)).getLocalDeviceInfo().getId();
- if(s.equals("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")){
- super.setUIContent(ResourceTable.Layout_ability_phone);
- }else{
- super.setUIContent(ResourceTable.Layout_ability_wearable);
- }*/
- }
- private void setLocation(){
- timer_wearable = new Timer();
- timer_wearable.schedule(new TimerTask() {
- @Override
- public void run() {
- getUITaskDispatcher().asyncDispatch(new Runnable() {
- @Override
- public void run() {
- setSingleLocation();
- writeData("Longitude", Longitude);
- writeData("Latitude", Latitude);
- writeData("PlaceName", PlaceName);
- writeData("CountryName", CountryName);
- }
- });
- }
- }, 0, 5000);
- }
- private void setSingleLocation(){
- if(location == null){
- Longitude = "null";
- Latitude = "null";
- return;
- }
- Longitude = Double.toString(location.getLongitude());
- Latitude = Double.toString(location.getLatitude());
- GeoConvert geoConvert = new GeoConvert();
- try {
- List<GeoAddress> list = geoConvert.getAddressFromLocation(location.getLatitude(),location.getLongitude(),1);
- GeoAddress geoAddress = list.get(0);
- if(geoAddress == null){
- PlaceName = "null";
- CountryName = "null";
- return;
- }
- PlaceName = geoAddress.getPlaceName();
- CountryName = geoAddress.getCountryName();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
复制代码 四、显示孩子端设备实时信息1. 通过service播放声音添加一个ServiceAbility.java,将音频文件放到文件夹rawfile中,并命名为text.mp3。在onStart中播放音频,在onStop中停止播放音频。
- public class ServiceAbility extends Ability {
- private static final HiLogLabel LABEL_LOG = new HiLogLabel(3, 0xD001100, "Demo");
- private Player player = new Player(this);
- @Override
- public void onStart(Intent intent) {
- HiLog.error(LABEL_LOG, "ServiceAbility::onStart");
- super.onStart(intent);
- EventRunner eventRunner =EventRunner.create(true);
- EventHandler eventHandler = new EventHandler(eventRunner);
- eventHandler.postSyncTask(new Runnable() {
- @Override
- public void run() {
- try{
- RawFileDescriptor rawFileDescriptor = getResourceManager()
- .getRawFileEntry("resources/rawfile/text.mp3")
- .openRawFileDescriptor();
- Source source = new Source(rawFileDescriptor.getFileDescriptor(),
- rawFileDescriptor.getStartPosition(),
- rawFileDescriptor.getFileSize());
- player.setSource(source);
- player.prepare();
- player.play();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- });
- }
- @Override
- public void onBackground() {
- super.onBackground();
- HiLog.info(LABEL_LOG, "ServiceAbility::onBackground");
- }
- @Override
- public void onStop() {
- super.onStop();
- HiLog.info(LABEL_LOG, "ServiceAbility::onStop");
- if(player.isNowPlaying()){
- player.stop();
- player.release();
- }
- }
- @Override
- public void onCommand(Intent intent, boolean restart, int startId) {
- }
- @Override
- public IRemoteObject onConnect(Intent intent) {
- return null;
- }
- @Override
- public void onDisconnect(Intent intent) {
- }
- }
复制代码 2. 显示实时位置在MainAbilitySlice.java中编写以下代码。
通过查询分布式数据库中的key值得到孩子端设备实时位置信息,并写到文本中。
- private void getSingleLocation(){
- Text text_Longitude = (Text) findComponentById(ResourceTable.Id_Longitude);
- Text text_Latitude = (Text) findComponentById(ResourceTable.Id_Latitude);
- Text text_PlaceName = (Text) findComponentById(ResourceTable.Id_PlaceName);
- Text text_CountryName = (Text) findComponentById(ResourceTable.Id_CountryName);
- Longitude = queryContact("Longitude");
- Latitude = queryContact("Latitude");
- PlaceName = queryContact("PlaceName");
- CountryName = queryContact("CountryName");
- text_Longitude.setText("经度:" + Longitude);
- text_Latitude.setText("纬度:" + Latitude);
- text_PlaceName.setText("位置:" + PlaceName);
- text_CountryName.setText("国家:" + CountryName);
- }
复制代码 3. 添加活动范围监听事件从四个文本输入框中获取设定的位置范围,并与实时位置判断,如果超出设定的位置范围则通过startAbility方法播放音频,否则通过stopAbility方法停止播放音频。
写在最后更多资料请关注我们的项目 : Awesome-Harmony_木棉花
本项目会长期更新 ,希望随着鸿蒙一同成长变强的既有我们,也有正在看着这个项目的你。明年3月,深大校园内的木棉花会盛开,那时,鸿蒙也会变的更好,愿这花开,有你我的一份。