1. 程式人生 > >自定義ImageView實現地圖(一)

自定義ImageView實現地圖(一)

  現在一提到地圖很多人就會想到百度地圖高德地圖等SDK,但是公司遇到個專案要做到室內導航,現在的第三方用GPS定位又不準,所以使用了一個自定義的ImageView去實現地圖的功能。該地圖有導航、定位、分級顯示、點選檢視位置詳細資訊的功能。今天我們先說下基礎地圖資料的顯示及分級顯示。

  先說下思路,一、地圖的底圖先要畫好(交給UI畫吧),畫出各個區域,如下圖(忽略那些文字吧,地圖原稿找不到)沒有那些文字的,只有那些有顏色的小格;

  二、伺服器先將每個需要畫名字或者區域的的座標標出來存起來,然後下發給APP;

  三、APP載入完地圖地圖後(記住一定要按比例來)按照服務端拿下來的資料把名字畫上去。

  四、根據手指放大縮小的縮放比例分成三個級別,實現分級別顯示,就像百度地圖那樣放大了才能顯示更詳細的資訊。

沒有縮放時顯示的內容:


放大一個級別之後顯示的內容:

放大到最大級別顯示的內容:

  接下來上程式碼

  伺服器地圖區域資料的模型

public class MapInfo implements Serializable {

	/**
	 * 地圖資料模型
	 */
	private static final long serialVersionUID = 4667341137172521648L;

	private String map_id;     //地圖ID
	private String map_WH;     //地圖的寬高
	private String map_route;  //定位線段
	private String map_img;    //地圖底圖的地址
	private String show_exit;  //出入口
	private ArrayList<MapDetail> map_details;   //地圖區域資料

	@Override
	public String toString() {
		return "MapInfo [map_id=" + map_id + ", map_WH=" + map_WH
				+ ", map_route=" + map_route + ", map_img=" + map_img
				+ ", map_details=" + map_details + "]";
	}

	
	public String getShow_exit() {
		return show_exit;
	}


	public void setShow_exit(String show_exit) {
		this.show_exit = show_exit;
	}


	public static long getSerialversionuid() {
		return serialVersionUID;
	}


	public String getMap_id() {
		return map_id;
	}

	public void setMap_id(String map_id) {
		this.map_id = map_id;
	}

	public String getMap_WH() {
		return map_WH;
	}

	public void setMap_WH(String map_WH) {
		this.map_WH = map_WH;
	}

	public String getMap_route() {
		return map_route;
	}

	public void setMap_route(String map_route) {
		this.map_route = map_route;
	}

	public String getMap_img() {
		return map_img;
	}

	public void setMap_img(String map_img) {
		this.map_img = map_img;
	}

	public ArrayList<MapDetail> getMap_details() {
		return map_details;
	}

	public void setMap_details(ArrayList<MapDetail> map_details) {
		this.map_details = map_details;
	}

	public class MapDetail implements Serializable {
		/**
		 * 
		 */
		private static final long serialVersionUID = -7211436420618576335L;
		private String D_id;             //區域ID
		private String D_location;       //區域座標
		private String D_status;         //區域的狀態
		private String map_id;           //地圖的ID
		private String point_type;       //座標的型別
		private String point_xy;         //定位的座標
 		private String show_id;          //展會的ID
		private String user_id;          //使用者ID
		private String user_nicename;    //使用者名稱
		private String gradeid;          //等級

		public String getGradeid() {
			return gradeid;
		}

		public void setGradeid(String gradeid) {
			this.gradeid = gradeid;
		}

		public String getD_id() {
			return D_id;
		}

		public void setD_id(String d_id) {
			D_id = d_id;
		}

		public String getD_location() {
			return D_location;
		}

		public void setD_location(String d_location) {
			D_location = d_location;
		}

		public String getD_status() {
			return D_status;
		}

		public void setD_status(String d_status) {
			D_status = d_status;
		}

		public String getMap_id() {
			return map_id;
		}

		public void setMap_id(String map_id) {
			this.map_id = map_id;
		}

		public String getPoint_type() {
			return point_type;
		}

		public void setPoint_type(String point_type) {
			this.point_type = point_type;
		}

		public String getPoint_xy() {
			return point_xy;
		}

		public void setPoint_xy(String point_xy) {
			this.point_xy = point_xy;
		}

		public String getShow_id() {
			return show_id;
		}

		public void setShow_id(String show_id) {
			this.show_id = show_id;
		}

		public String getUser_id() {
			return user_id;
		}

		public void setUser_id(String user_id) {
			this.user_id = user_id;
		}

		public String getUser_nicename() {
			return user_nicename;
		}

		public void setUser_nicename(String user_nicename) {
			this.user_nicename = user_nicename;
		}

