鸿蒙首款地图服务卡片,我心动了!

林家大哥 2021-07-26 14:22:03 4210

处于隐私保护借用熊猫基地定位,代码层实现了获取实时定位功能。

代码已开源至 gitee:
https://gitee.com/panda-coder/harmonyos-apps/tree/master/AMapCard

B 站效果视频链接:
https://www.bilibili.com/video/BV1Rq4y1X7BC?share_source=copy_web

卡片效果如下:



关键技术及实现原理

卡片现有支持的基础组件有:button、calendar、chart、clock、divider、image、input、progress、span、text。

可以看到现有的卡片组件并不支持地图的开发,那么如何在卡片上显示地图尼?

通过 image 组件+高德地图 WebAPI 的静态地图即可实现地图的显示。

以上方便有开发卡片经验的开发者提供思路,具体方式方法如下:

从零开始

①创建项目

打开 DevEco Studio 工具,点击 File->New->New Project 创建一个 Empty Ability(JS),如下图,SDK 选用了 API 5。



然后进入 FormControllerManager 找到 createFormController、getController、newInstance 进行修改。

①createFormController

在 newInstance 方法中添加参数 formId,如下图:

②getController

在 newInstance 方法中添加参数 formId,如下图:

③newInstace

该方法是动态的创建 WidgetImpl 方法,类似于 IOC 作用:

找到 java 目录下的 widget/widget/widgetImpl,卡片的所有逻辑都在该文件内。

首先修改构造函数及定义基础属性等,因上述修改了 FormController 及 FormControllerManager 构造函数必须增加 Long formId 参数。

private static Location slocation=null;//当前位置信息
private Boolean slocationChanged=false;//位置是否修改
private  int dimension=2;//当前卡片模式  2x2=2;2x4=3;4x4=4;
private List<String> defualtBtn=new ArrayList<>();//界面下方的按钮列表
private static Locator locator=null;//坐标获取类
private LocatorCallBack locatorCallBack=new LocatorCallBack();//坐标获取后返回调用类
private int mRoom=16;//静态地图显示层级
private String markType="";//静态地图周边搜索关键字
private String mSize="500*500";//静态地图大小
private List<String> mKeyLocation=new ArrayList<>();//静态地图获取周边标记的坐标
RequestParam requestParam = new RequestParam(RequestParam.PRIORITY_ACCURACY, 20, 0);

public WidgetImpl(Context context, String formName, Integer dimension,Long formId) {
    super(context, formName, dimension,formId);
    this.dimension=dimension;
    //获取当前定位
    if(locator==null){
        locator=new Locator(context);
        locator.startLocating(requestParam,locatorCallBack);
    }
    switch (dimension){
        case 2:{
            mSize="300*300";
            mRoom=13;
            break;
        }
        case 3:{
          mSize="500*250";
          mRoom=13;
          break;
        }
        case 4:{
            mSize="500*500";
            mRoom=15;
            break;
        }
    }
}

public class LocatorCallBack implements LocatorCallback{

    @Override
    public void onLocationReport(Location location) {
        slocation=location;
        //周边信息接口额度有限,限制为当坐标改变时刷新坐标mark信息,并更新卡片
        if(location==slocation || slocation==null)
            return;
        refreshMark();
        updateFormData(formId);
    }

    @Override
    public void onStatusChanged(int i) {

    }

    @Override
    public void onErrorReport(int i) {

    }
}

修改 createFormController,该方法在卡片创建时调用,我们需要把页面需要的参数传递过去。

注意网络图片需要使用“通过内存图片方式使用 image 组件。

@Override
public ProviderFormInfo bindFormData(){
defualtBtn=new ArrayList<>();
defualtBtn.add("酒店");
defualtBtn.add("餐饮");
defualtBtn.add("景点");
defualtBtn.add("加油站");
if(defualtBtn.size()<5){
for(int i=defualtBtn.size();i<5;i++){
defualtBtn.add("未设置");
}
}
this.markType=defualtBtn.get(0);
this.refreshMark();
FormBindingData formBindingData=null;
ZSONObject zsonObject =new ZSONObject();
zsonObject.put("imgSrc","memory://amap.png");
zsonObject.put("showCtlButton",this.dimension!=2);
zsonObject.put("searchBtns",defualtBtn);
zsonObject.put("searchText",markType);
formBindingData=new FormBindingData(zsonObject);
ProviderFormInfo formInfo = new ProviderFormInfo();
formInfo.setJsBindingData(formBindingData);
String amapUrl=getMapImageUrl(mKeyLocation);
byte[] bytes= HttpImageUtils.doGetRequestForFile(amapUrl);
formBindingData.addImageData("amap.png",bytes);
return formInfo;
}

