想了解更多内容,卡片开发开请访问:
和华为官方合作共建的服务鸿蒙技术社区
https://harmonyos.51cto.com
处于隐私保护借用熊猫基地定位,代码层实现了获取实时定位功能。个地
卡片效果2x2+2x44x4缩放
卡片现有支持的图服基础组件有:button、calendar、卡片开发开chart、服务clock、个地divider、图服image、卡片开发开input、服务progress、个地span、图服text
可以看到现有的卡片开发开卡片组件并不支持地图的开发,那么如何在卡片上显示地图尼?服务
通过image组件+高德地图WebAPI的静态地图即可实现地图的显示。
-以上方便有开发卡片经验的个地开发者提供思路,具体方式方法如下
打开DevEco Studio工具,点击File->New->New Project创建一个Empty Ability(JS)如下图:
SDK选用API 5
创建后的结构:
首先修改程序的配置文件:
打开config.json,高防服务器修改卡片支持类型情况:
添加权限:
配置完成还需要在MainAbility中显示的申明使用权限信息,详情参考文档配置相关内容:
打开MainAbility添加方法:
//获取权限 private void requestPermission() { String[] permission = { "ohos.permission.LOCATION", "ohos.permission.LOCATION_IN_BACKGROUND", }; List<String> applyPermissions = new ArrayList<>(); for (String element : permission) { if (verifySelfPermission(element) != 0) { if (canRequestPermission(element)) { applyPermissions.add(element); } } } requestPermissionsFromUser(applyPermissions.toArray(new String[0]), 0); }并在onStart方法中调用requestPermission();方法。
打开widget下的pages/index/imdex.hml
<div class="container"> <div class="container-inner" > <div class="container-img"> <stack> <image src="{ { imgSrc}}" class="bg-img"></image> <div class="container-show-text" > <text class="show-text" >当前检索项:</text> <text class="show-text" style="color: coral;" >{ { searchText}}</text> </div> <div class="container-map-ctl"> <button class="map-ctl-btn" @click="mapAddEvent" type="circle">+</button> <button class="map-ctl-btn" @click="mapReduceEvent" type="circle">-</button> </div> <div show="{ { showCtlButton}}" class="container-ctl" > <button class="ctl-btn" @click="searchCheckedEvent0">{ { searchBtns[0]}}</button> <button class="ctl-btn" @click="searchCheckedEvent1">{ { searchBtns[1]}}</button> <button class="ctl-btn" @click="searchCheckedEvent2">{ { searchBtns[2]}}</button> <button class="ctl-btn" @click="searchCheckedEvent3">{ { searchBtns[3]}}</button> <button class="ctl-btn" @click="searchCheckedEvent4">{ { searchBtns[4]}}</button> </div> </stack> </div> </div> </div>需要注意:卡片的事件不能使用表达式,不能使用for语句循环构建
样式调整文件pages/index/imdex.css:
.container { flex-direction: column; justify-content: center; align-items: center; } .bg-img { flex-shrink: 0; height: 100%; object-fit: cover; } .container-ctl{ opacity: 0.9; width: 100%; height: 100%; justify-content: center; flex-direction: row; align-items: flex-end; bottom: 3px; } .ctl-btn{ padding: 3px 6px; margin:3px 6px; font-size: 12px; border-radius: 3px; background-color: #409eff; border: 1px solid #cbcbcb; box-shadow: 1px 1px 3px #a8a8a8; } .container-map-ctl{ opacity: 0.8; justify-content: flex-end; margin-right: 3px; } .map-ctl-btn{ background-color: #409eff; border: 1px solid #cbcbcb; box-shadow: 1px 1px 3px #a8a8a8; width: 24px; height: 24px; margin:3px; } .container-show-text{ padding: 9px; } .show-text{ font-size: 8px; font-weight: bolder; }json配置信息修改pages/index/index.json:
{ "data": { "showCtlButton": false,//是否显示button。由Java传值且在2x2的界面不显示 "imgSrc": "/common/ic_default_image@3x.png",//默认图片 "searchText": "", "searchBtns": []//配置的button按钮信息 }, "actions": { "searchCheckedEvent0": { "action": "message", "params": { "index": 0, "name": "checkSearch" } }, "searchCheckedEvent1": { "action": "message", "params": { "index": 1, "name": "checkSearch" } }, "searchCheckedEvent2": { "action": "message", "params": { "index": 2, "name": "checkSearch" } }, "searchCheckedEvent3": { "action": "message", "params": { "index": 3, "name": "checkSearch" } }, "searchCheckedEvent4": { "action": "message", "params": { "index": 4, "name": "checkSearch" } }, "mapAddEvent": { "action": "message", "params": { "name": "mapAdd" } }, "mapReduceEvent": { "action": "message", "params": { "name": "mapReduce" } } } }由于更新卡片时需要提供formId,我们对FormController及FormControllerManager这两个帮助类进行一个修改。
打开java目录下的FormController文件并添加受保护的属性 formId,并修改构造函数。
然后进入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; } }想了解更多内容,请访问:
和华为官方合作共建的鸿蒙技术社区
https://harmonyos.51cto.com