		@Override
		public String toString() {
			return "MapDetail [D_id=" + D_id + ", D_location=" + D_location
					+ ", D_status=" + D_status + ", map_id=" + map_id
					+ ", point_type=" + point_type + ", point_xy=" + point_xy
					+ ", show_id=" + show_id + ", user_id=" + user_id
					+ ", user_nicename=" + user_nicename + ",gradeid="+gradeid+"]";
		}

	}
}

  地圖載入完成後開始畫區域名(我使用的是ImageLoader剛好有監聽方法)

//載入地圖
	private void loadMapImage() {
		ImageLoader.getInstance().displayImage(
				ApiClient.BASE_IMAGE + mapInfo.getMap_img(), mIndoorMapView,
				appContext.getOptions(), new ImageLoadingListener() {
					@Override
					public void onLoadingStarted(String arg0, View arg1) {

					}

					@Override
					public void onLoadingFailed(String arg0, View arg1,
							FailReason arg2) {

					}

					@Override
					public void onLoadingComplete(String arg0, View arg1,
							Bitmap arg2) {
						mIndoorMapView.setMapInfo(mapInfo);
						
						//System.out.println(mapInfo);
						int oriImageWidth = 0;
						int oriImageHeight = 0;
						try {
							oriImageWidth = Integer.parseInt(mapInfo
									.getMap_WH().split(",")[0]);
							oriImageHeight = Integer.parseInt(mapInfo
									.getMap_WH().split(",")[1]);
						} catch (NumberFormatException e) {
							e.printStackTrace();
						}
						int width = arg2.getWidth();
						int height = arg2.getHeight();
						Log.d(TAG, "" + width + "---" + height);
						Bitmap tempBitmap = arg2;
						if (width < (oriImageWidth / 2)) {
							tempBitmap = BitmapManager.zoomBitmap(arg2,
									oriImageWidth / 2, oriImageHeight / 2);
						}
						mIndoorMapView.setImageBitmap(tempBitmap);
						if(mIndoorMapView.getFirstbgbitmap()==null){
							mIndoorMapView.setFirstbgbitmap(((BitmapDrawable) mIndoorMapView.getDrawable()).getBitmap());
						}
						// The MAGIC happens here!
						mAttacher = new PhotoViewAttacher(mIndoorMapView);
						//儲存最開始的圖片比例
						if(firstZoom>=8){
							RectF rect=mAttacher.getDisplayRect();
							firstZoom=(float) Math.sqrt(((rect.height()*rect.height())+(rect.width()*rect.width())));
						}

					
						mIndoorMapView.drawMerchant();
						if(StringUtils.isEmpty(startPoint)){
							mIndoorMapView.setStartPointLoc(StringUtils.pointFromString(startPoint));
							startPoint=null;
						}
						mIndoorMapView.drawPointAndNavLine();
						//監聽地圖的點選 點選展位區域,跳轉到展會的詳情頁面
                                                //mAttacher.setOnPhotoTapListener(mapOnPhotoTapListener);			
						setZoomChang();
					 
					}

					@Override
					public void onLoadingCancelled(String arg0, View arg1) {

					}
				});
	}

  載入完成後畫出區域名字的方法

	public synchronized void drawMerchant() {
		/**
		 * 載入地圖後的初始化工作
		 */
		endBitmap = BitmapFactory.decodeResource(getResources(),
				R.drawable.indoormap_end);
		startBitmap = BitmapFactory.decodeResource(getResources(),
				R.drawable.indoormap_start);
		astar = new AStar();
		backBitmap = createMerchantInfoMapBitmap();
	}

	//畫展位名稱
	public Bitmap createMerchantInfoMapBitmap() {
		Bitmap myBitmap =firstbgbitmap;
		Bitmap bmpTemp = Bitmap.createBitmap(myBitmap.getWidth(),
				myBitmap.getHeight(), Bitmap.Config.RGB_565);
		Canvas canvas = new Canvas(bmpTemp);
		Paint p = new Paint();
		p.setColor(Color.rgb(105, 126, 151));
		p.setTextAlign(Paint.Align.CENTER);
		p.setAntiAlias(true);
		// 讓畫出的圖形是空心的
		p.setStyle(Paint.Style.FILL);
		// 設定畫出的線的 粗細程度
		p.setStrokeWidth(3);
		String familyName = "宋體";
		Typeface font = Typeface.create(familyName, Typeface.BOLD);
		p.setTypeface(font);
		canvas.drawBitmap(myBitmap, 0, 0, p);
		if (mapInfo != null && mapInfo.getMap_details() != null) {
		
			for (int i = 0; i < mapInfo.getMap_details().size(); i++) {
				MapInfo.MapDetail mapDetail = mapInfo.getMap_details().get(i);
				if (StringUtils.isEmpty(mapDetail.getPoint_xy())) {
					continue;
				}
				String[] xyArray = mapDetail.getPoint_xy().split(",");
				if (xyArray.length == 8) {
				// 設定字型大小
					if ((Integer.parseInt(xyArray[2]) / 2 - Integer
							.parseInt(xyArray[0]) / 2) > 40) {
						p.setTextSize(20);
					} else {
						p.setTextSize(14);
					}

					float x = Integer.parseInt(xyArray[0])
							/ 2
							+ (Integer.parseInt(xyArray[2]) / 2 - Integer
									.parseInt(xyArray[0]) / 2) / 2;
					float y = Integer.parseInt(xyArray[1])
							/ 2
							+ (Integer.parseInt(xyArray[5]) / 2 - Integer
									.parseInt(xyArray[1]) / 2) / 2;
					x += 6;
					y += 12;
					if (((Integer.parseInt(xyArray[2]) / 2 - Integer
							.parseInt(xyArray[0]) / 2)) < ((Integer
							.parseInt(xyArray[5]) / 2 - Integer
							.parseInt(xyArray[1]) / 2))
							&& !StringUtils.isEmpty(mapDetail
									.getUser_nicename())
							&& mapDetail.getUser_nicename().length() > 3) {
						//根據顯示等級顯示展位名稱
						//System.out.println("當前顯示等級"+AppConfig.getAppConfig(context).getShowName_Type());
						if(Integer.parseInt(mapDetail.getGradeid())>=AppConfig.getAppConfig(context).getShowName_Type()){
							canvas.rotate(-90, x, y);
							canvas.drawText(mapDetail.getUser_nicename(), x, y, p);
							canvas.rotate(90, x, y);
						}
							
						
					} else {
						//根據顯示等級顯示展位名稱
						if(Integer.parseInt(mapDetail.getGradeid())>=AppConfig.getAppConfig(context).getShowName_Type()){
							canvas.rotate(0, x, y);
							if (!StringUtils.isEmpty(mapDetail.getUser_nicename())) {
								canvas.drawText(mapDetail.getUser_nicename(), x, y,
										p);
							}
						}
						
						
					}
				}

			}
		}
		canvas.save(Canvas.ALL_SAVE_FLAG);
		canvas.restore();
		//如果有導航就重畫
		if(startPointLoc!=null&&endPoint!=null){
			backBitmap=bmpTemp;
			this.setImageDrawable(new BitmapDrawable(getResources(), bmpTemp));
			return drawPointAndNavLine();
		}else{
			this.setImageDrawable(new BitmapDrawable(getResources(), bmpTemp));
			return bmpTemp;
		}
		
	}

  以上我那麼就實現了地圖基礎資料的顯示以及根據不同的縮放比例顯示不同的區域名,差不多相當於最原始的百度地圖,思路是不是超級簡單呢?

  原諒我無法提供原始碼下載,因為涉及的東西有點多,至於導航、定位功能我會在下篇部落格講。