初始化卡片后改进 onTriggerFormEvent,该方法为接收卡片事件,message 为事件传递的 params 参数。

@Override
public void onTriggerFormEvent(long formId, String message) {
    ZSONObject request=ZSONObject.stringToZSON(message);
    String EventName=request.getString("name");
    switch (EventName){
        case "checkSearch":{
            int index=request.getIntValue("index");
            markType=defualtBtn.get(index);
            this.refreshMark();
            break;
        }
        case "mapAdd":{
            if(mRoom<17){
                mRoom+=1;
            }
            break;
        }
        case "mapReduce":{
            if(mRoom>0){
                mRoom-=1;
            }
            break;
        }
    }
    updateFormData(formId);
}

修改更新卡片信息的方法,此方法不仅是系统会定时刷新,也有主动刷新的调用如:卡片事件改变后调用,坐标改变后的调用。

这也是需要修改 FormController、FormControllerManager 增加 formId 属性的原因,因为在主动刷新时需要 formId 参数。

此处还有一个重点就是:
((Ability)context).updateForm(formId,bindingData);

@Override
public void updateFormData(long formId, Object... vars) {
    ZSONObject zsonObject=new ZSONObject();
    zsonObject.put("searchBtns",defualtBtn);
    zsonObject.put("searchText",markType);
    String mapName="amap"+System.currentTimeMillis()+".png";
    zsonObject.put("imgSrc","memory://"+mapName);
    FormBindingData bindingData = new FormBindingData(zsonObject);
    String amapUrl=getMapImageUrl(mKeyLocation);
    byte[] bytes= HttpImageUtils.doGetRequestForFile(amapUrl);
    bindingData.addImageData(mapName,bytes);
    try{
        ((Ability)context).updateForm(formId,bindingData);
    }catch (Exception ex){
        ex.printStackTrace();
    }

}

其他一些上述方法中调用的私有方法及类。

私有方法:

private void refreshMark(){
    try{
        this.mKeyLocation= HttpImageUtils.SearchByKeyUrl(getMapMarkUrl(10));
    }catch (Exception ex){
        ex.printStackTrace();
    }
}

private String getMapImageUrl(List<String> Position){
    String url="https://restapi.amap.com/v3/staticmap";
    String params="key=";
    params+="&zoom="+mRoom;
    params+="&size="+mSize;
    if(slocation!=null)
        params+="&location="+slocation.getLongitude()+","+slocation.getLatitude();
    params+="&markers=large,0xea7700,H:"+slocation.getLongitude()+","+slocation.getLatitude();
    if(Position==null || Position.size()==0)
        return  url+"?"+params;
    String markers="|mid,0xFF0000,:";

    for(int i=0;i<Position.size();i++){
        markers+=Position.get(i)+";";
    }
    params+=markers.substring(0,markers.length()-1);
    return url+"?"+params;
}

private  String getMapMarkUrl(int size){
    String Url="https://restapi.amap.com/v5/place/around?key=";
    Url+="&keywords="+(markType=="未设置"?"":markType);
    if(slocation!=null)
        Url+="&location="+slocation.getLongitude()+","+slocation.getLatitude();
    Url+="&size="+size;
    return Url;
}

HttpImageUtils 类:

package com.panda_coder.amapcard.utils;

import com.panda_coder.amapcard.MainAbility;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ohos.utils.zson.ZSONArray;
import ohos.utils.zson.ZSONObject;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

