卡片服務(wù)開(kāi)發(fā)之如何開(kāi)發(fā)一個(gè)地圖服務(wù)卡片
想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
前言
處于隱私保護(hù)借用熊貓基地定位,代碼層實(shí)現(xiàn)了獲取實(shí)時(shí)定位功能。
效果圖
卡片效果2x2+2x44x4縮放
關(guān)鍵技術(shù)及實(shí)現(xiàn)原理
卡片現(xiàn)有支持的基礎(chǔ)組件有:button、calendar、chart、clock、divider、image、input、progress、span、text
可以看到現(xiàn)有的卡片組件并不支持地圖的開(kāi)發(fā),那么如何在卡片上顯示地圖尼?
通過(guò)image組件+高德地圖WebAPI的靜態(tài)地圖即可實(shí)現(xiàn)地圖的顯示。
-以上方便有開(kāi)發(fā)卡片經(jīng)驗(yàn)的開(kāi)發(fā)者提供思路,具體方式方法如下
從零開(kāi)始
創(chuàng)建項(xiàng)目
打開(kāi)DevEco Studio工具,點(diǎn)擊File->New->New Project創(chuàng)建一個(gè)Empty Ability(JS)如下圖:
SDK選用API 5
創(chuàng)建后的結(jié)構(gòu):

首先修改程序的配置文件:
打開(kāi)config.json,修改卡片支持類型情況:

添加權(quán)限:

配置完成還需要在MainAbility中顯示的申明使用權(quán)限信息,詳情參考文檔配置相關(guān)內(nèi)容:
打開(kāi)MainAbility添加方法:
- //獲取權(quán)限
- 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方法中調(diào)用requestPermission();方法。
修改界面:
打開(kāi)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" >當(dāng)前檢索項(xiàng):</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>
需要注意:卡片的事件不能使用表達(dá)式,不能使用for語(yǔ)句循環(huán)構(gòu)建
樣式調(diào)整文件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",//默認(rèn)圖片
- "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"
- }
- }
- }
- }
后臺(tái)邏輯
由于更新卡片時(shí)需要提供formId,我們對(duì)FormController及FormControllerManager這兩個(gè)幫助類進(jìn)行一個(gè)修改。
打開(kāi)java目錄下的FormController文件并添加受保護(hù)的屬性 formId,并修改構(gòu)造函數(shù)。
然后進(jìn)入FormControllerManager找到createFormController、getController、newInstance進(jìn)行修改。
createFormController:
在newInstance方法中添加參數(shù)formId,如下圖:
getController:
在newInstance方法中添加參數(shù)formId,如下圖:
newInstace:
該方法是動(dòng)態(tài)的創(chuàng)建WidgetImpl方法,類似于IOC作用。
找到j(luò)ava目錄下的widget/widget/widgetImpl,卡片的所有邏輯都在該文件內(nèi)
首先修改構(gòu)造函數(shù)及定義基礎(chǔ)屬性等
因上述修改了FormController及FormControllerManager構(gòu)造函數(shù)必須增加Long formId參數(shù)
- private static Location slocation=null;//當(dāng)前位置信息
- private Boolean slocationChanged=false;//位置是否修改
- private int dimension=2;//當(dāng)前卡片模式 2x2=2;2x4=3;4x4=4;
- private List<String> defualtBtn=new ArrayList<>();//界面下方的按鈕列表
- private static Locator locator=null;//坐標(biāo)獲取類
- private LocatorCallBack locatorCallBack=new LocatorCallBack();//坐標(biāo)獲取后返回調(diào)用類
- private int mRoom=16;//靜態(tài)地圖顯示層級(jí)
- private String markType="";//靜態(tài)地圖周邊搜索關(guān)鍵字
- private String mSize="500*500";//靜態(tài)地圖大小
- private List<String> mKeyLocation=new ArrayList<>();//靜態(tài)地圖獲取周邊標(biāo)記的坐標(biāo)
- 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;
- //獲取當(dāng)前定位
- 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;
- //周邊信息接口額度有限,限制為當(dāng)坐標(biāo)改變時(shí)刷新坐標(biāo)mark信息,并更新卡片
- if(location==slocation || slocation==null)
- return;
- refreshMark();
- updateFormData(formId);
- }
- @Override
- public void onStatusChanged(int i) {
- }
- @Override
- public void onErrorReport(int i) {
- }
- }
修改createFormController,該方法在卡片創(chuàng)建時(shí)調(diào)用,我們需要把頁(yè)面需要的參數(shù)傳遞過(guò)去
注意網(wǎng)絡(luò)圖片需要使用“通過(guò)內(nèi)存圖片方式使用image組件”
- @Override
- public ProviderFormInfo bindFormData(){
- defualtBtn=new ArrayList<>();
- defualtBtn.add("酒店");
- defualtBtn.add("餐飲");
- defualtBtn.add("景點(diǎn)");
- defualtBtn.add("加油站");
- if(defualtBtn.size()<5){
- for(int i=defualtBtn.size();i<5;i++){
- defualtBtn.add("未設(shè)置");
- }
- }
- 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;
- }
初始化卡片后改進(jìn)onTriggerFormEvent
該方法為接收卡片事件,message為事件傳遞的params參數(shù)
- @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);
- }
修改更新卡片信息的方法(此方法不僅是系統(tǒng)會(huì)定時(shí)刷新,也有主動(dòng)刷新的調(diào)用如:卡片事件改變后調(diào)用,坐標(biāo)改變后的調(diào)用,這也是需要修改FormController、FormControllerManager增加formId屬性的原因,因?yàn)樵谥鲃?dòng)刷新時(shí)需要formId參數(shù))
此處還有一個(gè)重點(diǎn)就是 ((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();
- }
- }
其它一些上述方法中調(diào)用的私有方法及類
私有方法:
- 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=="未設(shè)置"?"":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,"【獲取圖片異?!?quot;,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,"【訪問(wèn)異?!?quot;,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;
- }
- }
想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)