You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Xamarin Android实现带可筛选标记与搜索功能的自定义非谷歌地图

Got it, let's walk through how to implement this custom image map in your Xamarin Android app — I've built similar campus/mall map features before, so here's a step-by-step breakdown that works:

1. Prepare Custom Map Resources & Grid Calculation

First, get your custom map image (campus/mall layout) optimized for different screen densities and place it in the drawable or mipmap folders. Then, we need to calculate the grid boundaries based on your A-Z latitude and 1-26 longitude rule.

We'll do this once the map view is fully laid out to get its actual dimensions:

private float _gridWidth;
private float _gridHeight;
private ImageView _mapImageView;

protected override void OnCreate(Bundle savedInstanceState)
{
    base.OnCreate(savedInstanceState);
    SetContentView(Resource.Layout.Main);

    _mapImageView = FindViewById<ImageView>(Resource.Id.mapImageView);
    // Wait for the map to finish layout to get accurate size
    _mapImageView.ViewTreeObserver.GlobalLayout += (sender, e) =>
    {
        int mapWidth = _mapImageView.Width;
        int mapHeight = _mapImageView.Height;
        // Split into 26 equal columns (longitude 1-26) and rows (latitude A-Z)
        _gridWidth = mapWidth / 26f;
        _gridHeight = mapHeight / 26f;
    };
}
2. Add Marker Overlays

Create a simple model to store marker data, then convert grid coordinates to screen positions to place markers on top of the map.

First, define your marker model:

public class MapMarker
{
    public char Latitude { get; set; } // A-Z
    public int Longitude { get; set; } // 1-26
    public string Title { get; set; }
    public string Category { get; set; } // For filtering (e.g., "Cafe", "Classroom")
}

Next, a helper to convert grid positions to screen coordinates:

private PointF GetMarkerScreenPosition(MapMarker marker)
{
    int latIndex = marker.Latitude - 'A'; // A = 0, B = 1 ... Z = 25
    int lonIndex = marker.Longitude - 1; // 1 = 0, 2 = 1 ... 26 = 25
    // Center the marker in its grid cell
    float x = lonIndex * _gridWidth + (_gridWidth / 2);
    float y = latIndex * _gridHeight + (_gridHeight / 2);
    return new PointF(x, y);
}

Add markers to your map (use a FrameLayout to layer markers over the image view):

private List<ImageView> _allMarkerViews = new List<ImageView>();
private List<MapMarker> _allMarkers = new List<MapMarker>();

private void AddMarker(MapMarker marker)
{
    _allMarkers.Add(marker);
    PointF screenPos = GetMarkerScreenPosition(marker);

    ImageView markerView = new ImageView(this);
    markerView.SetImageResource(Resource.Drawable.marker_icon);
    markerView.Tag = marker; // Attach marker data for later use
    _allMarkerViews.Add(markerView);

    // Position the marker on the map
    FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
        ViewGroup.LayoutParams.WrapContent,
        ViewGroup.LayoutParams.WrapContent
    );
    layoutParams.LeftMargin = (int)(screenPos.X - markerView.Width / 2);
    layoutParams.TopMargin = (int)(screenPos.Y - markerView.Height / 2);

    FrameLayout mapContainer = FindViewById<FrameLayout>(Resource.Id.mapContainer);
    mapContainer.AddView(markerView);
}
3. Implement Checkbox-Based Marker Filtering

Add checkboxes to your layout for each marker category, then wire up logic to show/hide markers based on selected filters.

First, add checkboxes to your XML layout:

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:padding="8dp">
    <CheckBox
        android:id="@+id/cb_cafes"
        android:text="Cafes"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <CheckBox
        android:id="@+id/cb_classrooms"
        android:text="Classrooms"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:marginStart="16dp"/>
</LinearLayout>

Then handle filter logic in code:

private void SetupFilterCheckboxes()
{
    CheckBox cbCafes = FindViewById<CheckBox>(Resource.Id.cb_cafes);
    CheckBox cbClassrooms = FindViewById<CheckBox>(Resource.Id.cb_classrooms);

    // Reuse the same handler for all checkboxes
    EventHandler<CompoundButton.CheckedChangeEventArgs> filterHandler = (sender, e) =>
    {
        UpdateMarkerVisibility();
    };

    cbCafes.CheckedChange += filterHandler;
    cbClassrooms.CheckedChange += filterHandler;
}

private void UpdateMarkerVisibility()
{
    CheckBox cbCafes = FindViewById<CheckBox>(Resource.Id.cb_cafes);
    CheckBox cbClassrooms = FindViewById<CheckBox>(Resource.Id.cb_classrooms);

    foreach (var markerView in _allMarkerViews)
    {
        var marker = markerView.Tag as MapMarker;
        bool shouldShow = false;

        // Check if marker matches any selected filter
        if ((cbCafes.Checked && marker.Category == "Cafe") ||
            (cbClassrooms.Checked && marker.Category == "Classroom"))
        {
            shouldShow = true;
        }

        markerView.Visibility = shouldShow ? ViewStates.Visible : ViewStates.Gone;
    }
}
4. Add Location Search Functionality

Implement a search bar to find markers by grid position (e.g., "A1") or title. Add these views to your layout:

<EditText
    android:id="@+id/et_search"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:hint="Search (e.g., A1 or Library)"
    android:padding="8dp"/>
<Button
    android:id="@+id/btn_search"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Search"/>

Then wire up the search logic:

private void SetupSearch()
{
    EditText etSearch = FindViewById<EditText>(Resource.Id.et_search);
    Button btnSearch = FindViewById<Button>(Resource.Id.btn_search);

    btnSearch.Click += (sender, e) =>
    {
        string query = etSearch.Text.Trim().ToUpper();
        MapMarker foundMarker = null;

        // Search by grid position (e.g., "A1")
        if (query.Length == 2 && char.IsLetter(query[0]) && char.IsDigit(query[1]))
        {
            char lat = query[0];
            int lon = int.Parse(query[1].ToString());
            foundMarker = _allMarkers.FirstOrDefault(m => m.Latitude == lat && m.Longitude == lon);
        }
        // Search by marker title
        else
        {
            foundMarker = _allMarkers.FirstOrDefault(m => m.Title.ToUpper().Contains(query));
        }

        if (foundMarker != null)
        {
            // Scroll map to center the marker
            PointF pos = GetMarkerScreenPosition(foundMarker);
            FrameLayout mapContainer = FindViewById<FrameLayout>(Resource.Id.mapContainer);
            mapContainer.ScrollTo(
                (int)(pos.X - mapContainer.Width / 2),
                (int)(pos.Y - mapContainer.Height / 2)
            );

            // Highlight the found marker temporarily
            foreach (var markerView in _allMarkerViews)
            {
                var marker = markerView.Tag as MapMarker;
                if (marker == foundMarker)
                {
                    markerView.SetBackgroundColor(Color.Yellow);
                    new Handler().PostDelayed(() => 
                        markerView.SetBackgroundColor(Color.Transparent), 3000);
                }
            }
        }
        else
        {
            Toast.MakeText(this, "No location found", ToastLength.Short).Show();
        }
    };
}
Quick Extra Tips
  • If your map is larger than the screen, wrap the FrameLayout in a ScrollView (or HorizontalScrollView) to enable dragging.
  • For better user clarity, you can draw grid lines directly on the map image or via a custom View overlay.
  • If you have a lot of markers, consider using a RecyclerView instead of adding individual views, but for campus/mall scenarios, the above approach is lightweight enough.

内容的提问来源于stack exchange,提问作者Adam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 03:52:03