public class HttpImageUtils {
private static final HiLogLabel TAG = new HiLogLabel(HiLog.DEBUG, 0x0, MainAbility.class.getName());

public final static byte[] doGetRequestForFile(String urlStr) {
    InputStream is = null;
    HttpURLConnection conn = null;
    byte[] buff = new byte[1024];
    try {
        URL url = new URL(urlStr);
        conn = (HttpURLConnection) url.openConnection();

        conn.setDoInput(true);
        conn.setRequestMethod("GET");
        conn.setReadTimeout(6000);
        conn.connect();
        is = conn.getInputStream();
        if (conn.getResponseCode() == 200) {
            buff = readInputStream(is);
        } else{
            buff=null;
        }
    } catch (Exception e) {
        HiLog.error(TAG,"【获取图片异常】",e);
    }
    finally {
        try {
            if(is != null){
                is.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        conn.disconnect();
    }

    return buff;
}

public static byte[] readInputStream(InputStream is) {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    byte[] buffer = new byte[1024];
    int length = -1;
    try {
        while ((length = is.read(buffer)) != -1) {
            baos.write(buffer, 0, length);
        }
        baos.flush();
    } catch (IOException e) {
        e.printStackTrace();
    }
    byte[] data = baos.toByteArray();
    try {
        is.close();
        baos.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
    return data;
}

public static String httpGet(String urlStr){
    InputStream is = null;
    HttpURLConnection conn = null;
    String response="";
    StringBuffer buffer = new StringBuffer();
    try {
        URL url = new URL(urlStr);
        conn = (HttpURLConnection) url.openConnection();

        conn.setDoInput(true);
        conn.setRequestMethod("GET");
        conn.setReadTimeout(6000);
        conn.connect();
        is = conn.getInputStream();
        if (conn.getResponseCode() == 200) {
            String str=null;
            InputStreamReader isr = new InputStreamReader(is,"utf-8");
            BufferedReader br = new BufferedReader(isr);
            while((response = br.readLine())!=null){
                buffer.append(response);
            }
        }
        response=buffer.toString();

    } catch (Exception e) {
        HiLog.error(TAG,"【访问异常】",e);
    }
    finally {
        try {
            if(is != null){
                is.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        conn.disconnect();
    }
    return response;
}

public final  static List<String> SearchByKeyUrl(String urlStr){
    List<String> result=new ArrayList<>();
    String response= httpGet(urlStr);
    if(response==null || response=="")
        return result;
    ZSONObject zson=ZSONObject.stringToZSON(response);
    if(zson.getIntValue("infocode")!=10000)
        return result;
    ZSONArray zsonArray=zson.getZSONArray("pois");
    for(int i=0;i<zsonArray.size();i++){
        ZSONObject child= (ZSONObject)zsonArray.get(i);
        String location=child.getString("location");
        result.add(location);
    }
    return result;
}

}

至此一个地图周边的卡片即可开发完成,后续会增加卡片的编辑功能可关注 gitee。

来源:鸿蒙技术社区

声明:本文内容由易百纳平台入驻作者撰写,文章观点仅代表作者本人,不代表易百纳立场。如有内容侵权或者其他问题,请联系本站进行删除。
红包 92 收藏 评论 打赏
评论
0个
内容存在敏感词
手气红包
    易百纳技术社区暂无数据
相关专栏
置顶时间设置
结束时间
删除原因
  • 广告/SPAM
  • 恶意灌水
  • 违规内容
  • 文不对题
  • 重复发帖
打赏作者
易百纳技术社区
林家大哥
您的支持将鼓励我继续创作!
打赏金额:
¥1易百纳技术社区
¥5易百纳技术社区
¥10易百纳技术社区
¥50易百纳技术社区
¥100易百纳技术社区
支付方式:
微信支付
支付宝支付
易百纳技术社区微信支付
易百纳技术社区
打赏成功!

感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~

举报反馈

举报类型

  • 内容涉黄/赌/毒
  • 内容侵权/抄袭
  • 政治相关
  • 涉嫌广告
  • 侮辱谩骂
  • 其他

详细说明

审核成功

发布时间设置
发布时间:
是否关联周任务-专栏模块

审核失败

失败原因
备注
拼手气红包 红包规则
祝福语
恭喜发财,大吉大利!
红包金额
红包最小金额不能低于5元
红包数量
红包数量范围10~50个
余额支付
当前余额:
可前往问答、专栏板块获取收益 去获取
取 消 确 定

小包子的红包

恭喜发财,大吉大利

已领取20/40,共1.6元 红包规则

    易百纳技术社区