相關推薦

定義ImageView實現地圖

  現在一提到地圖很多人就會想到百度地圖高德地圖等SDK,但是公司遇到個專案要做到室內導航,現在的第三方用GPS定位又不準,所以使用了一個自定義的ImageView去實現地圖的功能。該地圖有導航、定位、分級顯示、點選檢視位置詳細資訊的功能。今天我們先說下基礎地圖資料的顯示及

android定義view之地圖

最近參加了一個比賽,要用到自己做一個自定義的小地圖,所以在網上查找了一些關於自定義view的有關資料,也瞭解了自定義控制元件的初步知識。 效果圖 第一階段我畫了一個自制的網格圖,點哪個網格就會哪個網格就會顯示。 工作環境圖 程式碼介紹

8、jeecg 筆記之 定義word 模板匯出

1、前言 jeecg 中已經自帶 word 的匯出匯出功能,其所使用的也是 easypoi,儘管所匯出的 word 能滿足大部分需求, 但總是有需要用到自定義 word匯出模板,下文所用到的皆是 easypoi 提供的,為方便下次翻閱,故記之。   2、程式碼部分 2.1、controll

定義圓形進度條

由於專案需要,需要自定義一個圓形進度條,效果如下: 1 建立什麼檔案? 具體的程式碼我會放在github上,所以暫時忽略attr檔案和activity_main2檔案 2 CustomCircleProgressBar packa

Android 定義View--ProgressBar篇

1、概述 1.1 目的 : 在我們的日常開發中,有很多Android UI介面上有一些特殊或者特別的控制元件與介面,是Android自帶的控制元件所不能滿足的,需要我們自己定製一些適合的控制元件來完成。 1.2 Android自定義View步驟 : 自定

