The commonly used map-based house search function involves adding custom markers for regions, business districts, properties, etc., on the map, combined with the filtering logic of the application. Here is a simple implementation of adding region/business district and property markers using HarmonyOS ArkUI.
1. Enable Map Service
Register the application on the Huawei Developer Website, then enable the Map Kit service in My Projects > My Apps > API Management.
2. Map-Related Configuration
Configure the client_id
in the module.json5
file of the entry module. The client_id
can be found in Project Settings > General > App. You may need to configure the public key fingerprint; refer to the developer website for details.
"module": {
"name": "xxxx",
"type": "entry",
"description": "xxxx",
"mainElement": "xxxx",
"deviceTypes": [
"phone",
"tablet"
],
"pages": "xxxx",
"abilities": [],
"metadata": [
{
"name": "client_id",
"value": "xxxxxx" // Configure with the obtained Client ID
}
]
}
3. Create a Map
Use the MapComponent
to create a map, which requires two parameters: mapOptions
and mapCallback
. Override the lifecycle methods onPageShow
and onPageHide
to display and hide the map. If the map does not display, refer to the official website for troubleshooting.
MapComponent(mapOptions: mapCommon.MapOptions, mapCallback: AsyncCallback<map.MapComponentController>)
// Map initialization parameters: set the map center coordinates and zoom level
this.mapOptions = {
position: {
target: {
latitude: 39.9,
longitude: 116.4
},
zoom: 10
}
};
// Map initialization callback
this.callback = async (err, mapController) => {
if (!err) {
// Get the map controller to operate the map
this.mapController = mapController;
this.mapEventManager = this.mapController.getEventManager();
const callback = () => {
console.info(this.TAG, `on-mapLoad`);
}
this.mapEventManager.on("mapLoad", callback);
}
};
// Triggered each time the page is displayed (including routing and app foreground scenarios), only valid for @Entry-decorated custom components
onPageShow(): void {
// Bring the map to the foreground
if (this.mapController) {
this.mapController.show();
}
}
// Triggered each time the page is hidden (including routing and app background scenarios), only valid for @Entry-decorated custom components
onPageHide(): void {
// Send the map to the background
if (this.mapController) {
this.mapController.hide();
}
}
build() {
Stack() {
// Initialize the map by calling MapComponent
MapComponent({ mapOptions: this.mapOptions, mapCallback: this.callback }).width('100%').height('100%');
}.height('100%')
}
4. Display My Location Icon on the Map
First, dynamically request location permissions. After obtaining the specific location coordinates, use the map operation class mapController
to call setMyLocation
to set the location coordinates and animateCamera
to move the map to the location (with a configurable zoom level) to display a location icon.
static LocationPermissions: Array<Permissions> =
['ohos.permission.LOCATION', 'ohos.permission.APPROXIMATELY_LOCATION'];
// Start location positioning
startLocation() {
// Request location permissions
AgentUtil.requestPermissions(AgentUtil.LocationPermissions, async () => {
// Location permission granted
// Enable the my-location layer (mapController is obtained as described in the "Display Map" section)
this.mapController?.setMyLocationEnabled(true);
// Enable the my-location button
this.mapController?.setMyLocationControlsEnabled(true);
// Get the user's location coordinates
const location = await geoLocationManager.getCurrentLocation();
// Convert coordinate system (if needed)
// const wgs84LatLng = AgentUtil.wgs84LatLng(location.latitude, location.longitude);
// location.latitude = wgs84LatLng.latitude;
// location.longitude = wgs84LatLng.longitude;
// Set the user's location
this.mapController?.setMyLocation(location);
setTimeout(() => {
// Move the map to the target location
this.mapController?.animateCamera(map.newLatLng(location, 15));
}, 100);
});
}
5. Listen for Map Movement Stop
After the map moves to the location coordinates, use mapController.on("cameraIdle", () => {})
to listen for when the map stops moving. In the callback, implement the logic to display markers based on the map zoom level (e.g., display regions/districts at lower zooms and properties at higher zooms), and further refine business logic such as business district layers.
this.mapEventManager.on("cameraIdle", () => {
// The map has stopped moving; perform operations
const position = this.mapController?.getCameraPosition();
const currentZoom = position?.zoom ?? 0;
if (currentZoom < 14) {
// Display region/business district markers
this.addAreaMarkers();
} else {
// Display property markers
this.addHouseMarkers();
}
});
6. Customize Markers
Custom Circular Marker
Used to display regions and business districts. Since Map Kit does not support custom styles for markers, only icon settings, use a @Builder
custom component to draw a circular marker:
@Builder
BuilderMapCircularLabel(text: string, id: string) {
Stack() {
// Draw a circular background
Circle({ width: 70, height: 70 })
.shadow({ radius: 8 })
.fill($r("app.color.main_color"));
Text(text)
.fontSize(12)
.fontColor($r("app.color.white"))
.textAlign(TextAlign.Center)
.padding({ left: 4, right: 4 });
}.id(id);
}
Custom Property Display Marker
Draw a small triangle at the bottom using a Path
path.
/**
* Rectangular marker with a small triangle indicator
* @param text
*/
@Builder
BuilderMapRectLabel(text: string, id: string) {
Column() {
Text(text)
.fontSize(14)
.fontColor($r("app.color.white"))
.backgroundColor($r("app.color.main_color"))
.shadow({ radius: 5, color: $r("app.color.white") })
.borderRadius(14)
.padding({ left: 15, right: 15, top: 4, bottom: 4 });
// Draw a small triangle using path commands
Path()
.width(12)
.height(6)
.commands('M0 0 L30 0 L15 15 Z')
.fill($r("app.color.main_color"))
.strokeWidth(0);
}.id(id).alignItems(HorizontalAlign.Center);
}
7. Display Custom Markers on the Map
Use the screenshot class componentSnapshot
to generate a snapshot of the custom component via createFromBuilder
. In the callback, obtain the PixelMap
object, set it as the icon
in MarkerOptions
, and call addMarker
to generate the marker on the map.
componentSnapshot.createFromBuilder(() => {
// The custom component to generate a PixelMap image
this.BuilderMapRectLabel(text, houseId);
}, async (error, imagePixelMap) => {
if (error) {
LogUtil.debug("snapshot failure: " + JSON.stringify(error));
return;
}
const markerOptions: mapCommon.MarkerOptions = {
position: latLng,
icon: imagePixelMap, // Custom component converted to PixelMap
title: houseId, // Pass custom parameters to bind with the component for click operations
};
if (!this.rectMarkers.find(marker => marker.getTitle() === houseId)) {
// Add the marker if it doesn't exist in the marker collection
const marker = await this.mapController?.addMarker(markerOptions);
marker?.setClickable(true);
if (marker) {
this.rectMarkers.push(marker);
}
}
});
8. Listen for Marker Click Events
Use mapEventManager.on("markerClick", () => {})
to listen for marker clicks. In the callback, handle business logic based on the map level (e.g., navigate to the property layer when a region/business district marker is clicked).
this.mapEventManager.on("markerClick", (marker) => {
if (marker.getTitle().length === 2) {
// Clicked a region/business district marker: switch to the property layer and display property information
this.mapController?.animateCamera(map.newLatLng({
latitude: 30.513746,
longitude: 114.403009
}, 15));
} else {
// Clicked a property marker: perform subsequent operations
ToastUtil.showToast(marker?.getTitle());
}
});
Summary
The above is the basic process for implementing map-based house search with HarmonyOS ArkUI. Details can be customized according to business needs. Currently, custom markers are displayed by generating images from custom components, which may affect performance with a large number of markers. Optimization can be done by displaying only markers within the current screen, removing those outside the screen when scrolling, and reusing generated PixelMap
objects to avoid repeated creation.