View的體系和定義View的流程

前言: 最近學習了View的體系與自定義View,並且看了android進階之光這部書,記錄一下學習心得 一、View與ViewGroup 其實,平時我們開發用的各種控制元件(TextView,Button)和佈局(LinearLayout,Rela

Android定義視訊播放器

一、引言 我們在開發Android多媒體應用時,有兩種方式來播放多媒體資源。第一種是使用隱式的Intent,來使用系統或者手機已經安裝的第三方播放器應用來播放音視訊,第二種是使用Android自帶的、我們自定義的播放器來播放,這種主要是採用Android提供的

網路測速全解析之一:定義View基礎知識

效果如圖: 一、基礎知識: 基礎知識,也就是最基本的自定義view需要掌握的知識,我這裡主要是學習了GcsSloop大佬的view自定義系列部落格(這裡是部落格地址) 1.自定義view繪製流程呼叫鏈 2.android自定義座標系(向右向下為正)

定義View進階--手繪地圖

一:最近學習了自定義view,剛好就接到了相關的需求,於是就上手做了,下面描述一下需求       需求:簡單的來說就是做一個地圖,不同的是,為了追求美觀,於是地圖是一張由UI出的圖片,poi點       為運營採集點,實現地圖的縮放,移動,poi打點,以及其他的東西,由於

百度地圖--獲取定義區域JSON資料

基於百度地圖的自定義區域繪圖–呼叫地圖 註冊百度地圖的SDK: 這一步是呼叫百度地圖的核心,通過獲取SDK才能進行相關操作。 使用百度地圖: 在html中引入js檔案: <script src="http://api.map.baidu.com/a

百度地圖--獲取定義區域JSON資料

基於百度地圖的自定義區域繪圖–繪製區域 基於上一篇的尊卑工作後,開始正式繪圖: 第一步:建立react專案(我的目錄結構如圖)                                                   

goland匯入定義包時出錯招解決問題

使用goland編寫go語言程式的時候,發現針對自定義的包無法引入。如下圖所示: 具體錯誤也就是main.go:13:2: cannot find package "grpool" in any of: 然後就各種在網上查詢,大多數也都講得很不清楚,最後在網上找到一個說的比較詳細的

android 定義ListView實現下拉重新整理、分頁載入、點選事件——定義控制元件學習

package com.example.administrator.customerpulldownrefreshandpageload; import android.content.Context; import android.os.Handler; import android.os.Message

【朝花夕拾】Android定義View篇之定義View的三種實現方式及定義屬性詳解

前言        儘管Android系統提供了不少控制元件,但是有很多酷炫效果仍然是系統原生控制元件無法實現的。好在Android允許自定義控制元件,來彌補原生控制元件的不足。但是在很多初學者看來,自定義View似乎很難掌握。其中有很大一部分原因是我們平時看到的自定

xgboost 定義評價函數metric與目標函數

binary ret and 參數 cnblogs from valid ges zed 比賽得分公式如下: 其中,P為Precision , R為 Recall。 GBDT訓練基於驗證集評價,此時會調用評價函數,XGBoost的best_iteration和

XAF 框架中,定義參數動作Action,輸入參數的控件可定義,用於選擇組織及項目

示例 app frame tro href express documents 定義 ron XAF 框架中,如何生成一個自定義參數動作(Action),輸入參數的控件可定義? 參考文檔:https://documentation.devexpress.com/eXpres

AngularJs定義指令詳解5 - link

演示 hang cursor off drag font 雙向 事件 date 在指令中操作DOM,我們需要link參數,這參數要求聲明一個函數,稱之為鏈接函數。 寫法: link: function(scope, element, attrs) {  // 在這裏操作DO

sench touch 定義小圖標

found conf custom cmd svg logs 頁面 一個 會有 自定義圖標的方法 Sencha touch自帶圖標有限,有時需要自己添加圖標。下面介紹自定義圖標的方法: 首先需要生成圖標字體。有許多網站提供在線生成圖標字體的功能,比如IcoMoon,通過這個

vue2 定義 折疊列表Accordion組件

rep link 分享圖片 toggle sset pac baseline object 列表 1.自定義 折疊列表 Accordion.vue <!-- 折疊列表 組件 --> <template> <nav :class="$st

ASP.NET MVC 學習筆記-7.定義配置信息後續

字符串 return abstract 新的 work 生成 value DC 連接字符串加密 自定義配置信息的高級應用 通過上篇博文對簡單的自定義配置信息的學習,使得更加靈活的控制系統配置信息。實際項目中,這種配置的靈活度往往無法滿足項目的靈活度和擴展性。 比如,一個